Skip to content

Commit bf7f5f3

Browse files
committed
Merge remote-tracking branch 'origin/MQE-1706' into MQE-1706
2 parents 18abf17 + e8807e7 commit bf7f5f3

File tree

2 files changed

+157
-56
lines changed

2 files changed

+157
-56
lines changed

docs/guides/selectors.md

Lines changed: 89 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,162 @@
1-
# How To Write Good Selectors
1+
# How To write good selectors
22

3-
Selectors are an atomic unit of test writing. They fit into the hierarchy like this: MFTF tests make use of action groups, which are made up of actions, which interact with page objects, which contain elements, which are specified by selectors. Because they are the building blocks, we must take care when writing them.
3+
Selectors are the atomic unit of test writing. They fit into the hierarchy like this: MFTF tests make use of action groups > which are made up of actions > which interact with page objects > which contain elements > which are specified by selectors. Because they are fundamental building blocks, we must take care when writing them.
44

55
## What is a selector?
66

7-
A "selector" is like an address to an element in the Document Object Model (DOM). It locates those elements and allows MFTF to interact with them. By element we mean things such as input fields, buttons, tables, divs, etc. By interact we mean actions such as click, fill field, etc.
7+
A "selector" works like an address to an element in the Document Object Model (DOM). It specifies page elements and allows MFTF to interact with them.
8+
By 'element' we mean things such as input fields, buttons, tables, divs, etc.
9+
By 'interact' we mean actions such as click, fill field, etc.
810

911
Selectors live inside of MFTF page objects and are meant to be highly re-usable amongst all tests. They can be written in either CSS or XPath.
1012

1113
## Why are good selectors important?
1214

13-
Good selectors are important because they are the most re-used component of functional testing. They are the lowest building blocks of tests, the foundation. If they are unstable then everything else built on top of them will inherit that instability.
15+
Good selectors are important because they are the most re-used component of functional testing. They are the lowest building blocks of tests; the foundation. If they are unstable then everything else built on top of them will inherit that instability.
1416

1517
## How do I write good selectors?
1618

17-
We could cover this subject with an infinite amount of documentation and some lessons only come from experience. But this guide will explain some DOs and DONTs to help you along the way towards mastery.
19+
We could cover this subject with an infinite amount of documentation and some lessons only come from experience. This guide explains some DOs and DONTs to help you along the way towards selector mastery.
1820

1921
### Inspecting the DOM
2022

21-
To write a selector you need to be able to see the DOM and find the element within it. Fortunately you don't have to look at the entire DOM every time. Nor do you have to read it from top to bottom. Instead you can make use of your browsers built in developer tools or go a step further and try out some popular browser extensions.
23+
To write a selector you need to be able to see the DOM and find the element within it. Fortunately you do not have to look at the entire DOM every time. Nor do you have to read it from top to bottom. Instead you can make use of your browsers built-in developer tools or go a step further and try out some popular browser extensions.
2224

