Commit a7e16ee2 authored by Dan Davison's avatar Dan Davison

Add documentation surrounding [data-qa-selector]

Documentation was lacking for the [data-qa-selector]
method of defining methods vs .qa-selector method.
parent b921b2d1
......@@ -92,20 +92,25 @@ end
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
`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
code but **this is deprecated** in favor of the above method for two reasons:
- 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.)
```ruby
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
### Bad ###
## This is deprecated and forbidden by the `QA/ElementWithPattern` RuboCop cop.
# Require `f.submit "Sign in"` to be present in `my/view.html.haml
element :my_button, 'f.submit "Sign in"' # rubocop:disable QA/ElementWithPattern
......@@ -129,24 +134,39 @@ view 'app/views/my/view.html.haml' do
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.
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**
```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.password_field :password, class: "form-control bottom qa-password-field", required: true, title: "This field is required."
= f.submit "Sign in", class: "btn btn-success qa-sign-in-button"
= 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", required: true, title: "This field is required.", data: { qa_selector: 'password_field' }
= f.submit "Sign in", class: "btn btn-success", data: { qa_selector: 'sign_in_button' }
```
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
[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
......
......@@ -101,7 +101,7 @@ it 'replaces an existing label if it has the same key' do
page.find('#content-body').click
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).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.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('plant::orchid')
......@@ -139,7 +139,7 @@ it 'keeps both scoped labels when adding a label with a different key' do
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:
......@@ -168,7 +168,7 @@ end
it 'replaces an existing label if it has the same key' do
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).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
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(@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
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_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
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:
```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:
```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:
```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.
> 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.
> 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.
#### Updates in the `QA::Page::Base` class
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment