Add documentation surrounding [data-qa-selector]
Documentation was lacking for the [data-qa-selector] method of defining methods vs .qa-selector method.
This commit is contained in:
parent
b921b2d1fb
commit
a7e16ee208
2 changed files with 45 additions and 26 deletions
|
@ -92,20 +92,25 @@ end
|
||||||
The `view` DSL method will correspond to the rails View, partial, or vue component that renders the elements.
|
The `view` DSL method will correspond to the rails View, partial, or vue component that renders the elements.
|
||||||
|
|
||||||
The `element` DSL method in turn declares an element for which a corresponding
|
The `element` DSL method in turn declares an element for which a corresponding
|
||||||
`qa-element-name-dasherized` CSS class will need to be added to the view file.
|
`data-qa-selector=element_name_snaked` data attribute will need to be added to the view file.
|
||||||
|
|
||||||
You can also define a value (String or Regexp) to match to the actual view
|
You can also define a value (String or Regexp) to match to the actual view
|
||||||
code but **this is deprecated** in favor of the above method for two reasons:
|
code but **this is deprecated** in favor of the above method for two reasons:
|
||||||
|
|
||||||
- Consistency: there is only one way to define an element
|
- Consistency: there is only one way to define an element
|
||||||
- Separation of concerns: QA uses dedicated CSS classes instead of reusing code
|
- Separation of concerns: QA uses dedicated `data-qa-*` attributes instead of reusing code
|
||||||
or classes used by other components (e.g. `js-*` classes etc.)
|
or classes used by other components (e.g. `js-*` classes etc.)
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
view 'app/views/my/view.html.haml' do
|
view 'app/views/my/view.html.haml' do
|
||||||
# Implicitly require `.qa-logout-button` CSS class to be present in the view
|
|
||||||
|
### Good ###
|
||||||
|
|
||||||
|
# Implicitly require the CSS selector `[data-qa-selector="logout_button"]` to be present in the view
|
||||||
element :logout_button
|
element :logout_button
|
||||||
|
|
||||||
|
### Bad ###
|
||||||
|
|
||||||
## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
|
## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
|
||||||
# Require `f.submit "Sign in"` to be present in `my/view.html.haml
|
# Require `f.submit "Sign in"` to be present in `my/view.html.haml
|
||||||
element :my_button, 'f.submit "Sign in"' # rubocop:disable QA/ElementWithPattern
|
element :my_button, 'f.submit "Sign in"' # rubocop:disable QA/ElementWithPattern
|
||||||
|
@ -129,24 +134,39 @@ view 'app/views/my/view.html.haml' do
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
To add these elements to the view, you must change the rails View, partial, or vue component by adding a `qa-element-descriptor` class
|
To add these elements to the view, you must change the rails View, partial, or vue component by adding a `data-qa-selector` attribute
|
||||||
for each element defined.
|
for each element defined.
|
||||||
|
|
||||||
In our case, `qa-login-field`, `qa-password-field` and `qa-sign-in-button`
|
In our case, `data-qa-selector="login_field"`, `data-qa-selector="password_field"` and `data-qa-selector="sign_in_button"`
|
||||||
|
|
||||||
**app/views/my/view.html.haml**
|
**app/views/my/view.html.haml**
|
||||||
|
|
||||||
```haml
|
```haml
|
||||||
= f.text_field :login, class: "form-control top qa-login-field", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required."
|
= f.text_field :login, class: "form-control top", autofocus: "autofocus", autocapitalize: "off", autocorrect: "off", required: true, title: "This field is required.", data: { qa_selector: 'login_field' }
|
||||||
= f.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required."
|
= f.password_field :password, class: "form-control bottom", required: true, title: "This field is required.", data: { qa_selector: 'password_field' }
|
||||||
= f.submit "Sign in", class: "btn btn-success qa-sign-in-button"
|
= f.submit "Sign in", class: "btn btn-success", data: { qa_selector: 'sign_in_button' }
|
||||||
```
|
```
|
||||||
|
|
||||||
Things to note:
|
Things to note:
|
||||||
|
|
||||||
- The CSS class must be `kebab-cased` (separated with hyphens "`-`")
|
- The name of the element and the qa_selector must match and be snake_cased
|
||||||
- If the element appears on the page unconditionally, add `required: true` to the element. See
|
- If the element appears on the page unconditionally, add `required: true` to the element. See
|
||||||
[Dynamic element validation](dynamic_element_validation.md)
|
[Dynamic element validation](dynamic_element_validation.md)
|
||||||
|
- You may see `.qa-selector` classes in existing Page Objects. We should prefer the [`data-qa-selector`](#data-qa-selector-vs-qa-selector)
|
||||||
|
method of definition over the `.qa-selector` CSS class
|
||||||
|
|
||||||
|
|
||||||
|
### `data-qa-selector` vs `.qa-selector`
|
||||||
|
|
||||||
|
> Introduced in GitLab 12.1
|
||||||
|
|
||||||
|
There are two supported methods of defining elements within a view.
|
||||||
|
|
||||||
|
1. `data-qa-selector` attribute
|
||||||
|
1. `.qa-selector` class
|
||||||
|
|
||||||
|
Any existing `.qa-selector` class should be considered deprecated
|
||||||
|
and we should prefer the `data-qa-selector` method of definition.
|
||||||
|
|
||||||
## Running the test locally
|
## Running the test locally
|
||||||
|
|
||||||
|
|
|
@ -101,7 +101,7 @@ it 'replaces an existing label if it has the same key' do
|
||||||
page.find('#content-body').click
|
page.find('#content-body').click
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
labels_block = page.find('.qa-labels-block')
|
labels_block = page.find(%q([data-qa-selector="labels_block"]))
|
||||||
|
|
||||||
expect(labels_block).to have_content('animal::dolphin')
|
expect(labels_block).to have_content('animal::dolphin')
|
||||||
expect(labels_block).not_to have_content('animal::fox')
|
expect(labels_block).not_to have_content('animal::fox')
|
||||||
|
@ -130,7 +130,7 @@ it 'keeps both scoped labels when adding a label with a different key' do
|
||||||
page.find('#content-body').click
|
page.find('#content-body').click
|
||||||
page.refresh
|
page.refresh
|
||||||
|
|
||||||
labels_block = page.find('.qa-labels-block')
|
labels_block = page.find(%q([data-qa-selector="labels_block"]))
|
||||||
|
|
||||||
expect(labels_block).to have_content('animal::fox')
|
expect(labels_block).to have_content('animal::fox')
|
||||||
expect(labels_block).to have_content('plant::orchid')
|
expect(labels_block).to have_content('plant::orchid')
|
||||||
|
@ -139,7 +139,7 @@ it 'keeps both scoped labels when adding a label with a different key' do
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note that elements are always located using CSS selectors, and a good practice is to add test-specific selectors (this is called adding testability to the application and we will talk more about it later.) For example, the `labels_block` element uses the selector `.qa-labels-block`, which was added specifically for testing purposes.
|
> Note that elements are always located using CSS selectors, and a good practice is to add test-specific selectors (this is called "testability"). For example, the `labels_block` element uses the CSS selector [`data-qa-selector="labels_block"`](page_objects.md#data-qa-selector-vs-qa-selector), which was added specifically for testing purposes.
|
||||||
|
|
||||||
Below are the steps that the test covers:
|
Below are the steps that the test covers:
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ end
|
||||||
it 'replaces an existing label if it has the same key' do
|
it 'replaces an existing label if it has the same key' do
|
||||||
select_label_and_refresh @new_label_same_scope
|
select_label_and_refresh @new_label_same_scope
|
||||||
|
|
||||||
labels_block = page.find('.qa-labels-block')
|
labels_block = page.find(%q([data-qa-selector="labels_block"]))
|
||||||
|
|
||||||
expect(labels_block).to have_content(@new_label_same_scope)
|
expect(labels_block).to have_content(@new_label_same_scope)
|
||||||
expect(labels_block).not_to have_content(@initial_label)
|
expect(labels_block).not_to have_content(@initial_label)
|
||||||
|
@ -179,7 +179,7 @@ end
|
||||||
it 'keeps both scoped label when adding a label with a different key' do
|
it 'keeps both scoped label when adding a label with a different key' do
|
||||||
select_label_and_refresh @new_label_different_scope
|
select_label_and_refresh @new_label_different_scope
|
||||||
|
|
||||||
labels_block = page.find('.qa-labels-block')
|
labels_block = page.find(%q([data-qa-selector="labels_block"]))
|
||||||
|
|
||||||
expect(labels_blocks).to have_content(@new_label_different_scope)
|
expect(labels_blocks).to have_content(@new_label_different_scope)
|
||||||
expect(labels_blocks).to have_content(@initial_label)
|
expect(labels_blocks).to have_content(@initial_label)
|
||||||
|
@ -305,7 +305,7 @@ module QA
|
||||||
it 'correctly applies scoped labels depending on if they are from the same or a different scope' do
|
it 'correctly applies scoped labels depending on if they are from the same or a different scope' do
|
||||||
select_labels_and_refresh [@new_label_same_scope, @new_label_different_scope]
|
select_labels_and_refresh [@new_label_same_scope, @new_label_different_scope]
|
||||||
|
|
||||||
labels_block = page.all('.qa-labels-block')
|
labels_block = page.all(%q([data-qa-selector="labels_block"]))
|
||||||
|
|
||||||
expect(labels_block).to have_content(@new_label_same_scope)
|
expect(labels_block).to have_content(@new_label_same_scope)
|
||||||
expect(labels_block).to have_content(@new_label_different_scope)
|
expect(labels_block).to have_content(@new_label_different_scope)
|
||||||
|
@ -552,37 +552,36 @@ The `text_of_labels_block` method is a simple method that returns the `:labels_b
|
||||||
|
|
||||||
#### Updates in the view (*.html.haml) and `dropdowns_helper.rb` files
|
#### Updates in the view (*.html.haml) and `dropdowns_helper.rb` files
|
||||||
|
|
||||||
Now let's change the view and the `dropdowns_helper` files to add the selectors that relate to the Page Object.
|
Now let's change the view and the `dropdowns_helper` files to add the selectors that relate to the [Page Objects].
|
||||||
|
|
||||||
In the [app/views/shared/issuable/_sidebar.html.haml](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/views/shared/issuable/_sidebar.html.haml) file, on [line 105 ](https://gitlab.com/gitlab-org/gitlab-ee/blob/84043fa72ca7f83ae9cde48ad670e6d5d16501a3/app/views/shared/issuable/_sidebar.html.haml#L105), add an extra class `qa-edit-link-labels`.
|
In [`app/views/shared/issuable/_sidebar.html.haml:105`](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/views/shared/issuable/_sidebar.html.haml#L105), add a `data: { qa_selector: 'edit_link_labels' }` data attribute.
|
||||||
|
|
||||||
The code should look like this:
|
The code should look like this:
|
||||||
|
|
||||||
```haml
|
```haml
|
||||||
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right qa-edit-link-labels'
|
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right', data: { qa_selector: 'edit_link_labels' }
|
||||||
```
|
```
|
||||||
|
|
||||||
In the same file, on [line 121](https://gitlab.com/gitlab-org/gitlab-ee/blob/84043fa72ca7f83ae9cde48ad670e6d5d16501a3/app/views/shared/issuable/_sidebar.html.haml#L121), add an extra class `.qa-dropdown-menu-labels`.
|
In the same file, on [line 121](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/views/shared/issuable/_sidebar.html.haml#L121), add a `data: { qa_selector: 'dropdown_menu_labels' }` data attribute.
|
||||||
|
|
||||||
The code should look like this:
|
The code should look like this:
|
||||||
|
|
||||||
```haml
|
```haml
|
||||||
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.qa-dropdown-menu-labels
|
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height{ data: { qa_selector: 'dropdown_menu_labels' } }
|
||||||
```
|
```
|
||||||
|
|
||||||
In the [`dropdowns_helper.rb`](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/app/helpers/dropdowns_helper.rb) file, on [line 94](https://gitlab.com/gitlab-org/gitlab-ee/blob/99e51a374f2c20bee0989cac802e4b5621f72714/app/helpers/dropdowns_helper.rb#L94), add an extra class `qa-dropdown-input-field`.
|
In [`app/helpers/dropdowns_helper.rb:94`](https://gitlab.com/gitlab-org/gitlab-ee/blob/7ca12defc7a965987b162a6ebef302f95dc8867f/app/helpers/dropdowns_helper.rb#L94), add a `data: { qa_selector: 'dropdown_input_field' }` data attribute.
|
||||||
|
|
||||||
The code should look like this:
|
The code should look like this:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field qa-dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
|
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off', data: { qa_selector: 'dropdown_input_field' }
|
||||||
```
|
```
|
||||||
|
|
||||||
> Classes starting with `qa-` are used for testing purposes only, and by defining such classes in the elements we add **testability** in the application.
|
> `data-qa-*` data attributes and CSS classes starting with `qa-` are used solely for the purpose of QA and testing.
|
||||||
|
> By defining these, we add **testability** to the application.
|
||||||
|
|
||||||
> When defining a class like `qa-labels-block`, it is transformed into `:labels_block` for usage in the Page Objects. So, `qa-edit-link-labels` is transformed into `:edit_link_labels`, `qa-dropdown-menu-labels` is transformed into `:dropdown_menu_labels`, and `qa-dropdown-input-field` is transformed into `:dropdown_input_field`. Also, we use a [sanity test](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/page#how-did-we-solve-fragile-tests-problem) to check that defined elements have their respective `qa-` selectors in the specified views.
|
> When defining a data attribute like: `qa_selector: 'labels_block'`, it should match the element definition: `element :labels_block`. We use a [sanity test](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa/qa/page#how-did-we-solve-fragile-tests-problem) to check that defined elements have their respective selectors in the specified views.
|
||||||
|
|
||||||
> We did not define the `qa-labels-block` class in the `app/views/shared/issuable/_sidebar.html.haml` file because it was already there to be used.
|
|
||||||
|
|
||||||
#### Updates in the `QA::Page::Base` class
|
#### Updates in the `QA::Page::Base` class
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue