Skip to content

Fix assert_selector support for Hash locator - #2826

Closed
seanpdoyle wants to merge 1 commit into
teamcapybara:masterfrom
seanpdoyle:table-row-hash-locator
Closed

seanpdoyle wants to merge 1 commit into
teamcapybara:masterfrom
seanpdoyle:table-row-hash-locator

Conversation

@seanpdoyle

Copy link
Copy Markdown
Contributor

Prior to this commit, the following style of assertion fails:

assert_selector :table_row, "First Name" => "John", "Last Name" => "Doe"

The assertion cites that the "First Name" is an invalid option, signaling that it's being treated as a filter rather than a locator value:

ArgumentError: Invalid option(s) "First Name", should be one of :above,
  :below, :left_of, :right_of, :near, :text, :id, :class, :style,
  :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait,
  :filter_set, :focused

To resolve this issue, modify the private
Capybara::Node::Matchers#_set_query_session_options to treat the last positional argument as a positional argument (rather than a pseudo keyword argument) if it's a Hash with any String keys.

Prior to this commit, the following style of assertion fails:

```ruby
assert_selector :table_row, "First Name" => "John", "Last Name" => "Doe"
```

The assertion cites that the `"First Name"` is an invalid option,
signaling that it's being treated as a filter rather than a locator
value:

```
ArgumentError: Invalid option(s) "First Name", should be one of :above,
  :below, :left_of, :right_of, :near, :text, :id, :class, :style,
  :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait,
  :filter_set, :focused
```

To resolve this issue, modify the private
`Capybara::Node::Matchers#_set_query_session_options` to treat the last
positional argument as a positional argument (rather than a pseudo
keyword argument) if it's a `Hash` with any `String` keys.
query_args, query_options = query_args.dup, {}
# query_options = query_args.pop if query_options.empty? && query_args.last.is_a?(Hash)
query_options = query_args.pop if query_args.last.is_a?(Hash)
query_options = query_args.pop if query_args.last.is_a?(Hash) && query_args.last.keys.none?(String)

@seanpdoyle seanpdoyle Sep 17, 2025 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm pretty dissatisfied with this solution. Since it's related to positional-to-keyword argument coercion, I wonder if there's a more wholistic approach that could be take to resolve this. For example, this would still fail for symbol keys that aren't filter options:

assert_selector :table_row, First: "John", Last: "Doe"

@twalpole

twalpole commented Jul 9, 2026

Copy link
Copy Markdown
Member

I don't think this is a good solution. Selectors are allowed to be defined that don't require a locator, so globally forcing something that might be a filter option to be a locator is a bad idea.

@twalpole

twalpole commented Jul 9, 2026 •

Copy link
Copy Markdown
Member

I believe current usage would need to be soemthing like:

assert_selector :table_row, { "First Name" => "John", "Last Name" => "Doe" }, {}

@twalpole

Copy link
Copy Markdown
Member

#2846 does the same but with more restrictions when it works (only when selector specifies support of a Hash locator). It's still hacky but, I think, less likely to break anyones existing code. Closing this PR

@twalpole twalpole closed this Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants