-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Acceptance Tests: Use the most specific selector #661
Conversation
Capybara provides a wide range of semantically meaningful [selectors][], and also provides affordances for applications to declare their own through the [Capybara.add_selector][] interface. In the sample Acceptance test, replace the Exercise phase's [within][] call with a call to [within_fieldset][]. The `within` call's `"#sign_up"` argument scopes the subsequent calls to `fill_in` and `click_button` to operate on elements nested inside an element with `[id="sign_up"]`. The `within_fieldset` call's "Sign up"` argument (its "locator") scopes the calls to `fill_in` and `click_button` to operate on elements nested inside a [`<fieldset>` element][fieldset] with a descendant [`<legend>` element][legend] that contains the text "Sign up". Similarly, replace the Verify phase's [have_content][] call with one to [have_button][]. A call to `have_content("Sign out")` will succeed if the phrase `Sign out` exists as visible text anywhere on the page. A call to `have_button("Sign out")` will only match a `<button>` element with `Sign out` as its text or an `<input type="button" value="Sign out">` element. [selectors]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Selector#built-in-selectors [add_selector]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara.add_selector [within]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session:within [within_fieldset]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/Session:within_fieldset [fieldset]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset [legend]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend [have_content]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers:have_content [have_button]: https://rubydoc.info/github/teamcapybara/capybara/master/Capybara/RSpecMatchers:have_button
fill_in "Email", with: "[email protected]" | ||
fill_in "Password", with: "Examp1ePa$$" | ||
click_button "Sign up" | ||
end | ||
|
||
expect(page).to have_content("Sign out") | ||
expect(page).to have_button("Sign out") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cpjmcquillan this commit replaces the have_content
matcher with a have_button
matcher.
Since the sample code block is already making assumptions about the hypothetical content of the response, making the matcher more specific didn't feel like a risk.
Did you have something else in mind when you introduced this code sample? Does this feel like an improvement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't originally introduce the code sample, but I changed the formatting and some of the language used to describe the tests in #654.
I think the change you've made is helpful, though. I can't imagine a real scenario where I would expect this test to render a page with "Sign out" content that wasn't a button.
@@ -6,12 +6,12 @@ | |||
it "signs up the user with valid details" do | |||
visit sign_up_path | |||
|
|||
within "#sign_up" do | |||
within_fieldset "Sign up" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cpjmcquillan similar to https://github.com/thoughtbot/guides/pull/661/files#r868194849, I'm curious how you feel about changing the hypothetical example page to nest these form fields within a <fieldset>
+ <legend>
pairing instead of a <div id="sign_up">
element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To share more context, this change draws inspiration from Section 5.3.2.5 Naming Fieldsets with the Legend Element:
Using the legend element to name a fieldset element satisfies Rule 2: Prefer Visible Text and Rule 3: Prefer Native Techniques.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a worthwhile change.
Is it worth linking to the w3 guidance somewhere too?
I miss being able to lean on the GOV.UK Design System for things like this. I wonder if an accessibility-focused, style-agnostic design system (that we could build component libraries and form builders for) would help us standardise the way we structure most of our web pages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
fill_in "Email", with: "[email protected]" | ||
fill_in "Password", with: "Examp1ePa$$" | ||
click_button "Sign up" | ||
end | ||
|
||
expect(page).to have_content("Sign out") | ||
expect(page).to have_button("Sign out") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't originally introduce the code sample, but I changed the formatting and some of the language used to describe the tests in #654.
I think the change you've made is helpful, though. I can't imagine a real scenario where I would expect this test to render a page with "Sign out" content that wasn't a button.
@@ -6,12 +6,12 @@ | |||
it "signs up the user with valid details" do | |||
visit sign_up_path | |||
|
|||
within "#sign_up" do | |||
within_fieldset "Sign up" do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's a worthwhile change.
Is it worth linking to the w3 guidance somewhere too?
I miss being able to lean on the GOV.UK Design System for things like this. I wonder if an accessibility-focused, style-agnostic design system (that we could build component libraries and form builders for) would help us standardise the way we structure most of our web pages?
@@ -45,6 +45,7 @@ | |||
|
|||
[Sample](acceptance_test_spec.rb) | |||
|
|||
- Use the most specific [selectors][] available. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does "most specific selector" mean favoring button
selectors when using buttons? I can definitely see a case where some flexibility would be helpful - for example, with a selector like link_or_button
, which would allow changing from a button to a link and vice-versa without a test failing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a development perspective, being flexible enough to support finding or clicking on an <a>
or <button>
has appeal.
My goal with these guidelines is to encourage design and development exercises to center accessibility. With that in mind, the semantics of a <a>
and <button>
vary. The expectation for an <a>
element are navigation focused, whereas <button>
elements are action focused. To that end, assistive technologies like Screen Readers (e.g. VoiceOver) present Button and Links in different lists.
Being specific in your testing expectations forces you to decide which element to use ahead of time. A failing test is valuable feedback that the element in question was changed, and provides you with an inflection point to re-consider or re-affirm that decision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, thank you for explaining. Indeed there's a role difference between links and buttons and it really matters for accessibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Things that will make me read your PR more carefully: the eq
selector ... I feel like have_content
is the eq
selector of the Capybara world.
Thanks for teaching me about within_fieldset
!
Thanks for adding this! Hopefully this nudges us towards better form design while also making our tests less brittle 👍 |
Capybara provides a wide range of semantically meaningful selectors,
and also provides affordances for applications to declare their own
through the Capybara.add_selector interface.
In the sample Acceptance test, replace the Exercise phase's within
call with a call to within_fieldset.
The
within
call's"#sign_up"
argument scopes the subsequent calls tofill_in
andclick_button
to operate on elements nested inside anelement with
[id="sign_up"]
. The"Sign up"
argument passed to the
within_fieldset
call (its "locator") scopes the calls tofill_in
andclick_button
to operate on elements nested inside a<fieldset>
element with a descendant
<legend>
element thatcontains the text "Sign up".
Similarly, replace the Verify phase's have_content call with one to
have_button. A call to
have_content("Sign out")
will succeed ifthe phrase
Sign out
exists as visible text anywhere on the page. Acall to
have_button("Sign out")
will only match a<button>
elementwith
Sign out
as its text or an<input type="button" value="Sign out">
element.