From 2d885a7c381f263aad728d3e0c615d10271ebd2b Mon Sep 17 00:00:00 2001 From: Bruno Prieto Date: Thu, 30 Nov 2023 00:47:10 -0300 Subject: [PATCH] Improve documentation --- README.md | 17 +++- app/channels/turbo/streams/broadcasts.rb | 2 +- app/controllers/turbo/frames/frame_request.rb | 2 +- app/controllers/turbo/native/navigation.rb | 9 +- app/helpers/turbo/drive_helper.rb | 42 ++++---- app/helpers/turbo/frames_helper.rb | 11 +-- app/helpers/turbo/streams/action_helper.rb | 5 +- app/helpers/turbo/streams_helper.rb | 1 - app/models/concerns/turbo/broadcastable.rb | 99 ++++++++++++++++--- lib/turbo/broadcastable/test_helper.rb | 8 +- lib/turbo/test_assertions.rb | 4 +- .../integration_test_assertions.rb | 4 +- 12 files changed, 144 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 6cb5780d..cb401cd2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ On top of accelerating web applications, Turbo was built from the ground-up to f Turbo is a language-agnostic framework written in JavaScript, but this gem builds on top of those basics to make the integration with Rails as smooth as possible. You can deliver turbo updates via model callbacks over Action Cable, respond to controller actions with native navigation or standard redirects, and render turbo frames with helpers and layout-free responses. - ## Navigate with Turbo Drive Turbo is a continuation of the ideas from the previous [Turbolinks](https://github.com/turbolinks/turbolinks) framework, and the heart of that past approach lives on as Turbo Drive. When installed, Turbo automatically intercepts all clicks on `` links to the same domain. When you click an eligible link, Turbo prevents the browser from following it. Instead, Turbo changes the browser’s URL using the History API, requests the new page using `fetch`, and then renders the HTML response. @@ -122,7 +121,6 @@ The `Turbo` instance is automatically assigned to `window.Turbo` upon import: import "@hotwired/turbo-rails" ``` - ## Usage You can watch [the video introduction to Hotwire](https://hotwired.dev/#screencast), which focuses extensively on demonstrating Turbo in a Rails demo. Then you should familiarize yourself with [Turbo handbook](https://turbo.hotwired.dev/handbook/introduction) to understand Drive, Frames, and Streams in-depth. Finally, dive into the code documentation by starting with [`Turbo::FramesHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/frames_helper.rb), [`Turbo::StreamsHelper`](https://github.com/hotwired/turbo-rails/blob/main/app/helpers/turbo/streams_helper.rb), [`Turbo::Streams::TagBuilder`](https://github.com/hotwired/turbo-rails/blob/main/app/models/turbo/streams/tag_builder.rb), and [`Turbo::Broadcastable`](https://github.com/hotwired/turbo-rails/blob/main/app/models/concerns/turbo/broadcastable.rb). @@ -130,6 +128,17 @@ You can watch [the video introduction to Hotwire](https://hotwired.dev/#screenca ### RubyDoc Documentation For the API documentation covering this gem's classes and packages, [visit the RubyDoc page](https://rubydoc.info/github/hotwired/turbo-rails/main). +Note that this documentation is updated automatically from the main branch, so it may contain features that are not released yet. + +- [Turbo Drive Helpers](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/DriveHelper) +- [Turbo Frames Helpers](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/FramesHelper) +- [Turbo Streams View Helpers](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/StreamsHelper) +- [Turbo Streams Broadcast Methods](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/Broadcastable) +- [Turbo Streams Channel](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/StreamsChannel) +- [Turbo Native Navigation](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/Native/Navigation) +- [Turbo Test Assertions](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/TestAssertions) +- [Turbo Integration Test Assertions](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/TestAssertions/IntegrationTestAssertions) +- [Turbo Broadcastable Test Helper](https://rubydoc.info/github/hotwired/turbo-rails/main/Turbo/Broadcastable/TestHelper) ## Compatibility with Rails UJS @@ -137,8 +146,7 @@ Turbo can coexist with Rails UJS, but you need to take a series of upgrade steps ## Testing - -The [`Turbo::TestAssertions`](./lib/turbo/test_assertions.rb) concern provides Turbo Stream test helpers that assert the presence or absence of `` elements in a rendered fragment of HTML. `Turbo::TestAssertions` are automatically included in [`ActiveSupport::TestCase`](https://edgeapi.rubyonrails.org/classes/ActiveSupport/TestCase.html) and depend on the presence of [`rails-dom-testing`](https://github.com/rails/rails-dom-testing/) assertions. +The [`Turbo::TestAssertions`](./lib/turbo/test_assertions.rb) concern provides Turbo Stream test helpers that assert the presence or absence ofs s `` elements in a rendered fragment of HTML. `Turbo::TestAssertions` are automatically included in [`ActiveSaupport::TestCase`](https://edgeapi.rubyonrails.org/classes/ActiveSupport/TestCase.html) and depend on the presence of [`rails-dom-testing`](https://github.com/rails/rails-dom-testing/) assertions. The [`Turbo::TestAssertions::IntegrationTestAssertions`](./lib/turbo/test_assertions/integration_test_assertions.rb) are built on top of `Turbo::TestAssertions`, and add support for passing a `status:` keyword. They are automatically included in [`ActionDispatch::IntegrationTest`](https://edgeguides.rubyonrails.org/testing.html#integration-testing). @@ -148,7 +156,6 @@ The [`Turbo::Broadcastable::TestHelper`](./lib/turbo/broadcastable/test_helper.r Run the tests with `./bin/test`. - ## License Turbo is released under the [MIT License](https://opensource.org/licenses/MIT). diff --git a/app/channels/turbo/streams/broadcasts.rb b/app/channels/turbo/streams/broadcasts.rb index 1b13afed..de1c0330 100644 --- a/app/channels/turbo/streams/broadcasts.rb +++ b/app/channels/turbo/streams/broadcasts.rb @@ -1,4 +1,4 @@ -# Provides the broadcast actions in synchronous and asynchrous form for the Turbo::StreamsChannel. +# Provides the broadcast actions in synchronous and asynchronous form for the Turbo::StreamsChannel. # See Turbo::Broadcastable for the user-facing API that invokes these methods with most of the paperwork filled out already. # # Can be used directly using something like Turbo::StreamsChannel.broadcast_remove_to :entries, target: 1. diff --git a/app/controllers/turbo/frames/frame_request.rb b/app/controllers/turbo/frames/frame_request.rb index 2063a28d..831a35ee 100644 --- a/app/controllers/turbo/frames/frame_request.rb +++ b/app/controllers/turbo/frames/frame_request.rb @@ -5,7 +5,7 @@ # When that header is detected by the controller, we substitute our own minimal layout in place of the # application-supplied layout (since we're only working on an in-page frame, thus can skip the weight of the layout). We # use a minimal layout, rather than avoid the layout entirely, so that it's still possible to render content into the -# head. +# head. # # Accordingly, we ensure that the etag for the page is changed, such that a cache for a minimal-layout request isn't # served on a normal request and vice versa. diff --git a/app/controllers/turbo/native/navigation.rb b/app/controllers/turbo/native/navigation.rb index fc8ff35d..9f1d6b1b 100644 --- a/app/controllers/turbo/native/navigation.rb +++ b/app/controllers/turbo/native/navigation.rb @@ -15,29 +15,32 @@ def turbo_native_app? request.user_agent.to_s.match?(/Turbo Native/) end - # Tell the Turbo Native app to dismiss a modal (if presented) or pop a screen off of the navigation stack. + # Tell the Turbo Native app to dismiss a modal (if presented) or pop a screen off of the navigation stack. Otherwise redirect to the given URL if Turbo Native is not present. def recede_or_redirect_to(url, **options) turbo_native_action_or_redirect url, :recede, :to, options end - # Tell the Turbo Native app to ignore this navigation. + # Tell the Turbo Native app to ignore this navigation, otherwise redirect to the given URL if Turbo Native is not present. def resume_or_redirect_to(url, **options) turbo_native_action_or_redirect url, :resume, :to, options end - # Tell the Turbo Native app to refresh the current screen. + # Tell the Turbo Native app to refresh the current screen, otherwise redirect to the given URL if Turbo Native is not present. def refresh_or_redirect_to(url, **options) turbo_native_action_or_redirect url, :refresh, :to, options end + # Same as recede_or_redirect_to but redirects to the previous page or provided fallback location if the Turbo Native app is not present. def recede_or_redirect_back_or_to(url, **options) turbo_native_action_or_redirect url, :recede, :back, options end + # Same as resume_or_redirect_to but redirects to the previous page or provided fallback location if the Turbo Native app is not present. def resume_or_redirect_back_or_to(url, **options) turbo_native_action_or_redirect url, :resume, :back, options end + # Same as refresh_or_redirect_to but redirects to the previous page or provided fallback location if the Turbo Native app is not present. def refresh_or_redirect_back_or_to(url, **options) turbo_native_action_or_redirect url, :refresh, :back, options end diff --git a/app/helpers/turbo/drive_helper.rb b/app/helpers/turbo/drive_helper.rb index 4d3cf167..1c5d87c4 100644 --- a/app/helpers/turbo/drive_helper.rb +++ b/app/helpers/turbo/drive_helper.rb @@ -1,22 +1,21 @@ +# Helpers to configure Turbo Drive via meta directives. They come in two +# variants: +# +# The recommended option is to include +yield :head+ in the ++ section +# of the layout. Then you can use the helpers in any view. +# +# ==== Example +# +# # app/views/application.html.erb +# <%= yield :head %><%= yield %> +# +# # app/views/trays/index.html.erb +# <% turbo_exempts_page_from_cache %> +#