2325
See these links for more information about built-in browser developer tools:
24-
* [Chrome Developer Tools](https://developers.google.com/web/tools/chrome-devtools/)
25-
* [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools)
26+
27+
* [Chrome Developer Tools](https://developers.google.com/web/tools/chrome-devtools/)
28+
* [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools)
2629

2730
See these links for common browser addons that may offer advantages over browser developer tools:
28-
* [Live editor for CSS, Less & Sass - Magic CSS](https://chrome.google.com/webstore/detail/live-editor-for-css-less/ifhikkcafabcgolfjegfcgloomalapol?hl=en)
29-
* [XPath Helper](https://chrome.google.com/webstore/detail/xpath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl?hl=en)
31+
32+
* [Live editor for CSS, Less & Sass - Magic CSS](https://chrome.google.com/webstore/detail/live-editor-for-css-less/ifhikkcafabcgolfjegfcgloomalapol?hl=en)
33+
* [XPath Helper](https://chrome.google.com/webstore/detail/xpath-helper/hgimnogjllphhhkhlmebbmlgjoejdpjl?hl=en)
3034

3135
### CSS vs XPath
3236

33-
There are many similarities and many differences between CSS and XPath. It is too much to cover in this guide alone. You should search out additional material on your own. In general:
37+
There are similarities and differences between CSS and XPath. Both are powerful and complex in ways that are outside of the scope of this document.
38+
In general:
3439

35-
* CSS is more stable, easier to read, and easier to maintain (typically).
36-
* XPath provides several powerful tools and it has been around the longest so it’s well documented.
37-
* XPath can be less stable and potentially unsupported by certain actions in Selenium.
40+
* CSS is more stable, easier to read, and easier to maintain (typically).
41+
* XPath provides several powerful tools and it has been around the longest so it is well documented.
42+
* XPath can be less stable and potentially unsupported by certain actions in Selenium.
3843

3944
### Priority
4045

41-
The best and most simple selector will always be `#some-id-here`. If only we were so lucky to have this every time. When writing selectors, you should prioritize looking for these starting points to build your selector from.
46+
The best and most simple selector will always be to use an element ID: `#some-id-here`. If only we were so lucky to have this every time.
4247

43-
1. ID, Name, Class, or anything else that is unique to the element
48+
When writing selectors, you should prioritize finding in this order:
49+
50+
1. ID, name, class, or anything else that is unique to the element
4451
2. Complex CSS selectors
4552
3. XPath selectors
46-
4. If none of the above work for you, then the last resort is to ask a developer to add a unique ID or Class to the element you're trying to select.
53+
4. If none of the above work for you, then the last resort is to ask a developer to add a unique ID or class to the element you are trying to select.
4754

48-
Important: Notice we prefer the use of CSS selectors above XPath selectors when possible.
55+
We suggest the use of CSS selectors above XPath selectors when possible.
4956

50-
### Do's and Don'ts
57+
### Writing proper selectors
5158

52-
Let's start with the Don'ts because they're more important.
59+
There are correct ways of writing selectors and incorrect ways. These suggestions will help you write better selectors.
5360

54-
#### Don't #1
61+
#### Incorrect - copy selector/xpath
5562

5663
DO NOT right click on an element in your browser developer tools and select "Copy selector" or "Copy XPath" and simply use that as your selector. These auto-generated selectors are prime examples of what not to do.
5764

5865
These are bad:
5966

60-
```
67+
```css
6168
#html-body > section > div > div > div > div
6269
```
6370

64-
```
71+
```xpath
6572
//*[@id='html-body']/section/div/div/div/div
6673
```
6774
68-
They both include unnecessary hierarchical details. As written, we're looking for a `div` inside of a `div` inside of a `div` inside of... you get the picture. But if an application developer adds another `div` parent tomorrow, even for stylistic reasons, then this selector will break. Furthermore, when reading it, it's not even clear what was the intended target in the first place.
75+
Both include unnecessary hierarchical details. As written, we are looking for a `div` inside of a `div` inside of a `div` inside of... you get the picture. If an application developer adds another `div` parent tomorrow, for whatever reason, this selector will break. Furthermore, when reading it, it is not clear what the intended target is. It may also grab other elements that were not intended.
6976
70-
#### Don't #2
77+
#### Do not be too general
7178
72-
DO NOT make your selectors too generic. If a selector is too generic, there is a high probability that it will match multiple elements on the page. Maybe not today. But probably tomorrow when the application under test changes.
79+
DO NOT make your selectors too generic. If a selector is too generic, there is a high probability that it will match multiple elements on the page. Maybe not today, but perhaps tomorrow when the application being tested changes.
7380
7481
These are bad:
7582
76-
```
83+
```html
7784
input[name*='firstname']
7885
```
7986
80-
The `*=` means `contains`. So the selector is saying find an input whose name contains the string "firstname". But if a future change adds a new element to the page whose name also contains "firstname", then this selector will now match two elements and that's bad.
87+
The `*=` means `contains`. The selector is saying 'find an input whose name contains the string "firstname"'. But if a future change adds a new element to the page whose name also contains "firstname", then this selector will match two elements and that is bad.
8188
82-
```
89+
```css
8390
.add
8491
```
8592
86-
Similarly here, this will match any element which contains the class `.add`. This is brittle and susceptible to breaking when new elements/styles are added to the page.
93+
Similarly here, this will match all elements which contains the class `.add`. This is brittle and susceptible to breaking when new elements/styles are added to the page.
8794
88-
#### Don't #3
95+
#### Avoid being too specific
8996
90-
DO NOT make your selectors too specific either. If a selector is too specific, there is a high probability that it will break due to even minor changes to the application under test.
97+
DO NOT make your selectors too specific either. If a selector is too specific, there is a high probability that it will break due to even minor changes to the application being tested.
9198
9299
These are bad:
93100
94-
```
101+
```css
95102
#container .dashboard-advanced-reports .dashboard-advanced-reports-description .dashboard-advanced-reports-title
96103
```
97104
98105
This selector is too brittle. It would break very easily if an application developer does something as simple as adding a parent container for style reasons.
99106
100-
```
107+
```xpath
101108
//*[@id='container']/*[@class='dashboard-advanced-reports']/*[@class='dashboard-advanced-reports-description']/*[@class='dashboard-advanced-reports-title']
102109
```
103110
104111
This is the same selector as above, but represented in XPath instead of CSS. It is brittle for the same reasons.
105112
106-
#### Do #1
113+
#### XPath selectors should not use @attribute="foo"
114+
115+
This XPath is fragile. It would fail if the attribute was `attribute="foo bar"`. Instead you should use `contains(@attribute, "foo")` where @attribute is any valid attribute such as @text or @class.
116+
117+
#### CSS and XPath selectors should avoid making use of hardcoded indices
118+
119+
Hardcoded values are by definition not flexible. A hardcoded index may change if new code is introduced. Instead, parameterize the selector.
120+
121+
GOOD: .foo:nth-of-type({{index}})
122+
123+
BAD: .foo:nth-of-type(1)
124+
125+
GOOD: button[contains(@id, "foo")][{{index}}]
126+
127+
BAD: button[contains(@id, "foo")][1]
128+
129+
GOOD: #actions__{{index}}__aggregator
130+
131+
BAD: #actions__1__aggregator
132+
133+
#### CSS and XPath selectors MUST NOT reference the @data-bind attribute
134+
135+
The @data-bind attribute is used by KnockoutJS, a framework Magento uses to create dynamic Javascript pages. Since this @data-bind attribute is tied to a specific framework, it should not be used for selectors. If Magento decides to use a different framework then these @data-bind selectors would break.
136+
137+
#### Use isolation
107138
108139
You should think in terms of "isolation" when writing new selectors.
109140
110-
For example, let's say you have a login form that contains a username field, a password field, and a Sign In button. First isolate the parent element. Perhaps it's `#login-form`. Then target the child element under that parent element: `.sign-in-button` The result is `#login-form .sign-in-button`.
141+
For example, say you have a login form that contains a username field, a password field, and a 'Sign In' button. First isolate the parent element. Perhaps it's `#login-form`. Then target the child element under that parent element: `.sign-in-button` The result is `#login-form .sign-in-button`.
111142
112-
Thinking like this will reduce the amount of DOM that you need to worry about.
143+
Using isolation techniques reduces the amount of DOM that needs to be processed. This makes the selector both accurate and efficient.
113144
114-
#### Do #2
145+
#### Use advanced notation
115146
116-
If you need to interact with the parent element but it's too generic, and the internal contents are unique then you need to:
147+
If you need to interact with the parent element but it is too generic, and the internal contents are unique then you need to:
117148
118-
1. Target the unique internal contents first
119-
2. Then jump to the parent element using `::parent`
149+
1. Target the unique internal contents first.
150+
1. Then jump to the parent element using `::parent`.
120151
121-
For example let's imagine you want to find a table row that contains the string "Jerry Seinfeld". You can use the following XPath selector:
152+
Imagine you want to find a table row that contains the string "Jerry Seinfeld". You can use the following XPath selector:
122153
123-
```
154+
```xpath
124155
//div[contains(text(), 'Jerry Seinfeld')]/parent::td/parent::tr
125156
```
126157
158+
Note in this instance that CSS does not have an equivalent to `::parent`, so XPath is a better choice.
159+
127160
### CSS Examples
128161
129162
Examples of common HTML elements and the corresponding selector to find that element in the DOM:
@@ -148,7 +181,7 @@ Whitespace|Descendant Combinator|Allows you to combine 2 or more selectors.|`#id
148181
`+`|Adjacent Sibling Combinator|Allows you to select an element THAT FOLLOWS DIRECTLY AFTER another specified element.|`#idname + .classname`
149182
`~`|General Sibling Combinator|Allows you to select an element THAT FOLLOWS (directly or indirectly) another specified element.|`#idname ~ .classname`
150183
151-
Examples of CSS attribute operators and their purpose
184+
Examples of CSS attribute operators and their purpose:
152185
153186
Symbol|Purpose|Example
154187
---|---|---
@@ -157,29 +190,29 @@ Symbol|Purpose|Example
157190
`~=`|Returns all elements that CONTAINS the given words delimited by spaces in the value.|`[attribute~='value']`
158191
`$=`|Returns all elements that ENDS WITH the substring in the value.|`[attribute$='value']`
159192
`^=`|Returns all elements that BEGIN EXACTLY WITH the substring in the value.|`[attribute^='value']`
160-
`!=`|Returns all elements that either DOESN’T HAVE the given attribute or the value of the attribute is NOT EQUAL to the value.|`[attribute!='value']`
193+
`!=`|Returns all elements that either DOES NOT HAVE the given attribute or the value of the attribute is NOT EQUAL to the value.|`[attribute!='value']`
161194
162195
### XPath Examples
163196
164197
#### `/` vs `//`
165198
166199
The absolute XPath selector is a single forward slash `/`. It is used to provide a direct path to the element from the root element.
167200
168-
WARNING: The `/` selector is brittle and should only be used sparingly.
201+
WARNING: The `/` selector is brittle and should be used sparingly.
169202
170-
Here's an example of what NOT to do, but this demonstrates how the selector works:
203+
Here is an example of what NOT to do, but this demonstrates how the selector works:
171204
172-
```
205+
```xpath
173206
/html/body/div[2]/div/div[2]/div[1]/div[2]/form/div/input
174207
```
175208
176-
In the BAD example above we are specifying a very precise path to an input element in the DOM.
209+
In the BAD example above, we are specifying a very precise path to an input element in the DOM, starting from the very top of the document.
177210
178211
Similarly, the relative XPath selector is a double forward slash `//`. It is used to start searching for an element anywhere in the DOM.
179212
180213
Example:
181214
182-
```
215+
```xpath
183216
//div[@class=’form-group’]//input[@id='user-message']
184217
```
185218
@@ -191,7 +224,7 @@ Example #1:
191224
192225
Given this HTML:
193226
194-
```
227+
```html
195228
<tr>
196229
<td>
197230
<div>Unique Value</div>
@@ -201,15 +234,15 @@ Given this HTML:
201234
202235
We can locate the `<tr>` element with this selector:
203236
204-
```
237+
```xpath
205238
//*[text()='Unique Value']/../..
206239
```
207240
208241
Example #2:
209242
210243
Given this HTML:
211244
212-
```
245+
```html
213246
<tr>
214247
<td>
215248
<a href=“#”>Edit</a>
@@ -222,13 +255,13 @@ Given this HTML:
222255
223256
We can locate the `<a>` element with this selector:
224257
225-
```
258+
```xpath
226259
//div[text()='Unique Value']/../..//a
227260
```
228261
229262
#### Attribute Selectors
230263
231-
Attribute selectors allow you to select an element that match a specific attribute value.
264+
Attribute selectors allow you to select elements that match a specific attribute value.
232265
233266
Examples:
234267
@@ -243,7 +276,7 @@ src|`<img src='/img.png'/>`|`//*[@src='/img.png']`
243276
244277
#### `contains()` Selector
245278
246-
The `contains()` selector allows you to select an element that contains an attribute value search string.
279+
The `contains()` selector allows you to select an element that contains an attribute value string.
247280
248281
Examples:
249282

docs/guides/test-modularity.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Test Modularity
2+
3+
One of MFTF's most distinguishing functionalities is the framework's modularity.
4+
5+
## What is test modularity
6+
7+
Within MFTF, test modularity can refer to two different concepts:
8+
9+
### Test material merging
10+
11+
This concept is covered extensively in the [merging] topic, so it will not be our focus in this guide
12+
13+
### Modular test materials
14+
15+
This refers to test materials being correctly owned by the right Magento module, and for tests to have references to only what their parent Magento module has a dependency on.
16+
17+
Since MFTF queries the Magento instance for enabled modules, MFTF test materials are included or excluded from the merging process dynamically, making proper ownership and dependencies a must.
18+
19+
## Why is test modularity important?
20+
21+
This concept is important simply because without proper modularity, tests or test materials may be incorrectly merged in (or be left out), leading to the the test itself being out of sync with the Magento instance.
22+
23+
For example, in a situation where an extension drastically alters the login process (something like two factor authentication), the only way the tests will be able to pass is if the test materials are correctly nested in the extension.
24+
25+
## How can I achieve test modularity?
26+
27+
Test modularity can be challenging, depending on the breadth of the changes being introduced in a module.
28+
29+
### Determine test material ownership
30+
31+
This is should be the first step when creating new test materials. We will use the `New Product` page as an example.
32+
33+
#### Intuitive reasoning
34+
35+
The easiest way to do this has limited application, but some times it's fairly obvious where a test material comes from due to nomenclature or functionality.
36+
37+
For instance, the following `<select>` for `Tax Class` clearly belongs to the `Tax` module
38+
39+
```xml
40+
<select class="admin__control-select" name="product[tax_class_id]"/>
41+
```
42+
43+
This approach will work on getting the quickest ownership, but it's fairly obvious that it may be necessary to double check
44+
45+
#### Deduction
46+
47+
This is the next step up in difficulty from the above method, as it involves searching through the Magento codebase.
48+
49+
Let's take the `Add Attribute` button for example. The button has an `id="addAttribute"`, and searching through the codebase for `"addAttribute"` will lead you to `Catalog/view/adminhtml/ui_component/product_form.xml`:
50+
```xml
51+
<button name="addAttribute" class="Magento\Catalog\Block\Adminhtml\Product\Edit\Button\AddAttribute"/>
52+
```
53+
54+
This means that `Add Attribute` button belongs to Catalog based on the above class namespace and filepath.
55+
56+
This kind of deduction is more involved, but it much more likely to give you the true source of the element.
57+
58+
### Use bin/mftf static-checks
59+
60+
The latter aspect of modular test materials involves test material references to other test materials, and making sure the dependencies are not out of sync with the parent module.
61+
62+
The `static-checks` command includes a test material ownership check that should help suss out these kind of dependency issues.
63+
64+
See [mftf commands] for more information.
65+
66+
<!-- Link definitions -->
67+
[merging]: ../merging.md
68+
[mftf commands]: ../commands/mftf.md

0 commit comments

Comments
 (0)