-
Notifications
You must be signed in to change notification settings - Fork 839
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
844 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
link: | ||
type: doc | ||
id: testing-introduction |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
--- | ||
title: Testing | ||
slug: /guidelines/testing | ||
id: testing-introduction | ||
--- | ||
|
||
<p style={{ fontSize: '22px' }}> | ||
Learn how we test our code internally and how you should test integration with EUI components to ensure | ||
good test coverage and easy maintainability. | ||
</p> | ||
|
||
## How we test our components | ||
|
||
We maintain a large amount of integration and end-to-end tests suites for every component we develop. | ||
They ensure high coverage of most paths within our components, including accessibility features, and lets us know | ||
whenever we change something that may affect your applications. | ||
|
||
Internally, we use [Enzyme](https://enzymejs.github.io/enzyme/) | ||
and [React Testing Library](https://testing-library.com/) for integration testing, | ||
[Cypress](https://www.cypress.io/) for end-to-end component testing, as well as static code analysis | ||
and type checking tools that run automatically on every pull request. You can find them all in our | ||
[GitHub repository](https://github.com/elastic/eui). | ||
|
||
All of these tests, including a manual code review and QA steps must pass before the changes are merged | ||
to confirm they're up to our standards. | ||
|
||
## How should you test views using EUI components? | ||
|
||
Testing integration with 3rd-party UI libraries like EUI is slightly different from testing first-party components. | ||
In general, just like with any other dependency, you should not test implementation details like the exact | ||
DOM structure or class names. They're likely to change between versions and usually don't prove a component works | ||
correctly anyway, so it's best to skip these. | ||
|
||
Instead, you should **focus on testing your code and the integration with EUI components first**. | ||
|
||
Depending on your testing stack, **integration tests** might be written using React Testing Library (RTL), | ||
and are meant to be low-level tests of components working together as expected. For example, if your component fetches | ||
and displays processed data a certain way whenever an `<EuiButton />` is clicked, you should write | ||
a test that simulates a click on that button and verifies the data is actually fetched and displayed the way | ||
you expect it to. Doing this will confirm your code integrates with `<EuiButton />` correctly by passing | ||
the right `onClick` handler, and so that your handler function fetches and updates the data to display the right way. | ||
|
||
On top of that, we also recommend writing **end-to-end (e2e) tests** that go through the flow just like a real user | ||
would. In addition to testing what integration tests do, they ensure a browser loads and executes your code properly | ||
as a whole, reacts to real user actions, makes needed network and API calls, and way more. You can use frameworks | ||
like [Cypress](https://cypress.io), [Selenium](https://www.selenium.dev/), [Playwright](https://playwright.dev/), | ||
and more to write end-to-end tests for your application. | ||
|
||
When written properly, integration and end-to-end tests will validate if the code you're shipping actually works for | ||
end users, and that the changes you introduce over time don't break the application. This means you can iterate | ||
faster and spend less time on manual testing. | ||
|
||
**Writing good tests isn't easy**. This is why we've prepared a set of general and component-specific guidelines | ||
to help you in that process, and we strongly encourage you to read them. | ||
In case these aren't enough, we're here to help! Please reach out to us by opening a GitHub | ||
[issue](https://github.com/elastic/eui/issues/new/choose) or | ||
[discussion](https://github.com/elastic/eui/discussions/new/choose). | ||
|
||
:::info Elastic employees | ||
If you're an Elastic employee, we recommend reaching out on the **#eui** Slack channel first. | ||
::: | ||
|
||
### Choosing the right selectors | ||
|
||
Whenever writing any kind of UI tests, choosing right selectors is the key to making tests reliable long-term. | ||
We recommend using the `data-test-subj` attributes (e.g., `[data-test-subj="comboBoxSearchInput"]`), | ||
ARIA `role` attributes (e.g., `[role="dialog"]`), | ||
and other [semantic queries](https://testing-library.com/docs/queries/about/#priority) for selectors whenever possible | ||
to ensure they reference the same underlying element between versions. | ||
|
||
You can find the list of available `data-test-subj` and other attributes as well as what semantic query selectors | ||
we recommend to use in component-specific testing documentation pages. | ||
|
||
If you need to use a custom, non-semantic selector (e.g., `div > span.title` or `span:first-child`) | ||
that's not a one-off, please open a [GitHub issue](https://github.com/elastic/eui/issues/new/choose), so we can add it, | ||
or even better so - try adding it yourself and contribute to EUI by opening a pull request! 🎉 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
--- | ||
title: Testing recommendations | ||
sidebar_label: Recommendations | ||
--- | ||
|
||
<p style={{ fontSize: '22px' }}> | ||
Our general set of do's and don'ts for testing components and views. | ||
</p> | ||
|
||
## Choose the right selectors | ||
|
||
Follow RTL's [Guiding Principles](https://testing-library.com/docs/guiding-principles/) | ||
and [query priorities](https://testing-library.com/docs/queries/about/#priority) when choosing right element selectors. | ||
|
||
Prioritize accessible and semantic queries (e.g., `[role="dialog"]`) followed by `data-test-subj` attributes over | ||
other, more complicated and prone to breaking queries (e.g. `div > span.title`) whenever possible. | ||
|
||
Check out our component-specific testing docs to find the selectors we officially support. | ||
|
||
**Do:** | ||
```js | ||
screen.getByRole('dialog'); // react-testing-library | ||
cy.get('[role="dialog"]'); // cypress | ||
driver.findElement(By.cssSelector('[role="dialog"]')); // selenium | ||
``` | ||
|
||
**Don't:** | ||
```js | ||
container.querySelector('.euiFlyout'); // react-testing-library | ||
cy.get('.euiFlyout'); // cypress | ||
driver.findElement(By.cssSelector('.euiFlyout')); // selenium | ||
``` | ||
|
||
## Don't use snapshots | ||
|
||
**The EUI team strongly discourages snapshot testing**, despite its simplicity. | ||
Snapshot tests are prone to frequent failures due to the smallest things, like whitespace changes. | ||
Developers often update stored snapshots when they see them fail without thinking too much about why they fail. | ||
|
||
Tests should tell a story and be considered an instant red flag whenever they fail. | ||
They should focus on the important details like the data a component is displaying | ||
or if the data is coming from a prop or being dynamically calculated. | ||
|
||
Instead, consider writing simple but precise assertion tests. | ||
|
||
**Do:** | ||
```js | ||
const { getByText, getByRole } = render(<MyComponent />); | ||
expect(getByText('Hello, World!')).toBeInTheDocument(); | ||
expect(getByRole('button')).toHaveTextContent('Save'); | ||
``` | ||
|
||
**Don't:** | ||
```js | ||
const { container } = render(<MyComponent />); // react-testing-library | ||
expect(container).toMatchSnapshot(); | ||
``` | ||
|
||
## Avoid time-based waits | ||
|
||
Sometimes the easiest solution to fixing a test is adding a wait/sleep call. In most cases, though, | ||
this can't be considered a reliable fix, because: | ||
|
||
1. It significantly increases total test run time, especially when used often | ||
2. Every machine will take a different amount of time to execute the code, and some — especially CI runners | ||
— are prone to lag during the test run. | ||
|
||
Instead, use the utilities available for every testing framework to wait for elements to appear | ||
or for asynchronous operations to finish execution. | ||
|
||
**Do:** | ||
```js | ||
screen.getByRole('button', { name: 'Save document' }); | ||
expect(await screen.findByText('Document saved successfully')).toBeInTheDocument(); | ||
``` | ||
|
||
**Don't:** | ||
```js | ||
screen.getByRole('button', { name: 'Save document' }); | ||
await new Promise((resolve) => setTimeout(resolve, 1000)); | ||
expect(screen.getByText('Document saved successfully')).toBeInTheDocument(); | ||
``` | ||
|
||
## Write clear test names | ||
|
||
Test cases and suites should have unambiguous names and match the naming convention throughout the project. | ||
Use short but descriptive names written in plain English. | ||
|
||
We recommend using the third-person singular simple present tense for its short form and ease of reading. | ||
|
||
**Do:** | ||
```js | ||
describe('arraySearch()', () => { // use tested function name as the root group name | ||
it('accepts object arrays', () => { /* [...] */ }); | ||
it('accepts string arrays', () => { /* [...] */ }); | ||
it('throw on non-array inputs', () => { /* [...] */}); | ||
it('supports the `options.caseSensitive` option', () => { /*[...]*/ }); | ||
}); | ||
``` | ||
|
||
**Don't:** | ||
```js | ||
describe('array search', () => { // bad: not pointing to what exactly this group is testing | ||
it('object arrays', () => { /* [...] */ }); // bad: not enough context | ||
it('arraySearch(["a", "b"])', () => { /* [...] */ }); // bad: function call example may not be easily understandable | ||
it('should throw on non-array inputs', () => { /* [...] */ }); | ||
it('supports options.caseSensitive', () => { /* [...] */ }); // bad: using two different naming conventions; see line above | ||
}); | ||
``` | ||
|
||
### Wrap property names and data in `` ` `` | ||
|
||
When including property and argument names in the test name string, wrap them in backticks (`` ` ``) to clearly | ||
separate them from the rest of the text. | ||
|
||
**Do:** | ||
```js | ||
it('returns an empty object when the `value` string is empty'); | ||
``` | ||
|
||
**Don't:** | ||
```js | ||
it('returns an empty object when the value string is empty'); | ||
``` | ||
|
||
## Add debug-friendly comments or error messages | ||
|
||
Consider adding custom error messages or code comments for assertions that are not obvious. | ||
For [jest](https://jestjs.io/), we recommend adding a comment on top or to the right of the assertion, | ||
so in case of an error, it will be printed out together as the failing code fragment. | ||
|
||
**Do:** | ||
```js | ||
// Total should equal the result of 1234^2 / 3.14 rounded to two decimal places | ||
expect(screen.getByText('Total: 484954.14')).toBeInTheDocument(); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
label: Components | ||
collapsed: false | ||
position: 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
label: Display | ||
collapsed: false | ||
position: 4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
link: | ||
type: doc | ||
id: component_callout_overview |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
title: Callout | ||
id: component_callout_overview | ||
export_name: EuiCallOut | ||
slug: /components/callout | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
title: Testing EuiCallOut | ||
sidebar_label: Testing | ||
--- | ||
|
||
import QuickReference from './testing_quick_reference.svg'; | ||
|
||
## Quick reference | ||
|
||
<QuickReference /> | ||
|
||
## How to test EuiCallOut? | ||
|
||
**EuiCallOut** adds simple wrapper elements to the rendered title, icon, and children. When testing, you should | ||
focus on the inner content that's being passed to the component instead of testing the exact DOM structure. | ||
|
||
We recommend testing for the expected text to exist in the document or within a section of the document it's supposed | ||
to be rendered in (e.g., by `screen.getByText('Lorem ipsum')`). In case there might be multiple EuiCallOuts rendered, | ||
we recommend adding `data-test-subj` attributes to each of them and running queries within specific EuiCallOut | ||
elements to ensure the test is running assertions on the right one. | ||
|
||
## Testing icon type | ||
|
||
You can use the `data-icon-type` attribute of the `.euiIcon` element to check what icon type is rendered. | ||
This level of detail in tests is often unnecessary, and we recommend having really good tests | ||
for the inner data this component renders first before testing icon and callout types. | ||
|
||
## Available selectors | ||
|
||
| Selector | Description | | ||
|:---------------------------------------------|:--------------------| | ||
| `.euiCallOut` | Root element | | ||
| `.euiCallOutHeader__title` | Callout title | | ||
| `.euiIcon` | Callout header icon | | ||
| `[data-test-subj="euiDismissCalloutButton"]` | Dismiss button | |
89 changes: 89 additions & 0 deletions
89
website/docs/02_components/display/callout/testing_quick_reference.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
link: | ||
type: doc | ||
id: component_text_overview |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: component_text_overview | ||
title: Text | ||
export_name: EuiText | ||
slug: /components/text | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--- | ||
title: Testing EuiText | ||
sidebar_label: Testing | ||
--- | ||
|
||
:::warning | ||
In most cases, testing for the usage of `<EuiText>` brings very little value and **should be omitted**. | ||
Instead of testing the implementation details, focus on testing if the contents rendered inside are correct. | ||
::: | ||
|
||
## Available selectors | ||
|
||
| Selector | Description | | ||
|:-----------|:-------------| | ||
| `.euiText` | Root element | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
link: | ||
type: doc | ||
id: component_title_overview |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
id: component_title_overview | ||
title: Title | ||
export_name: EuiTitle | ||
slug: /components/title | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--- | ||
title: Testing EuiTitle | ||
sidebar_label: Testing | ||
--- | ||
|
||
:::warning | ||
In most cases, testing for the usage of `<EuiTitle>` brings very little value and **should be omitted**. | ||
Instead of testing the implementation details, focus on testing if the contents rendered inside are correct. | ||
::: | ||
|
||
## Available selectors | ||
|
||
| Selector | Description | | ||
|:------------|:-------------| | ||
| `.euiTitle` | Root element | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
label: Layout | ||
collapsed: false | ||
position: 1 | ||
position: 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
label: Navigation | ||
collapsed: false | ||
position: 2 | ||
position: 3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--- | ||
title: Testing EuiButton and EuiButtonIcon | ||
sidebar_label: Testing | ||
--- | ||
|
||
`<EuiButton>`, `<EuiButtonIcon>` and `<EuiButtonEmpty>` are all rendering native `<button>` or `<a>` elements. | ||
|
||
## Buttons as `a` elements | ||
|
||
When `<EuiButton>`, `<EuiButtonIcon>` or `<EuiButtonEmpty>` receive a valid `href` property, they will render | ||
an anchor (`<a>`) element instead of a button (`<button>`). Please keep in mind that no matter what element | ||
is used underneath, they'll look the same, so it's best to open Dev Tools and check what element to query. | ||
|
||
## Icons | ||
|
||
In order to confirm the right icon is rendered, you can follow the same steps as you'd do for `<EuiIcon>`. | ||
You should check if the `<button>` (or `<a>` - [see above](#buttons-as-a-elements)) element has a `<svg>` element inside to ensure | ||
an icon is being displayed, and compare the `data-icon-type` attribute value. | ||
It matches the type of icon being displayed on the screen. | ||
|
||
## Content (children) | ||
|
||
Button content is wrapped in multiple inner elements to ensure correct styles, however, we don't recommend querying | ||
using the inner structure of our components. Instead, you should **read the inner text** of the rendered `<button>` | ||
(or `<a>` - [see above](#buttons-as-a-elements)) and confirm it's what you expected it to be. | ||
|
||
You can do that by reading the [`innerText`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText) | ||
or [`textContent`](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) properties of the element. | ||
Read about the differences between these two | ||
[here](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent#differences_from_innertext). | ||
|
||
## Available selectors | ||
|
||
| Selector | Description | | ||
|:----------------|:-----------------------------------------------------------------------| | ||
| `button` or `a` | Root button element | | ||
| `svg` | Button icon<br />Use `data-icon-type` to verify the right icon is used | |
Oops, something went wrong.