Page that shouldn't be cached by Turbo

+# +# Alternatively, you can use the +_tag+ variant of the helpers to only get the +# HTML for the meta directive. module Turbo::DriveHelper - # Helpers to configure Turbo Drive via meta directives. They come in two - # variants: - # - # The recommended option is to include +yield :head+ in the ++ section - # of the layout. Then you can use the helpers in any view. - # - # ==== Example - # - # # app/views/application.html.erb - # <%= yield :head %><%= yield %> - # - # # app/views/trays/index.html.erb - # <% turbo_exempts_page_from_cache %> - #

Page that shouldn't be cached by Turbo

- # - # Alternatively, you can use the +_tag+ variant of the helpers to only get the - # HTML for the meta directive. - # Pages that are more likely than not to be a cache miss can skip turbo cache to avoid visual jitter. # Cannot be used along with +turbo_exempts_page_from_preview+. def turbo_exempts_page_from_cache @@ -49,6 +48,7 @@ def turbo_page_requires_reload_tag tag.meta(name: "turbo-visit-control", content: "reload") end +<<<<<<< HEAD # Configure how to handle page refreshes. A page refresh happens when # Turbo loads the current page again with a *replace* visit: # @@ -68,6 +68,12 @@ def turbo_page_requires_reload_tag # === Example Usage: # # turbo_refreshes_with(method: :morph, scroll: :preserve) +======= + # Configure page refresh directives + # ==== Options + # * :method - +:replace+ or +:morph+ (default: +:replace+) + # * :scroll - +:reset+ or +:preserve+ (default: +:reset+) +>>>>>>> 56122d6 (Improve documentation) def turbo_refreshes_with(method: :replace, scroll: :reset) provide :head, turbo_refresh_method_tag(method) provide :head, turbo_refresh_scroll_tag(scroll) diff --git a/app/helpers/turbo/frames_helper.rb b/app/helpers/turbo/frames_helper.rb index 3697f13f..f52875b3 100644 --- a/app/helpers/turbo/frames_helper.rb +++ b/app/helpers/turbo/frames_helper.rb @@ -2,7 +2,7 @@ module Turbo::FramesHelper # Returns a frame tag that can either be used simply to encapsulate frame content or as a lazy-loading container that starts empty but # fetches the URL supplied in the +src+ attribute. # - # === Examples + # ==== Examples # # <%= turbo_frame_tag "tray", src: tray_path(tray) %> # # => @@ -27,17 +27,14 @@ module Turbo::FramesHelper # <%= turbo_frame_tag [user_id, "tray"], src: tray_path(tray) %> # # => # - # The `turbo_frame_tag` helper will convert the arguments it receives to their - # `dom_id` if applicable to easily generate unique ids for Turbo Frames: + # The +turbo_frame_tag+ helper will convert the arguments it receives to their + # +dom_id+ if applicable to easily generate unique ids for Turbo Frames: # # <%= turbo_frame_tag(Article.find(1)) %> # # => # # <%= turbo_frame_tag(Article.find(1), "comments") %> - # # => - # - # <%= turbo_frame_tag(Article.find(1), Comment.new) %> - # # => + # # => def turbo_frame_tag(*ids, src: nil, target: nil, **attributes, &block) id = ids.first.respond_to?(:to_key) ? ActionView::RecordIdentifier.dom_id(*ids) : ids.join('_') src = url_for(src) if src.present? diff --git a/app/helpers/turbo/streams/action_helper.rb b/app/helpers/turbo/streams/action_helper.rb index 35f52938..b23b0bd3 100644 --- a/app/helpers/turbo/streams/action_helper.rb +++ b/app/helpers/turbo/streams/action_helper.rb @@ -22,7 +22,6 @@ module Turbo::Streams::ActionHelper # message = Message.find(1) # turbo_stream_action_tag "remove", target: [message, :special] # # => - # def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, **attributes) template = action.to_sym.in?(%i[ remove refresh ]) ? "" : tag.template(template.to_s.html_safe) @@ -35,6 +34,10 @@ def turbo_stream_action_tag(action, target: nil, targets: nil, template: nil, ** end end + # Creates a `turbo-stream` tag with an `action="refresh"` attribute. Example: + # + # turbo_stream_refresh_tag + # # => def turbo_stream_refresh_tag(request_id: Turbo.current_request_id, **attributes) turbo_stream_action_tag(:refresh, **{ "request-id": request_id }.compact, **attributes) end diff --git a/app/helpers/turbo/streams_helper.rb b/app/helpers/turbo/streams_helper.rb index 2d104091..f4df525e 100644 --- a/app/helpers/turbo/streams_helper.rb +++ b/app/helpers/turbo/streams_helper.rb @@ -48,7 +48,6 @@ def turbo_stream # It is also possible to pass additional parameters to the channel by passing them through `data` attributes: # # <%= turbo_stream_from "room", channel: RoomChannel, data: {room_name: "room #1"} %> - # def turbo_stream_from(*streamables, **attributes) attributes[:channel] = attributes[:channel]&.to_s || "Turbo::StreamsChannel" attributes[:"signed-stream-name"] = Turbo::StreamsChannel.signed_stream_name(streamables) diff --git a/app/models/concerns/turbo/broadcastable.rb b/app/models/concerns/turbo/broadcastable.rb index 89861268..5933067a 100644 --- a/app/models/concerns/turbo/broadcastable.rb +++ b/app/models/concerns/turbo/broadcastable.rb @@ -14,8 +14,8 @@ # end # end # -# This is an example from [HEY](https://hey.com), and the clearance is the model that drives -# [the screener](https://hey.com/features/the-screener/), which gives users the power to deny first-time senders (petitioners) +# This is an example from {HEY}[https://hey.com], and the clearance is the model that drives +# {the screener}[https://hey.com/features/the-screener/], which gives users the power to deny first-time senders (petitioners) # access to their attention (as the examiner). When a new clearance is created upon receipt of an email from a first-time # sender, that'll trigger the call to broadcast_later, which in turn invokes broadcast_prepend_later_to. # @@ -27,7 +27,7 @@ # (which is derived by default from the plural model name of the model, but can be overwritten). # # You can also choose to render html instead of a partial inside of a broadcast -# you do this by passing the `html:` option to any broadcast method that accepts the **rendering argument. Example: +# you do this by passing the +html:+ option to any broadcast method that accepts the **rendering argument. Example: # # class Message < ApplicationRecord # belongs_to :user @@ -40,8 +40,8 @@ # end # end # -# If you want to render a template instead of a partial, e.g. ('messages/index' or 'messages/show'), you can use the `template:` option. -# Again, only to any broadcast method that accepts the `**rendering` argument. Example: +# If you want to render a template instead of a partial, e.g. ('messages/index' or 'messages/show'), you can use the +template:+ option. +# Again, only to any broadcast method that accepts the +**rendering+ argument. Example: # # class Message < ApplicationRecord # belongs_to :user @@ -54,7 +54,7 @@ # end # end # -# If you want to render a renderable object you can use the `renderable:` option. +# If you want to render a renderable object you can use the +renderable:+ option. # # class Message < ApplicationRecord # belongs_to :user @@ -67,15 +67,69 @@ # end # end # -# There are four basic actions you can broadcast: remove, replace, append, and -# prepend. As a rule, you should use the _later versions of everything except for remove when broadcasting +# There are seven basic actions you can broadcast: after, append, before, +# prepend, remove, replace, and +# update. As a rule, you should use the _later versions of everything except for remove when broadcasting # within a real-time path, like a controller or model, since all those updates require a rendering step, which can slow down # execution. You don't need to do this for remove, since only the dom id for the model is used. # -# In addition to the four basic actions, you can also use broadcast_render, +# In addition to the seven basic actions, you can also use broadcast_render, # broadcast_render_to broadcast_render_later, and broadcast_render_later_to # to render a turbo stream template with multiple actions. # +# == Page refreshes +# +# You can broadcast a "page refresh" to all clients subscribed to a stream. This is useful when you need to +# broadcast a lot of partial updates, and the entire content is not easily updatable using Turbo stream actions like replace, append, prepend, or remove. +# +# It's recommended to use page refreshes as the primary option for broadcasting changes. +# This approach allows you to remove the coupling between models and views to render complex partial updates without sacrificing the desired high UI fidelity. +# We hope this makes using the other available Turbo Stream actions less necessary. +# +# The +broadcast_refreshes+ class method configures the model to broadcast a "page refresh" on creates, updates, and destroys to a stream +# name derived at runtime by the stream symbol invocation. Examples +# +# class Board < ApplicationRecord +# broadcast_refreshes +# end +# +# In this example, when a board is created, updated, or destroyed, a Turbo Stream for a +# page refresh will be broadcasted to all clients subscribed to the "boards" stream. +# +# This works great in hierarchical structures, where the child record touches parent records automatically to invalidate the cache: +# +# class Column < ApplicationRecord +# belongs_to :board, touch: true # +Board+ will trigger a page refresh on column changes +# end +# +# You can also specify the streamable declaratively by passing a symbol to the +broadcast_refreshes_to+ method: +# +# class Column < ApplicationRecord +# belongs_to :board +# broadcast_refreshes_to :board +# end +# +# To achieve more granular control, , you can also broadcast a "page refresh" to a stream name derived from the passed streamables by using +# the instance-level methods broadcast_refresh_to or broadcast_refresh_later_to. +# These methods are particularly useful when you want to trigger a page refresh for more specific scenarios. Example: +# +# class Clearance < ApplicationRecord +# belongs_to :petitioner, class_name: "Contact" +# belongs_to :examiner, class_name: "User" +# +# after_create_commit :broadcast_refresh_later +# +# private +# def broadcast_refresh_later +# broadcast_refresh_later_to examiner.identity, :clearances +# end +# end +# +# In this example, a "page refresh" is broadcast to the stream named "identity:2:clearances" after a new clearance is created. +# All clients subscribed to this stream will refresh the page to reflect the changes. +# +# When broadcasting page refreshes, Turbo will automatically debounce multiple calls in a row to only broadcast the last one. This is meant for scenarios where you process records in mass. Because of the nature of such signals, it makes no sense to broadcast them repeatedly and individually. +# # == Suppressing broadcasts # # Sometimes, you need to disable broadcasts in certain scenarios. You can use .suppressing_turbo_broadcasts to create @@ -136,7 +190,17 @@ def broadcasts(stream = model_name.plural, inserts_by: :append, target: broadcas end # Configures the model to broadcast a "page refresh" on creates, updates, and destroys to a stream - # name derived at runtime by the stream symbol invocation. + # name derived at runtime by the stream symbol invocation. Examples: + # + # class Message < ApplicationRecord + # belongs_to :board + # broadcasts_refreshes_to :board + # end + # + # class Message < ApplicationRecord + # belongs_to :board + # broadcasts_refreshes_to ->(message) { [ message.board, :messages ] } + # end def broadcasts_refreshes_to(stream) after_commit -> { broadcast_refresh_later_to(stream.try(:call, self) || send(stream)) } end @@ -293,10 +357,15 @@ def broadcast_prepend(target: broadcast_target_default, **rendering) broadcast_prepend_to self, target: target, **rendering end + # Broadcast a "page refresh" to the stream name identified by the passed streamables. Example: + # + # # Sends to the stream named "identity:2:clearances" + # clearance.broadcast_refresh_to examiner.identity, :clearances def broadcast_refresh_to(*streamables) Turbo::StreamsChannel.broadcast_refresh_to(*streamables) unless suppressed_turbo_broadcasts? end + # Same as #broadcast_refresh_to, but the designated stream is automatically set to the current model. def broadcast_refresh broadcast_refresh_to self end @@ -315,7 +384,6 @@ def broadcast_action(action, target: broadcast_target_default, attributes: {}, * broadcast_action_to self, action: action, target: target, attributes: attributes, **rendering end - # Same as broadcast_replace_to but run asynchronously via a Turbo::Streams::BroadcastJob. def broadcast_replace_later_to(*streamables, **rendering) Turbo::StreamsChannel.broadcast_replace_later_to(*streamables, target: self, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? @@ -356,10 +424,12 @@ def broadcast_prepend_later(target: broadcast_target_default, **rendering) broadcast_prepend_later_to self, target: target, **rendering end + # Same as broadcast_refresh_to but run asynchronously via a Turbo::Streams::BroadcastJob. def broadcast_refresh_later_to(*streamables) Turbo::StreamsChannel.broadcast_refresh_later_to(*streamables, request_id: Turbo.current_request_id) unless suppressed_turbo_broadcasts? end + # Same as #broadcast_refresh_later_to, but the designated stream is automatically set to the current model. def broadcast_refresh_later broadcast_refresh_later_to self end @@ -390,7 +460,7 @@ def broadcast_action_later(action:, target: broadcast_target_default, attributes # # Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not # desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should - # be using `broadcast_render_later`, unless you specifically know why synchronous rendering is needed. + # be using +broadcast_render_later+, unless you specifically know why synchronous rendering is needed. def broadcast_render(**rendering) broadcast_render_to self, **rendering end @@ -400,12 +470,12 @@ def broadcast_render(**rendering) # # Note that rendering inline via this method will cause template rendering to happen synchronously. That is usually not # desireable for model callbacks, certainly not if those callbacks are inside of a transaction. Most of the time you should - # be using `broadcast_render_later_to`, unless you specifically know why synchronous rendering is needed. + # be using +broadcast_render_later_to+, unless you specifically know why synchronous rendering is needed. def broadcast_render_to(*streamables, **rendering) Turbo::StreamsChannel.broadcast_render_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? end - # Same as broadcast_action_to but run asynchronously via a Turbo::Streams::BroadcastJob. + # Same as broadcast_render_to but run asynchronously via a Turbo::Streams::BroadcastJob. def broadcast_render_later(**rendering) broadcast_render_later_to self, **rendering end @@ -416,7 +486,6 @@ def broadcast_render_later_to(*streamables, **rendering) Turbo::StreamsChannel.broadcast_render_later_to(*streamables, **broadcast_rendering_with_defaults(rendering)) unless suppressed_turbo_broadcasts? end - private def broadcast_target_default self.class.broadcast_target_default diff --git a/lib/turbo/broadcastable/test_helper.rb b/lib/turbo/broadcastable/test_helper.rb index f5e97017..2df88ef8 100644 --- a/lib/turbo/broadcastable/test_helper.rb +++ b/lib/turbo/broadcastable/test_helper.rb @@ -11,14 +11,14 @@ module TestHelper # Asserts that `` elements were broadcast over Action Cable # - # === Arguments + # ==== Arguments # # * stream_name_or_object the objects used to generate the # channel Action Cable name, or the name itself # * &block optional block executed before the # assertion # - # === Options + # ==== Options # # * count: the number of `` elements that are # expected to be broadcast @@ -70,7 +70,7 @@ def assert_turbo_stream_broadcasts(stream_name_or_object, count: nil, &block) # Asserts that no `` elements were broadcast over Action Cable # - # === Arguments + # ==== Arguments # # * stream_name_or_object the objects used to generate the # channel Action Cable name, or the name itself @@ -113,7 +113,7 @@ def assert_no_turbo_stream_broadcasts(stream_name_or_object, &block) # Captures any `` elements that were broadcast over Action Cable # - # === Arguments + # ==== Arguments # # * stream_name_or_object the objects used to generate the # channel Action Cable name, or the name itself diff --git a/lib/turbo/test_assertions.rb b/lib/turbo/test_assertions.rb index 477351b9..c80be538 100644 --- a/lib/turbo/test_assertions.rb +++ b/lib/turbo/test_assertions.rb @@ -10,7 +10,7 @@ module TestAssertions # Assert that the rendered fragment of HTML contains a `` # element. # - # === Options + # ==== Options # # * :action [String] matches the element's [action] # attribute @@ -55,7 +55,7 @@ def assert_turbo_stream(action:, target: nil, targets: nil, count: 1, &block) # Assert that the rendered fragment of HTML does not contain a `` # element. # - # === Options + # ==== Options # # * :action [String] matches the element's [action] # attribute diff --git a/lib/turbo/test_assertions/integration_test_assertions.rb b/lib/turbo/test_assertions/integration_test_assertions.rb index 33bae804..022868a4 100644 --- a/lib/turbo/test_assertions/integration_test_assertions.rb +++ b/lib/turbo/test_assertions/integration_test_assertions.rb @@ -4,7 +4,7 @@ module IntegrationTestAssertions # Assert that the Turbo Stream request's response body's HTML contains a # `` element. # - # === Options + # ==== Options # # * :status [Integer, Symbol] the HTTP response status # * :action [String] matches the element's [action] @@ -47,7 +47,7 @@ def assert_turbo_stream(status: :ok, **attributes, &block) # Assert that the Turbo Stream request's response body's HTML does not # contain a `` element. # - # === Options + # ==== Options # # * :status [Integer, Symbol] the HTTP response status # * :action [String] matches the element's [action]