Skip to content

Add turbo frame assertion test helpers #742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions lib/turbo/test_assertions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,76 @@ def assert_no_turbo_stream(action:, target: nil, targets: nil)
selector << %([targets="#{targets}"]) if targets
assert_select selector, count: 0
end

# Assert that the rendered fragment of HTML contains a `<turbo-frame>`
# element.
#
# ==== Options
#
# * <tt>:id</tt> [String] matches the element's <tt>[id]</tt> attribute
# * <tt>:loading</tt> [String] matches the element's <tt>[loading]</tt>
# attribute
# * <tt>:src</tt> [String] matches the element's <tt>[src]</tt> attribute
# * <tt>:target</tt> [String] matches the element's <tt>[target]</tt>
# attribute
# * <tt>:count</tt> [Integer] indicates how many turbo frames are expected.
# Defaults to <tt>1</tt>.
#
# Given the following HTML fragment:
#
# <turbo-frame id="example" target="_top"></turbo-frame>
#
# The following assertion would pass:
#
# assert_turbo_frame id: "example", target: "_top"
#
# You can also pass a block make assertions about the contents of the
# element. Given the following HTML fragment:
#
# <turbo-frame id="example">
# <p>Hello!</p>
# </turbo-frame>
#
# The following assertion would pass:
#
# assert_turbo_frame id: "example" do
# assert_select "p", text: "Hello!"
# end
#
def assert_turbo_frame(id:, loading: nil, src: nil, target: nil, count: 1, &block)
selector = %(turbo-frame[id="#{id}"])
selector << %([loading="#{loading}"]) if loading
selector << %([src="#{src}"]) if src
selector << %([target="#{target}"]) if target
assert_select selector, count: count, &block
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about omitting the count: keyword option, and instead forwarding all remaining options:

Suggested change
def assert_turbo_frame(id:, loading: nil, src: nil, target: nil, count: 1, &block)
selector = %(turbo-frame[id="#{id}"])
selector << %([loading="#{loading}"]) if loading
selector << %([src="#{src}"]) if src
selector << %([target="#{target}"]) if target
assert_select selector, count: count, &block
end
def assert_turbo_frame(id:, loading: nil, src: nil, target: nil, **options, &block)
selector = %(turbo-frame[id="#{id}"])
selector << %([loading="#{loading}"]) if loading
selector << %([src="#{src}"]) if src
selector << %([target="#{target}"]) if target
assert_select selector, **options, &block
end

Copy link
Contributor Author

@excid3 excid3 Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since frames IDs should be unique, I thought it might make sense to set count to 1 instead of unset which matches 1 or more.


# Assert that the rendered fragment of HTML does not contain a `<turbo-frame>`
# element.
#
# ==== Options
#
# * <tt>:id</tt> [String] matches the element's <tt>[id]</tt> attribute
# * <tt>:loading</tt> [String] matches the element's <tt>[loading]</tt>
# attribute
# * <tt>:src</tt> [String] matches the element's <tt>[src]</tt> attribute
# * <tt>:target</tt> [String] matches the element's <tt>[target]</tt>
# attribute
#
# Given the following HTML fragment:
#
# <turbo-frame id="example" target="_top"></turbo-frame>
#
# The following assertion would fail:
#
# assert_no_turbo_frame id: "example", target: "_top"
#
def assert_no_turbo_frame(id:, loading: nil, src: nil, target: nil)
selector = %(turbo-frame[id="#{id}"])
selector << %([loading="#{loading}"]) if loading
selector << %([src="#{src}"]) if src
selector << %([target="#{target}"]) if target
assert_select selector, count: 0
end
end
end
26 changes: 26 additions & 0 deletions test/frames/frame_request_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,30 @@ class Turbo::FrameRequestViewTest < ActionView::TestCase
end
end
end

class Turbo::TestAssertions::FrameTest < ActionDispatch::IntegrationTest
test "assert_turbo_frame passes with matching frame" do
get tray_path(id: 1)
assert_turbo_frame id: "tray"
end

test "assert_turbo_frame fails without matching frame" do
get tray_path(id: 1)
assert_raises Minitest::Assertion do
assert_turbo_frame id: "missing"
end
end

test "assert_no_turbo_frame fails with matching frame" do
get tray_path(id: 1)
assert_raises Minitest::Assertion do
assert_no_turbo_frame id: "tray"
end
end

test "assert_no_turbo_frame fails without matching frame" do
get tray_path(id: 1)
assert_no_turbo_frame id: "missing"
end
end
end