diff options
Diffstat (limited to 'doc/development/testing_guide/frontend_testing.md')
-rw-r--r-- | doc/development/testing_guide/frontend_testing.md | 193 |
1 files changed, 36 insertions, 157 deletions
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 3af806d8f57..76687db3a3f 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Frontend testing standards and style guidelines There are two types of test suites encountered while developing frontend code -at GitLab. We use Karma with Jasmine and Jest for JavaScript unit and integration testing, +at GitLab. We use Jest for JavaScript unit and integration testing, and RSpec feature tests with Capybara for e2e (end-to-end) integration testing. Unit and feature tests need to be written for all new features. @@ -28,46 +28,9 @@ If you are looking for a guide on Vue component testing, you can jump right away We use Jest to write frontend unit and integration tests. 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 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). - -[Karma](http://karma-runner.github.io/) is a test runner which uses -[Jasmine](https://jasmine.github.io/) as its test framework. Jest also uses Jasmine as foundation, -that's why it's looking quite similar. - -Karma tests live in `spec/javascripts/` and `/ee/spec/javascripts` in EE. - -`app/assets/javascripts/behaviors/autosize.js` -might have a corresponding `spec/javascripts/behaviors/autosize_spec.js` file. - -Keep in mind that in a CI environment, these tests are run in a headless -browser and you don't have access to certain APIs, such as -[`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification), -which have to be stubbed. - -### Differences to Karma - -- Jest runs in a Node.js environment, not in a browser. [An issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/26982) for running Jest tests in a browser. -- 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/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`). -- The following cause tests to fail in Jest: - - Unmocked requests. - - Unhandled Promise rejections. - - Calls to `console.warn`, including warnings from libraries like Vue. - ### Limitations of jsdom -As mentioned [above](#differences-to-karma), Jest uses jsdom instead of a browser for running tests. +Jest uses jsdom instead of a browser for running tests. This comes with a number of limitations, namely: - [No scrolling support](https://github.com/jsdom/jsdom/blob/15.1.1/lib/jsdom/browser/Window.js#L623-L625) @@ -387,8 +350,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/timer-mocks)). In Karma you can use the -[Jasmine mock clock](https://jasmine.github.io/api/2.9/Clock.html). +(see also [Jest Timer Mocks](https://jestjs.io/docs/timer-mocks)). ```javascript const doSomethingLater = () => { @@ -409,20 +371,6 @@ it('does something', () => { }); ``` -**in Karma:** - -```javascript -it('does something', () => { - jasmine.clock().install(); - - doSomethingLater(); - jasmine.clock().tick(4000); - - expect(something).toBe('done'); - jasmine.clock().uninstall(); -}); -``` - ### Mocking the current location in Jest NOTE: @@ -476,8 +424,7 @@ it('passes', () => { Sometimes a test needs to wait for something to happen in the application before it continues. Avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout) -because it makes the reason for waiting unclear and if used within Karma with a time larger than zero it slows down our test suite. -Instead use one of the following approaches. +because it makes the reason for waiting unclear. Instead use one of the following approaches. #### Promises and Ajax calls @@ -505,19 +452,6 @@ it('waits for an Ajax call', async () => { }); ``` -**in Karma:** - -```javascript -it('waits for an Ajax call', done => { - askTheServer() - .then(() => { - expect(something).toBe('done'); - }) - .then(done) - .catch(done.fail); -}); -``` - 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,22 +482,6 @@ it('renders something', () => { }); ``` -**in Karma:** - -```javascript -it('renders something', done => { - wrapper.setProps({ value: 'new value' }); - - wrapper.vm - .$nextTick() - .then(() => { - expect(wrapper.text()).toBe('new value'); - }) - .then(done) - .catch(done.fail); -}); -``` - #### Events If the application triggers an event that you need to wait for in your test, register an event handler which contains @@ -776,8 +694,6 @@ TBU ### Stubbing and Mocking -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/jest-object#jestspyonobject-methodname) The more challenging part are mocks, which can be used for functions or even dependencies. @@ -835,7 +751,6 @@ For running the frontend tests, you need the following commands: - `rake frontend:fixtures` (re-)generates [fixtures](#frontend-test-fixtures). Make sure that fixtures are up-to-date before running tests that require them. - `yarn jest` runs Jest tests. -- `yarn karma` runs Karma tests. ### Live testing and focused testing -- Jest @@ -860,49 +775,36 @@ yarn jest ./path/to/folder/ yarn jest term ``` -### Live testing and focused testing -- Karma +## Frontend test fixtures -Karma allows something similar, but it's way more costly. +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 +often using fixtures to validate correct integration with the backend code. -Running Karma with `yarn run karma-start` compiles the JavaScript -assets and runs a server at `http://localhost:9876/` where it automatically -runs the tests on any browser which connects to it. You can enter that URL on -multiple browsers at once to have it run the tests on each in parallel. +### Use fixtures -While Karma is running, any changes you make instantly trigger a recompile -and retest of the **entire test suite**, so you can see instantly if you've broken -a test with your changes. You can use [Jasmine focused](https://jasmine.github.io/2.5/focused_specs.html) or -excluded tests (with `fdescribe` or `xdescribe`) to get Karma to run only the -tests you want while you're working on a specific feature, but make sure to -remove these directives when you commit your code. +Jest uses `spec/frontend/__helpers__/fixtures.js` to import fixtures in tests. -It is also possible to only run Karma on specific folders or files by filtering -the run tests via the argument `--filter-spec` or short `-f`: +The following are examples of tests that work for Jest: -```shell -# Run all files -yarn karma-start -# Run specific spec files -yarn karma-start --filter-spec profile/account/components/update_username_spec.js -# Run specific spec folder -yarn karma-start --filter-spec profile/account/components/ -# Run all specs which path contain vue_shared or vie -yarn karma-start -f vue_shared -f vue_mr_widget -``` +```javascript +it('makes a request', () => { + const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json + axiosMock.onGet(endpoint).reply(200, responseBody); -You can also use glob syntax to match files. Remember to put quotes around the -glob otherwise your shell may split it into multiple arguments: + myButton.click(); -```shell -# Run all specs named `file_spec` within the IDE subdirectory -yarn karma -f 'spec/javascripts/ide/**/file_spec.js' -``` + // ... +}); -## Frontend test fixtures +it('uses some HTML element', () => { + loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM -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 -often using fixtures to validate correct integration with the backend code. + const element = document.getElementById('#my-id'); + + // ... +}); +``` ### Generate fixtures @@ -961,34 +863,6 @@ This will create a new fixture located at You can import the JSON fixture in a Jest test using the `getJSONFixture` method [as described below](#use-fixtures). -### Use fixtures - -Jest and Karma test suites import fixtures in different ways: - -- The Karma test suite are served by [jasmine-jquery](https://github.com/velesin/jasmine-jquery). -- Jest use `spec/frontend/__helpers__/fixtures.js`. - -The following are examples of tests that work for both Karma and Jest: - -```javascript -it('makes a request', () => { - const responseBody = getJSONFixture('some/fixture.json'); // loads spec/frontend/fixtures/some/fixture.json - axiosMock.onGet(endpoint).reply(200, responseBody); - - myButton.click(); - - // ... -}); - -it('uses some HTML element', () => { - loadFixtures('some/page.html'); // loads spec/frontend/fixtures/some/page.html and adds it to the DOM - - const element = document.getElementById('#my-id'); - - // ... -}); -``` - ## Data-driven tests Similar to [RSpec's parameterized tests](best_practices.md#table-based--parameterized-tests), @@ -1139,13 +1013,10 @@ Main information on frontend testing levels can be found in the [Testing Levels Tests relevant for frontend development can be found at the following places: -- `spec/javascripts/`, for Karma tests - `spec/frontend/`, for Jest tests - `spec/features/`, for RSpec tests -RSpec runs complete [feature tests](testing_levels.md#frontend-feature-tests), while the Jest and Karma directories contain [frontend unit tests](testing_levels.md#frontend-unit-tests), [frontend component tests](testing_levels.md#frontend-component-tests), and [frontend integration tests](testing_levels.md#frontend-integration-tests). - -All tests in `spec/javascripts/` are intended to be migrated to `spec/frontend/` (see also [#52483](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52483)). +RSpec runs complete [feature tests](testing_levels.md#frontend-feature-tests), while the Jest directories contain [frontend unit tests](testing_levels.md#frontend-unit-tests), [frontend component tests](testing_levels.md#frontend-component-tests), and [frontend integration tests](testing_levels.md#frontend-integration-tests). Before May 2018, `features/` also contained feature tests run by Spinach. These tests were removed from the codebase in May 2018 ([#23036](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23036)). @@ -1161,6 +1032,16 @@ If you introduce new helpers, place them in that directory. We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/guide/testing.html): ```javascript +// prefer using like this, a single object argument so parameters are obvious from reading the test +await testAction({ + action: actions.actionName, + payload: { deleteListId: 1 }, + state: { lists: [1, 2, 3] }, + expectedMutations: [ { type: types.MUTATION} ], + expectedActions: [], +}); + +// old way, don't do this for new tests testAction( actions.actionName, // action { }, // params to be passed to action @@ -1177,8 +1058,6 @@ testAction( ); ``` -Check an example in [`spec/frontend/ide/stores/actions_spec.js`](https://gitlab.com/gitlab-org/gitlab/-/blob/fdc7197609dfa7caeb1d962042a26248e49f27da/spec/frontend/ide/stores/actions_spec.js#L392). - ### Wait until Axios requests finish <!-- vale gitlab.Spelling = NO --> |