-
Notifications
You must be signed in to change notification settings - Fork 328
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce
Turbo::SystemTestHelper
(#577)
Introduce the `Turbo::SystemTestHelper` module to be included into [ActionDispatch::SystemTestCase][] when it's available. The module is named to mimic [ActionText::SystemTestHelper][]. The module defines a `#connect_turbo_cable_stream_sources` helper method extracted from this project's System Test suite. It aims to synchronize the test harness with Turbo's Action Cable-powered broadcast support. The method will find all `<turbo-cable-stream-source>` elements that are present but not yet `[connected]` (returning the results immediately with Capybara's `:wait`), then wait for them to connect (using whatever Capybara's configured wait value). In addition to the `connect_turbo_cable_stream_sources`, also introduce a `:turbo_cable_stream_source` Capybara selector, along with `assert_turbo_cable_stream_source` and `assert_no_turbo_cable_stream_source` helper methods. [ActionDispatch::SystemTestCase]: https://edgeapi.rubyonrails.org/classes/ActionDispatch/SystemTestCase.html [ActionText::SystemTestHelper]: https://edgeapi.rubyonrails.org/classes/ActionText/SystemTestHelper.html
- Loading branch information
1 parent
1b60474
commit 9f5c846
Showing
7 changed files
with
342 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
module Turbo::SystemTestHelper | ||
# Delay until every `<turbo-cable-stream-source>` element present in the page | ||
# is ready to receive broadcasts | ||
# | ||
# test "renders broadcasted Messages" do | ||
# message = Message.new content: "Hello, from Action Cable" | ||
# | ||
# visit "/" | ||
# click_link "All Messages" | ||
# message.save! # execute server-side code to broadcast a Message | ||
# | ||
# assert_text message.content | ||
# end | ||
# | ||
# By default, calls to `#visit` will wait for all `<turbo-cable-stream-source>` | ||
# elements to connect. You can control this by modifying the | ||
# `config.turbo.test_connect_after_actions`. For example, to wait after calls to | ||
# `#click_link`, add the following to `config/environments/test.rb`: | ||
# | ||
# # config/environments/test.rb | ||
# config.turbo.test_connect_after_actions << :click_link | ||
# | ||
# To disable automatic connecting, set the configuration to `[]`: | ||
# | ||
# # config/environments/test.rb | ||
# config.turbo.test_connect_after_actions = [] | ||
# | ||
def connect_turbo_cable_stream_sources(**options, &block) | ||
all(:turbo_cable_stream_source, **options, connected: false, wait: 0).each do |element| | ||
element.assert_matches_selector(:turbo_cable_stream_source, **options, connected: true, &block) | ||
end | ||
end | ||
|
||
# Asserts that a `<turbo-cable-stream-source>` element is present in the | ||
# document | ||
# | ||
# ==== Arguments | ||
# | ||
# * <tt>locator</tt> optional locator to determine the element's | ||
# `[signed-stream-name]` attribute. Can be of any type that is a valid | ||
# argument to <tt>Turbo::Streams::StreamName#signed_stream_name</tt>. | ||
# | ||
# ==== Options | ||
# | ||
# * <tt>:connected</tt> matches the `[connected]` attribute | ||
# * <tt>:channel</tt> matches the `[channel]` attribute. Can be a Class, | ||
# String, Symbol, or Regexp | ||
# * <tt>:signed_stream_name</tt> matches the element's `[signed-stream-name]` | ||
# attribute. Can be of any type that is a valid | ||
# argument to <tt>Turbo::Streams::StreamName#signed_stream_name</tt>. | ||
# | ||
# In addition to the filters listed above, accepts any valid Capybara global | ||
# filter option. | ||
def assert_turbo_cable_stream_source(...) | ||
assert_selector(:turbo_cable_stream_source, ...) | ||
end | ||
|
||
# Asserts that a `<turbo-cable-stream-source>` element is absent from the | ||
# document | ||
# | ||
# ==== Arguments | ||
# | ||
# * <tt>locator</tt> optional locator to determine the element's | ||
# `[signed-stream-name]` attribute. Can be of any type that is a valid | ||
# argument to <tt>Turbo::Streams::StreamName#signed_stream_name</tt>. | ||
# | ||
# ==== Options | ||
# | ||
# * <tt>:connected</tt> matches the `[connected]` attribute | ||
# * <tt>:channel</tt> matches the `[channel]` attribute. Can be a Class, | ||
# String, Symbol, or Regexp | ||
# * <tt>:signed_stream_name</tt> matches the element's `[signed-stream-name]` | ||
# attribute. Can be of any type that is a valid | ||
# argument to <tt>Turbo::Streams::StreamName#signed_stream_name</tt>. | ||
# | ||
# In addition to the filters listed above, accepts any valid Capybara global | ||
# filter option. | ||
def assert_no_turbo_cable_stream_source(...) | ||
assert_no_selector(:turbo_cable_stream_source, ...) | ||
end | ||
|
||
Capybara.add_selector :turbo_cable_stream_source do | ||
xpath do |locator| | ||
xpath = XPath.descendant.where(XPath.local_name == "turbo-cable-stream-source") | ||
xpath.where(SignedStreamNameConditions.new(locator).reduce(:|)) | ||
end | ||
|
||
expression_filter :connected do |xpath, value| | ||
builder(xpath).add_attribute_conditions(connected: value) | ||
end | ||
|
||
expression_filter :channel do |xpath, value| | ||
builder(xpath).add_attribute_conditions(channel: value.try(:name) || value) | ||
end | ||
|
||
expression_filter :signed_stream_name do |xpath, value| | ||
case value | ||
when TrueClass, FalseClass, NilClass, Regexp | ||
builder(xpath).add_attribute_conditions("signed-stream-name": value) | ||
else | ||
xpath.where(SignedStreamNameConditions.new(value).reduce(:|)) | ||
end | ||
end | ||
end | ||
|
||
class SignedStreamNameConditions # :nodoc: | ||
include Turbo::Streams::StreamName, Enumerable | ||
|
||
def initialize(value) | ||
@value = value | ||
end | ||
|
||
def attribute | ||
XPath.attr(:"signed-stream-name") | ||
end | ||
|
||
def each | ||
if @value.is_a?(String) | ||
yield attribute == @value | ||
yield attribute == signed_stream_name(@value) | ||
elsif @value.is_a?(Array) || @value.respond_to?(:to_key) | ||
yield attribute == signed_stream_name(@value) | ||
elsif @value.present? | ||
yield attribute == @value | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
require "test_helper" | ||
require "turbo/system_test_helper" | ||
require "capybara/minitest" | ||
|
||
class Turbo::CapybaraSelectorTestCase < ActionView::TestCase | ||
include Capybara::Minitest::Assertions | ||
|
||
attr_accessor :page | ||
|
||
def render_html(html, **local_assigns) | ||
render(inline: html, locals: local_assigns) | ||
|
||
self.page = Capybara.string(rendered.to_s) | ||
end | ||
end | ||
|
||
class Turbo::TurboCableStreamSourceSelectorTest < Turbo::CapybaraSelectorTestCase | ||
test ":turbo_cable_stream_source matches signed-stream-name as a locator" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: message | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_selector :turbo_cable_stream_source, count: 1 | ||
assert_selector :turbo_cable_stream_source, message, count: 1 | ||
assert_selector :turbo_cable_stream_source, [message], count: 1 | ||
assert_selector :turbo_cable_stream_source, Turbo::StreamsChannel.signed_stream_name(message), count: 1 | ||
end | ||
|
||
test ":turbo_cable_stream_source matches signed-stream-name with :signed_stream_name filter" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: message | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_selector :turbo_cable_stream_source, count: 1 | ||
assert_selector :turbo_cable_stream_source, signed_stream_name: message, count: 1 | ||
assert_selector :turbo_cable_stream_source, signed_stream_name: [message], count: 1 | ||
assert_selector :turbo_cable_stream_source, signed_stream_name: Turbo::StreamsChannel.signed_stream_name(message), count: 1 | ||
end | ||
|
||
test ":turbo_cable_stream_source matches channel with :channel filter" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: message | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_selector :turbo_cable_stream_source, count: 1 | ||
assert_selector :turbo_cable_stream_source, channel: true | ||
assert_selector :turbo_cable_stream_source, channel: Turbo::StreamsChannel | ||
assert_selector :turbo_cable_stream_source, channel: "Turbo::StreamsChannel" | ||
end | ||
|
||
test ":turbo_cable_stream_source does not match signed-stream-name as a locator" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: Message.new(id: 2) | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_no_selector :turbo_cable_stream_source, "junk", count: 1 | ||
assert_no_selector :turbo_cable_stream_source, message, count: 1 | ||
assert_no_selector :turbo_cable_stream_source, [message], count: 1 | ||
assert_no_selector :turbo_cable_stream_source, Turbo::StreamsChannel.signed_stream_name(message), count: 1 | ||
end | ||
|
||
test ":turbo_cable_stream_source does not match signed-stream-name with :signed_stream_name filter" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: Message.new(id: 2) | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_no_selector :turbo_cable_stream_source, signed_stream_name: "junk", count: 1 | ||
assert_no_selector :turbo_cable_stream_source, signed_stream_name: message, count: 1 | ||
assert_no_selector :turbo_cable_stream_source, signed_stream_name: [message], count: 1 | ||
assert_no_selector :turbo_cable_stream_source, signed_stream_name: Turbo::StreamsChannel.signed_stream_name(message), count: 1 | ||
end | ||
|
||
test ":turbo_cable_stream_source does not match channel with :channel filter" do | ||
message = Message.new(id: 1) | ||
|
||
render_html <<~ERB, message: message | ||
<%= turbo_stream_from message %> | ||
ERB | ||
|
||
assert_no_selector :turbo_cable_stream_source, channel: false | ||
assert_no_selector :turbo_cable_stream_source, channel: Object | ||
assert_no_selector :turbo_cable_stream_source, channel: "Object" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<h1>Message #<%= @message.id %></h1> | ||
|
||
<%= turbo_stream_from @message %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
require "application_system_test_case" | ||
|
||
class AssertionsTest < ApplicationSystemTestCase | ||
test "#assert_turbo_cable_stream_source treats the locator as :signed_stream_name filter" do | ||
message = Message.new(id: 1) | ||
|
||
visit message_path(message) | ||
|
||
assert_turbo_cable_stream_source message, count: 1 | ||
assert_no_turbo_cable_stream_source "junk" | ||
end | ||
|
||
test "#assert_turbo_cable_stream_source supports String collection filters" do | ||
visit messages_path | ||
|
||
assert_turbo_cable_stream_source connected: true, count: 1 | ||
assert_turbo_cable_stream_source channel: Turbo::StreamsChannel, count: 1 | ||
assert_turbo_cable_stream_source signed_stream_name: Turbo::StreamsChannel.signed_stream_name("messages"), count: 1 | ||
assert_no_turbo_cable_stream_source connected: false | ||
assert_no_turbo_cable_stream_source channel: "junk" | ||
assert_no_turbo_cable_stream_source signed_stream_name: Turbo::StreamsChannel.signed_stream_name("junk") | ||
end | ||
|
||
test "#assert_turbo_cable_stream_source supports record filters" do | ||
message = Message.new(id: 1) | ||
|
||
visit message_path(message) | ||
|
||
assert_turbo_cable_stream_source signed_stream_name: message | ||
assert_turbo_cable_stream_source signed_stream_name: [message] | ||
assert_turbo_cable_stream_source signed_stream_name: Turbo::StreamsChannel.signed_stream_name(message) | ||
assert_no_turbo_cable_stream_source signed_stream_name: [message, :junk] | ||
end | ||
end |
Oops, something went wrong.