summaryrefslogtreecommitdiff
path: root/doc/development/testing_guide/frontend_testing.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/testing_guide/frontend_testing.md')
-rw-r--r--doc/development/testing_guide/frontend_testing.md70
1 files changed, 53 insertions, 17 deletions
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 94bc80abcdb..73fce3a38d7 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -31,7 +31,7 @@ Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE.
## Karma test suite
While GitLab has switched over to [Jest](https://jestjs.io), Karma tests still exist in our
-application because some of our specs require a browser and can't be easiliy migrated to Jest.
+application because some of our specs require a browser and can't be easily migrated to Jest.
Those specs intend to eventually drop Karma in favor of either Jest or RSpec. You can track this migration
in the [related epic](https://gitlab.com/groups/gitlab-org/-/epics/4900).
@@ -258,7 +258,7 @@ it('exists', () => {
### Naming unit tests
When writing describe test blocks to test specific functions/methods,
-please use the method name as the describe block name.
+use the method name as the describe block name.
**Bad**:
@@ -439,7 +439,7 @@ it('waits for an Ajax call', done => {
});
```
-If you are not able to register handlers to the `Promise`, for example because it is executed in a synchronous Vue life cycle hook, please take a look at the [waitFor](#wait-until-axios-requests-finish) helpers or you can flush all pending `Promise`s:
+If you are not able to register handlers to the `Promise`, for example because it is executed in a synchronous Vue life cycle hook, take a look at the [waitFor](#wait-until-axios-requests-finish) helpers or you can flush all pending `Promise`s:
**in Jest:**
@@ -548,8 +548,12 @@ In order to ensure that a clean wrapper object and DOM are being used in each te
});
```
+<!-- vale gitlab.Spelling = NO -->
+
See also the [Vue Test Utils documentation on `destroy`](https://vue-test-utils.vuejs.org/api/wrapper/#destroy).
+<!-- vale gitlab.Spelling = YES -->
+
### Jest best practices
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34209) in GitLab 13.2.
@@ -646,20 +650,46 @@ The latter is useful when you have `setInterval` in the code. **Remember:** our
Non-determinism is the breeding ground for flaky and brittle specs. Such specs end up breaking the CI pipeline, interrupting the work flow of other contributors.
-1. Make sure your test subject's collaborators (e.g., axios, apollo, lodash helpers) and test environment (e.g., Date) behave consistently across systems and over time.
+1. Make sure your test subject's collaborators (e.g., Axios, apollo, Lodash helpers) and test environment (e.g., Date) behave consistently across systems and over time.
1. Make sure tests are focused and not doing "extra work" (e.g., needlessly creating the test subject more than once in an individual test)
### Faking `Date` for determinism
-Consider using `useFakeDate` to ensure a consistent value is returned with every `new Date()` or `Date.now()`.
+`Date` is faked by default in our Jest environment. This means every call to `Date()` or `Date.now()` returns a fixed deterministic value.
+
+If you really need to change the default fake date, you can call `useFakeDate` within any `describe` block, and
+the date will be replaced for that specs within that `describe` context only:
```javascript
import { useFakeDate } from 'helpers/fake_date';
describe('cool/component', () => {
- useFakeDate();
+ // Default fake `Date`
+ const TODAY = new Date();
- // ...
+ // NOTE: `useFakeDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.).
+ describe("on Ada Lovelace's Birthday", () => {
+ useFakeDate(1815, 11, 10)
+
+ it('Date is no longer default', () => {
+ expect(new Date()).not.toEqual(TODAY);
+ });
+ });
+
+ it('Date is still default in this scope', () => {
+ expect(new Date()).toEqual(TODAY)
+ });
+})
+```
+
+Similarly, if you really need to use the real `Date` class, then you can import and call `useRealDate` within any `describe` block:
+
+```javascript
+import { useRealDate } from 'helpers/fake_date';
+
+// NOTE: `useRealDate` cannot be called during test execution (i.e. inside `it`, `beforeEach`, `beforeAll`, etc.).
+describe('with real date', () => {
+ useRealDate();
});
```
@@ -702,10 +732,10 @@ unit testing by mocking out modules which cannot be easily consumed in our test
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
(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, please use the `spec/frontend/__mocks__` folder. Here's an example of
+If a manual mock is needed for a `node_modules` package, use the `spec/frontend/__mocks__` folder. Here's an example of
a [Jest mock for the package `monaco-editor`](https://gitlab.com/gitlab-org/gitlab/blob/b7f914cddec9fc5971238cdf12766e79fa1629d7/spec/frontend/__mocks__/monaco-editor/index.js#L1).
-If a manual mock is needed for a CE module, please place it in `spec/frontend/mocks/ce`.
+If a manual mock is needed for a CE module, place it in `spec/frontend/mocks/ce`.
- Files in `spec/frontend/mocks/ce` mocks the corresponding CE module from `app/assets/javascripts`, mirroring the source module's path.
- Example: `spec/frontend/mocks/ce/lib/utils/axios_utils` mocks the module `~/lib/utils/axios_utils`.
@@ -728,11 +758,11 @@ If a manual mock is needed for a CE module, please place it in `spec/frontend/mo
Global mocks introduce magic and technically can reduce test coverage. When mocking is deemed profitable:
- Keep the mock short and focused.
-- Please leave a top-level comment in the mock on why it is necessary.
+- Leave a top-level comment in the mock on why it is necessary.
### Additional mocking techniques
-Please 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/en/jest-object#mock-modules) for a full overview of the available mocking features.
## Running Frontend Tests
@@ -807,7 +837,7 @@ yarn karma -f 'spec/javascripts/ide/**/file_spec.js'
## Frontend test fixtures
Frontend fixtures are files containing responses from backend controllers. These responses can be either HTML
-generated from haml templates or JSON payloads. Frontend tests that rely on these responses are
+generated from HAML templates or JSON payloads. Frontend tests that rely on these responses are
often using fixtures to validate correct integration with the backend code.
### Generate fixtures
@@ -865,7 +895,7 @@ end
This will create a new fixture located at
`tmp/tests/frontend/fixtures-ee/graphql/releases/queries/all_releases.query.graphql.json`.
-Note that you will need to provide the paths to all fragments used by the query.
+You will need to provide the paths to all fragments used by the query.
`get_graphql_query_as_string` reads all of the provided file paths and returns
the result as a single, concatenated string.
@@ -929,7 +959,8 @@ it.each([
);
```
-**Note**: only use template literal block if pretty print is **not** needed for spec output. For example, empty strings, nested objects etc.
+NOTE:
+Only use template literal block if pretty print is not needed for spec output. For example, empty strings, nested objects etc.
For example, when testing the difference between an empty search string and a non-empty search string, the use of the array block syntax with the pretty print option would be preferred. That way the differences between an empty string e.g. `''` and a non-empty string e.g. `'search string'` would be visible in the spec output. Whereas with a template literal block, the empty string would be shown as a space, which could lead to a confusing developer experience
@@ -1038,9 +1069,10 @@ import Subject from '~/feature/the_subject.vue';
import _Thing from '~/feature/path/to/thing.vue';
```
-**PLEASE NOTE:** Do not simply disregard test timeouts. This could be a sign that there's
+NOTE:
+Do not disregard test timeouts. This could be a sign that there's
actually a production problem. Use this opportunity to analyze the production webpack bundles and
-chunks and confirm that there is not a production issue with the async imports.
+chunks and confirm that there is not a production issue with the asynchronous imports.
## Overview of Frontend Testing Levels
@@ -1063,7 +1095,7 @@ See also [Notes on testing Vue components](../fe_guide/vue.md#testing-vue-compon
## Test helpers
Test helpers can be found in [`spec/frontend/__helpers__`](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/frontend/__helpers__).
-If you introduce new helpers, please place them in that directory.
+If you introduce new helpers, place them in that directory.
### Vuex Helper: `testAction`
@@ -1090,9 +1122,13 @@ Check an example in [`spec/frontend/ide/stores/actions_spec.js`](https://gitlab.
### Wait until Axios requests finish
+<!-- vale gitlab.Spelling = NO -->
+
The Axios Utils mock module located in `spec/frontend/mocks/ce/lib/utils/axios_utils.js` contains two helper methods for Jest tests that spawn HTTP requests.
These are very useful if you don't have a handle to the request's Promise, for example when a Vue component does a request as part of its life cycle.
+<!-- vale gitlab.Spelling = YES -->
+
- `waitFor(url, callback)`: Runs `callback` after a request to `url` finishes (either successfully or unsuccessfully).
- `waitForAll(callback)`: Runs `callback` once all pending requests have finished. If no requests are pending, runs `callback` on the next tick.