Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/testing_guide/frontend_testing.md')
-rw-r--r--doc/development/testing_guide/frontend_testing.md104
1 files changed, 66 insertions, 38 deletions
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 9facca10142..7289e66a045 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -55,8 +55,8 @@ which have to be stubbed.
- Because Jest runs in a Node.js environment, it uses [jsdom](https://github.com/jsdom/jsdom) by default. See also its [limitations](#limitations-of-jsdom) below.
- Jest does not have access to Webpack loaders or aliases.
The aliases used by Jest are defined in its [own configuration](https://gitlab.com/gitlab-org/gitlab/blob/master/jest.config.js).
-- All calls to `setTimeout` and `setInterval` are mocked away. See also [Jest Timer Mocks](https://jestjs.io/docs/en/timer-mocks).
-- `rewire` is not required because Jest supports mocking modules. See also [Manual Mocks](https://jestjs.io/docs/en/manual-mocks).
+- All calls to `setTimeout` and `setInterval` are mocked away. See also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks).
+- `rewire` is not required because Jest supports mocking modules. See also [Manual Mocks](https://jestjs.io/docs/manual-mocks).
- No [context object](https://jasmine.github.io/tutorials/your_first_suite#section-The_%3Ccode%3Ethis%3C/code%3E_keyword) is passed to tests in Jest.
This means sharing `this.something` between `beforeEach()` and `it()` for example does not work.
Instead you should declare shared variables in the context that they are needed (via `const` / `let`).
@@ -78,7 +78,7 @@ See also the issue for [support running Jest tests in browsers](https://gitlab.c
### Debugging Jest tests
-Running `yarn jest-debug` runs Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/en/troubleshooting#tests-are-failing-and-you-don-t-know-why).
+Running `yarn jest-debug` runs Jest in debug mode, allowing you to debug/inspect as described in the [Jest docs](https://jestjs.io/docs/troubleshooting#tests-are-failing-and-you-don-t-know-why).
### Timeout error
@@ -104,6 +104,15 @@ describe('Component', () => {
Remember that the performance of each test depends on the environment.
+### Test-specific stylesheets
+
+To help facilitate RSpec integration tests we have two test-specific stylesheets. These can be used to do things like disable animations to improve test speed, or to make elements visible when they need to be targeted by Capybara click events:
+
+- `app/assets/stylesheets/disable_animations.scss`
+- `app/assets/stylesheets/test_environment.scss`
+
+Because the test environment should match the production environment as much as possible, use these minimally and only add to them when necessary.
+
## What and how to test
Before jumping into more gritty details about Jest-specific workflows like mocks and spies, we should briefly cover what to test with Jest.
@@ -212,8 +221,8 @@ When it comes to querying DOM elements in your tests, it is best to uniquely and
the element.
Preferentially, this is done by targeting what the user actually sees using [DOM Testing Library](https://testing-library.com/docs/dom-testing-library/intro/).
-When selecting by text it is best to use [`getByRole` or `findByRole`](https://testing-library.com/docs/queries/byrole/)
-as these enforce accessibility best practices as well. The examples below demonstrate the order of preference.
+When selecting by text it is best to use the [`byRole`](https://testing-library.com/docs/queries/byrole) query
+as it helps enforce accessibility best practices. `findByRole` and the other [DOM Testing Library queries](https://testing-library.com/docs/queries/about) are available when using [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended).
When writing Vue component unit tests, it can be wise to query children by component, so that the unit test can focus on comprehensive value coverage
rather than dealing with the complexity of a child component's behavior.
@@ -223,25 +232,27 @@ possible selectors include:
- A semantic attribute like `name` (also verifies that `name` was setup properly)
- A `data-testid` attribute ([recommended by maintainers of `@vue/test-utils`](https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-610133465))
- optionally combined with [`findByTestId`](#extendedwrapper-and-findbytestid)
+ optionally combined with [`shallowMountExtended` or `mountExtended`](#shallowmountextended-and-mountextended)
- a Vue `ref` (if using `@vue/test-utils`)
```javascript
-import { getByRole, getByText } from '@testing-library/dom'
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper'
+
+const wrapper = shallowMountExtended(ExampleComponent);
// In this example, `wrapper` is a `@vue/test-utils` wrapper returned from `mount` or `shallowMount`.
it('exists', () => {
// Best (especially for integration tests)
- getByRole(wrapper.element, 'link', { name: /Click Me/i })
- getByRole(wrapper.element, 'link', { name: 'Click Me' })
- getByText(wrapper.element, 'Click Me')
- getByText(wrapper.element, /Click Me/i)
+ wrapper.findByRole('link', { name: /Click Me/i })
+ wrapper.findByRole('link', { name: 'Click Me' })
+ wrapper.findByText('Click Me')
+ wrapper.findByText(/Click Me/i)
// Good (especially for unit tests)
wrapper.find(FooComponent);
wrapper.find('input[name=foo]');
wrapper.find('[data-testid="my-foo-id"]');
- wrapper.findByTestId('my-foo-id'); // with the extendedWrapper utility – check below
+ wrapper.findByTestId('my-foo-id'); // with shallowMountExtended or mountExtended – check below
wrapper.find({ ref: 'foo'});
// Bad
@@ -376,7 +387,7 @@ Sometimes we have to test time-sensitive code. For example, recurring events tha
If the application itself is waiting for some time, mock await the waiting. In Jest this is already
[done by default](https://gitlab.com/gitlab-org/gitlab/blob/a2128edfee799e49a8732bfa235e2c5e14949c68/jest.config.js#L47)
-(see also [Jest Timer Mocks](https://jestjs.io/docs/en/timer-mocks)). In Karma you can use the
+(see also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks)). In Karma you can use the
[Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html).
```javascript
@@ -564,9 +575,9 @@ This is however not entirely true as the `destroy` method does not remove everyt
#### Prefer `toBe` over `toEqual` when comparing primitive values
-Jest has [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) and
-[`toEqual`](https://jestjs.io/docs/en/expect#toequalvalue) matchers.
-As [`toBe`](https://jestjs.io/docs/en/expect#tobevalue) uses
+Jest has [`toBe`](https://jestjs.io/docs/expect#tobevalue) and
+[`toEqual`](https://jestjs.io/docs/expect#toequalvalue) matchers.
+As [`toBe`](https://jestjs.io/docs/expect#tobevalue) uses
[`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
to compare values, it's faster (by default) than using `toEqual`.
While the latter eventually falls back to leverage [`Object.is`](https://github.com/facebook/jest/blob/master/packages/expect/src/jasmineUtils.ts#L91),
@@ -588,7 +599,7 @@ expect(foo).toBe(1);
Jest provides useful matchers like `toHaveLength` or `toBeUndefined` to make your tests more
readable and to produce more understandable error messages. Check their docs for the
-[full list of matchers](https://jestjs.io/docs/en/expect#methods).
+[full list of matchers](https://jestjs.io/docs/expect#methods).
Examples:
@@ -719,7 +730,7 @@ TBU
Jasmine provides stubbing and mocking capabilities. There are some subtle differences in how to use it within Karma and Jest.
Stubs or spies are often used synonymously. In Jest it's quite easy thanks to the `.spyOn` method.
-[Official docs](https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname)
+[Official docs](https://jestjs.io/docs/jest-object#jestspyonobject-methodname)
The more challenging part are mocks, which can be used for functions or even dependencies.
### Manual module mocks
@@ -728,12 +739,12 @@ Manual mocks are used to mock modules across the entire Jest environment. This i
unit testing by mocking out modules which cannot be easily consumed in our test environment.
> **WARNING:** Do not use manual mocks if a mock should not be consistently applied in every spec (i.e. it's only needed by a few specs).
-> Instead, consider using [`jest.mock(..)`](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options)
+> Instead, consider using [`jest.mock(..)`](https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options)
> (or a similar mocking function) in the relevant spec file.
#### Where should I put manual mocks?
-Jest supports [manual module mocks](https://jestjs.io/docs/en/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module
+Jest supports [manual module mocks](https://jestjs.io/docs/manual-mocks) by placing a mock in a `__mocks__/` directory next to the source module
(e.g. `app/assets/javascripts/ide/__mocks__`). **Don't do this.** We want to keep all of our test-related code in one place (the `spec/` folder).
If a manual mock is needed for a `node_modules` package, use the `spec/frontend/__mocks__` folder. Here's an example of
@@ -755,7 +766,7 @@ If a manual mock is needed for a CE module, place it in `spec/frontend/mocks/ce`
any behavior, only provides a nice es6 compatible wrapper.
- [`__mocks__/monaco-editor/index.js`](https://gitlab.com/gitlab-org/gitlab/blob/b7f914cddec9fc5971238cdf12766e79fa1629d7/spec/frontend/__mocks__/monaco-editor/index.js) -
This mock is helpful because the Monaco package is completely incompatible in a Jest environment. In fact, webpack requires a special loader to make it work. This mock
- simply makes this package consumable by Jest.
+ makes this package consumable by Jest.
### Keep mocks light
@@ -766,7 +777,7 @@ Global mocks introduce magic and technically can reduce test coverage. When mock
### Additional mocking techniques
-Consult the [official Jest docs](https://jestjs.io/docs/en/jest-object#mock-modules) for a full overview of the available mocking features.
+Consult the [official Jest docs](https://jestjs.io/docs/jest-object#mock-modules) for a full overview of the available mocking features.
## Running Frontend Tests
@@ -939,8 +950,8 @@ it('uses some HTML element', () => {
Similar to [RSpec's parameterized tests](best_practices.md#table-based--parameterized-tests),
Jest supports data-driven tests for:
-- Individual tests using [`test.each`](https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout) (aliased to `it.each`).
-- Groups of tests using [`describe.each`](https://jestjs.io/docs/en/api#describeeachtable-name-fn-timeout).
+- Individual tests using [`test.each`](https://jestjs.io/docs/api#testeachtable-name-fn-timeout) (aliased to `it.each`).
+- Groups of tests using [`describe.each`](https://jestjs.io/docs/api#describeeachtable-name-fn-timeout).
These can be useful for reducing repetition within tests. Each option can take an array of
data values or a tagged template literal.
@@ -1138,23 +1149,40 @@ These are very useful if you don't have a handle to the request's Promise, for e
Both functions run `callback` on the next tick after the requests finish (using `setImmediate()`), to allow any `.then()` or `.catch()` handlers to run.
-### `extendedWrapper` and `findByTestId`
+### `shallowMountExtended` and `mountExtended`
-Using `data-testid` is one of the [recommended ways to query DOM elements](#how-to-query-dom-elements).
-You can use the `extendedWrapper` utility on the `wrapper` returned by `shalowMount`/`mount`.
-By doing so, the `wrapper` provides you with the ability to perform a `findByTestId`,
-which is a shortcut to the more verbose `wrapper.find('[data-testid="my-test-id"]');`
+The `shallowMountExtended` and `mountExtended` utilities provide you with the ability to perform
+any of the available [DOM Testing Library queries](https://testing-library.com/docs/queries/about)
+by prefixing them with `find` or `findAll`.
```javascript
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('FooComponent', () => {
- const wrapper = extendedWrapper(shallowMount({
- template: `<div data-testid="my-test-id"></div>`,
- }));
+ const wrapper = shallowMountExtended({
+ template: `
+ <div data-testid="gitlab-frontend-stack">
+ <p>GitLab frontend stack</p>
+ <div role="tablist">
+ <button role="tab" aria-selected="true">Vue.js</button>
+ <button role="tab" aria-selected="false">GraphQL</button>
+ <button role="tab" aria-selected="false">SCSS</button>
+ </div>
+ </div>
+ `,
+ });
+
+ it('finds elements with `findByTestId`', () => {
+ expect(wrapper.findByTestId('gitlab-frontend-stack').exists()).toBe(true);
+ });
+
+ it('finds elements with `findByText`', () => {
+ expect(wrapper.findByText('GitLab frontend stack').exists()).toBe(true);
+ expect(wrapper.findByText('TypeScript').exists()).toBe(false);
+ });
- it('exists', () => {
- expect(wrapper.findByTestId('my-test-id').exists()).toBe(true);
+ it('finds elements with `findAllByRole`', () => {
+ expect(wrapper.findAllByRole('tab').length).toBe(3);
});
});
```
@@ -1189,7 +1217,7 @@ You can download any older version of Firefox from the releases FTP server, <htt
## Snapshots
-By now you've probably heard of [Jest snapshot tests](https://jestjs.io/docs/en/snapshot-testing) and why they are useful for various reasons.
+By now you've probably heard of [Jest snapshot tests](https://jestjs.io/docs/snapshot-testing) and why they are useful for various reasons.
To use them within GitLab, there are a few guidelines that should be highlighted:
- Treat snapshots as code
@@ -1199,7 +1227,7 @@ To use them within GitLab, there are a few guidelines that should be highlighted
Think of a snapshot test as a simple way to store a raw `String` representation of what you've put into the item being tested. This can be used to evaluate changes in a component, a store, a complex piece of generated output, etc. You can see more in the list below for some recommended `Do's and Don'ts`.
While snapshot tests can be a very powerful tool. They are meant to supplement, not to replace unit tests.
-Jest provides a great set of docs on [best practices](https://jestjs.io/docs/en/snapshot-testing#best-practices) that we should keep in mind when creating snapshots.
+Jest provides a great set of docs on [best practices](https://jestjs.io/docs/snapshot-testing#best-practices) that we should keep in mind when creating snapshots.
### How does a snapshot work?
@@ -1207,7 +1235,7 @@ A snapshot is purely a stringified version of what you ask to be tested on the l
Should the outcome of your spec be different from what is in the generated snapshot file, you'll be notified about it by a failing test in your test suite.
-Find all the details in Jests official documentation [https://jestjs.io/docs/en/snapshot-testing](https://jestjs.io/docs/en/snapshot-testing)
+Find all the details in Jests official documentation [https://jestjs.io/docs/snapshot-testing](https://jestjs.io/docs/snapshot-testing)
### How to take a snapshot