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:
authorRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
committerRobert Speicher <rspeicher@gmail.com>2021-01-20 22:34:23 +0300
commit6438df3a1e0fb944485cebf07976160184697d72 (patch)
tree00b09bfd170e77ae9391b1a2f5a93ef6839f2597 /doc/development/fe_guide
parent42bcd54d971da7ef2854b896a7b34f4ef8601067 (diff)
Add latest changes from gitlab-org/gitlab@13-8-stable-eev13.8.0-rc42
Diffstat (limited to 'doc/development/fe_guide')
-rw-r--r--doc/development/fe_guide/dependencies.md7
-rw-r--r--doc/development/fe_guide/editor_lite.md9
-rw-r--r--doc/development/fe_guide/event_tracking.md6
-rw-r--r--doc/development/fe_guide/graphql.md5
-rw-r--r--doc/development/fe_guide/icons.md7
-rw-r--r--doc/development/fe_guide/performance.md4
-rw-r--r--doc/development/fe_guide/style/javascript.md2
-rw-r--r--doc/development/fe_guide/style/vue.md84
-rw-r--r--doc/development/fe_guide/vue.md107
9 files changed, 154 insertions, 77 deletions
diff --git a/doc/development/fe_guide/dependencies.md b/doc/development/fe_guide/dependencies.md
index 0ec10399ae0..b036819cde1 100644
--- a/doc/development/fe_guide/dependencies.md
+++ b/doc/development/fe_guide/dependencies.md
@@ -8,12 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Package manager
-We use [Yarn](https://yarnpkg.com/) to manage frontend dependencies. There are a few exceptions:
-
-- [FontAwesome](https://fontawesome.com/), installed via the `font-awesome-rails` gem: we are working to replace it with
- [GitLab SVGs](https://gitlab-org.gitlab.io/gitlab-svgs/) icons library.
-- [ACE](https://ace.c9.io/) editor, installed via the `ace-rails-ap` gem.
-- Other dependencies found under `vendor/assets/`.
+We use [Yarn](https://yarnpkg.com/) to manage frontend dependencies. There are a few exceptions, stored in `vendor/assets/`.
## Updating dependencies
diff --git a/doc/development/fe_guide/editor_lite.md b/doc/development/fe_guide/editor_lite.md
index 465d64ff63c..47ef85d8737 100644
--- a/doc/development/fe_guide/editor_lite.md
+++ b/doc/development/fe_guide/editor_lite.md
@@ -104,7 +104,14 @@ someActionFunction() {
## Extensions
-Editor Lite has been built to provide a universal, extensible editing tool to the whole product, which would not depend on any particular group. Even though the Editor Lite's core is owned by [Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor-fe/), the main functional elements — extensions — can be owned by any group. Editor Lite extensions' main idea is that the core of the editor remains very slim and stable. At the same time, whatever new functionality is needed can be added as an extension to this core, without touching the core itself. It allows any group to build and own any new editing functionality without being afraid of it being broken or overridden with the Editor Lite changes.
+Editor Lite has been built to provide a universal, extensible editing tool to the whole product,
+which would not depend on any particular group. Even though the Editor Lite's core is owned by
+[Create::Editor FE Team](https://about.gitlab.com/handbook/engineering/development/dev/create-editor/),
+the main functional elements — extensions — can be owned by any group. Editor Lite extensions' main idea
+is that the core of the editor remains very slim and stable. At the same time, whatever new functionality
+is needed can be added as an extension to this core, without touching the core itself. It allows any group
+to build and own any new editing functionality without being afraid of it being broken or overridden with
+the Editor Lite changes.
Structurally, the complete implementation of Editor Lite could be presented as the following diagram:
diff --git a/doc/development/fe_guide/event_tracking.md b/doc/development/fe_guide/event_tracking.md
index 24e83ffc524..e8b8e0c4885 100644
--- a/doc/development/fe_guide/event_tracking.md
+++ b/doc/development/fe_guide/event_tracking.md
@@ -1,8 +1,8 @@
---
-redirect_to: '../product_analytics/index.md'
+redirect_to: 'https://about.gitlab.com/handbook/product/product-intelligence-guide/'
---
-This document was moved to [another location](../product_analytics/index.md).
+This document was moved to [another location](https://about.gitlab.com/handbook/product/product-intelligence-guide/).
-<!-- This redirect file can be deleted after February 1, 2021. -->
+<!-- This redirect file can be deleted after December 1, 2021. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index b1896863af9..cbaa648570c 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -815,7 +815,7 @@ it('calls mutation on submitting form ', () => {
### Testing with mocked Apollo Client
-To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/helpers/mock_apollo_helper.js) we created on top of it.
+To test the logic of Apollo cache updates, we might want to mock an Apollo Client in our unit tests. We use [`mock-apollo-client`](https://www.npmjs.com/package/mock-apollo-client) library to mock Apollo client and [`createMockApollo` helper](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/frontend/__helpers__/mock_apollo_helper.js) we created on top of it.
To separate tests with mocked client from 'usual' unit tests, it's recommended to create an additional factory and pass the created `mockApollo` as an option to the `createComponent`-factory. This way we only create Apollo Client instance when it's necessary.
@@ -887,7 +887,7 @@ describe('Some component with Apollo mock', () => {
After this, we need to create a mock Apollo Client instance using a helper:
```javascript
-import createMockApollo from 'jest/helpers/mock_apollo_helper';
+import createMockApollo from 'helpers/mock_apollo_helper';
describe('Some component', () => {
let wrapper;
@@ -1031,7 +1031,6 @@ the following Apollo Client warning when passing only handlers:
```shell
Unexpected call of console.warn() with:
-
Warning: mock-apollo-client - The query is entirely client-side (using @client directives) and resolvers have been configured. The request handler will not be called.
```
diff --git a/doc/development/fe_guide/icons.md b/doc/development/fe_guide/icons.md
index 1468e886220..af587a31bbb 100644
--- a/doc/development/fe_guide/icons.md
+++ b/doc/development/fe_guide/icons.md
@@ -18,8 +18,6 @@ We are using SVG Icons in GitLab with a SVG Sprite.
This means the icons are only loaded once, and are referenced through an ID.
The sprite SVG is located under `/assets/icons.svg`.
-Our goal is to replace one by one all inline SVG Icons (as those currently bloat the HTML) and also all Font Awesome icons.
-
### Usage in HAML/Rails
To use a sprite Icon in HAML or Rails we use a specific helper function:
@@ -90,11 +88,6 @@ Please use the following function inside JS to render an icon:
### Usage in HAML/Rails
-WARNING:
-Do not use the `spinner` or `icon('spinner spin')` rails helpers to insert
-loading icons. These helpers rely on the Font Awesome icon library which is
-deprecated.
-
To insert a loading spinner in HAML or Rails use the `loading_icon` helper:
```haml
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index 7825c89b7cf..aac2258f3a3 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -43,7 +43,7 @@ It takes several arguments of which the measurement’s name is the only one req
performance.measure('My component', 'my-component-start', 'my-component-end')
```
-- Duration between a mark and the moment the measurement is taken. The end mark is omitted in
+- Duration between a mark and the moment the measurement is taken. The end mark is omitted in
this case.
```javascript
@@ -197,7 +197,7 @@ app-*-end // for an end ‘mark’
app-* // for ‘measure’
```
-For example, `'webide-init-editor-start`, `mr-diffs-mark-file-tree-end`, and so on. We do it to
+For example, `'webide-init-editor-start`, `mr-diffs-mark-file-tree-end`, and so on. We do it to
help identify marks and measures coming from the different apps on the same page.
## Best Practices
diff --git a/doc/development/fe_guide/style/javascript.md b/doc/development/fe_guide/style/javascript.md
index 8e3538e891d..faf03a03101 100644
--- a/doc/development/fe_guide/style/javascript.md
+++ b/doc/development/fe_guide/style/javascript.md
@@ -7,7 +7,7 @@ disqus_identifier: 'https://docs.gitlab.com/ee/development/fe_guide/style_guide_
# JavaScript style guide
-We use [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript) and it's accompanying
+We use [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript) and its accompanying
linter to manage most of our JavaScript style guidelines.
In addition to the style guidelines set by Airbnb, we also have a few specific rules
diff --git a/doc/development/fe_guide/style/vue.md b/doc/development/fe_guide/style/vue.md
index b85c1b1de35..0288238a9e5 100644
--- a/doc/development/fe_guide/style/vue.md
+++ b/doc/development/fe_guide/style/vue.md
@@ -119,7 +119,8 @@ Please check this [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules)
## Naming
-1. **Extensions**: Use `.vue` extension for Vue components. Do not use `.js` as file extension ([#34371](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/34371)).
+1. **Extensions**: Use `.vue` extension for Vue components. Do not use `.js` as file extension
+([#34371](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/34371)).
1. **Reference Naming**: Use PascalCase for their instances:
```javascript
@@ -402,7 +403,8 @@ When using `v-for` you need to provide a *unique* `:key` attribute for each item
</div>
```
-1. When using `v-for` with `template` and there is more than one child element, the `:key` values must be unique. It's advised to use `kebab-case` namespaces.
+1. When using `v-for` with `template` and there is more than one child element, the `:key` values
+must be unique. It's advised to use `kebab-case` namespaces.
```html
<template v-for="(item, index) in items">
@@ -468,9 +470,10 @@ Useful links:
## Vue testing
-Over time, a number of programming patterns and style preferences have emerged in our efforts to effectively test Vue components.
-The following guide describes some of these. **These are not strict guidelines**, but rather a collection of suggestions and
-good practices that aim to provide insight into how we write Vue tests at GitLab.
+Over time, a number of programming patterns and style preferences have emerged in our efforts to
+effectively test Vue components. The following guide describes some of these.
+**These are not strict guidelines**, but rather a collection of suggestions and good practices that
+aim to provide insight into how we write Vue tests at GitLab.
### Mounting a component
@@ -479,8 +482,10 @@ Typically, when testing a Vue component, the component should be "re-mounted" in
To achieve this:
1. Create a mutable `wrapper` variable inside the top-level `describe` block.
-1. Mount the component using [`mount`](https://vue-test-utils.vuejs.org/api/#mount)/[`shallowMount`](https://vue-test-utils.vuejs.org/api/#shallowMount).
-1. Reassign the resulting [`Wrapper`](https://vue-test-utils.vuejs.org/api/wrapper/#wrapper) instance to our `wrapper` variable.
+1. Mount the component using [`mount`](https://vue-test-utils.vuejs.org/api/#mount)/
+[`shallowMount`](https://vue-test-utils.vuejs.org/api/#shallowMount).
+1. Reassign the resulting [`Wrapper`](https://vue-test-utils.vuejs.org/api/wrapper/#wrapper)
+instance to our `wrapper` variable.
Creating a global, mutable wrapper provides a number of advantages, including the ability to:
@@ -497,14 +502,16 @@ Creating a global, mutable wrapper provides a number of advantages, including th
})
```
-- Use a `beforeEach` block to mount the component (see [the `createComponent` factory](#the-createcomponent-factory) for more information).
+- Use a `beforeEach` block to mount the component (see
+[the `createComponent` factory](#the-createcomponent-factory) for more information).
- Use an `afterEach` block to destroy the component, for example, `wrapper.destroy()`.
#### The `createComponent` factory
To avoid duplicating our mounting logic, it's useful to define a `createComponent` factory function
that we can reuse in each test block. This is a closure which should reassign our `wrapper` variable
-to the result of [`mount`](https://vue-test-utils.vuejs.org/api/#mount) and [`shallowMount`](https://vue-test-utils.vuejs.org/api/#shallowMount):
+to the result of [`mount`](https://vue-test-utils.vuejs.org/api/#mount) and
+[`shallowMount`](https://vue-test-utils.vuejs.org/api/#shallowMount):
```javascript
import MyComponent from '~/path/to/my_component.vue';
@@ -568,7 +575,8 @@ describe('MyComponent', () => {
1. Consider using a single (or a limited number of) object arguments over many arguments.
Defining single parameters for common data like `props` is okay,
- but keep in mind our [JavaScript style guide](javascript.md#limit-number-of-parameters) and stay within the parameter number limit:
+ but keep in mind our [JavaScript style guide](javascript.md#limit-number-of-parameters) and
+ stay within the parameter number limit:
```javascript
// bad
@@ -591,6 +599,19 @@ the mounting function (`mount` or `shallowMount`) to be used to mount the compon
function createComponent({ mountFn = shallowMount } = {}) { }
```
+1. Wrap calls to `mount` and `shallowMount` in `extendedWrapper`, this exposes `wrapper.findByTestId()`:
+
+ ```javascript
+ import { shallowMount } from '@vue/test-utils';
+ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+ import { SomeComponent } from 'components/some_component.vue';
+
+ let wrapper;
+
+ const createWrapper = () => { wrapper = extendedWrapper(shallowMount(SomeComponent)); };
+ const someButton = () => wrapper.findByTestId('someButtonTestId');
+ ```
+
### Setting component state
1. Avoid using [`setProps`](https://vue-test-utils.vuejs.org/api/wrapper/#setprops) to set
@@ -609,12 +630,13 @@ component state wherever possible. Instead, set the component's
```
The exception here is when you wish to test component reactivity in some way.
- For example, you may want to test the output of a component when after a particular watcher has executed.
- Using `setProps` to test such behavior is okay.
+ For example, you may want to test the output of a component when after a particular watcher has
+ executed. Using `setProps` to test such behavior is okay.
### Accessing component state
-1. When accessing props or attributes, prefer the `wrapper.props('myProp')` syntax over `wrapper.props().myProp`:
+1. When accessing props or attributes, prefer the `wrapper.props('myProp')` syntax over
+`wrapper.props().myProp` or `wrapper.vm.myProp`:
```javascript
// good
@@ -626,7 +648,8 @@ component state wherever possible. Instead, set the component's
expect(wrapper.attributes('myAttr')).toBe(true);
```
-1. When asserting multiple props, check the deep equality of the `props()` object with [`toEqual`](https://jestjs.io/docs/en/expect#toequalvalue):
+1. When asserting multiple props, check the deep equality of the `props()` object with
+[`toEqual`](https://jestjs.io/docs/en/expect#toequalvalue):
```javascript
// good
@@ -642,8 +665,9 @@ component state wherever possible. Instead, set the component's
});
```
-1. If you are only interested in some of the props, you can use [`toMatchObject`](https://jestjs.io/docs/en/expect#tomatchobjectobject).
-Prefer `toMatchObject` over [`expect.objectContaining`](https://jestjs.io/docs/en/expect#expectobjectcontainingobject):
+1. If you are only interested in some of the props, you can use
+[`toMatchObject`](https://jestjs.io/docs/en/expect#tomatchobjectobject). Prefer `toMatchObject`
+over [`expect.objectContaining`](https://jestjs.io/docs/en/expect#expectobjectcontainingobject):
```javascript
// good
@@ -664,12 +688,24 @@ Prefer `toMatchObject` over [`expect.objectContaining`](https://jestjs.io/docs/e
The goal of this accord is to make sure we are all on the same page.
1. When writing Vue, you may not use jQuery in your application.
- 1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your application to grab data attributes using `dataset`. You can do this without jQuery.
+ 1. If you need to grab data from the DOM, you may query the DOM 1 time while bootstrapping your
+ application to grab data attributes using `dataset`. You can do this without jQuery.
1. You may use a jQuery dependency in Vue.js following [this example from the docs](https://vuejs.org/v2/examples/select2.html).
- 1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use jQuery event listeners.
- 1. We avoid adding new jQuery events when they are not required. Instead of adding new jQuery events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
-1. You may query the `window` object one time, while bootstrapping your application for application specific data (e.g. `scrollTo` is ok to access anytime). Do this access during the bootstrapping of your application.
-1. You may have a temporary but immediate need to create technical debt by writing code that does not follow our standards, to be refactored later. Maintainers need to be ok with the tech debt in the first place. An issue should be created for that tech debt to evaluate it further and discuss. In the coming months you should fix that tech debt, with its priority to be determined by maintainers.
-1. When creating tech debt you must write the tests for that code before hand and those tests may not be rewritten. e.g. jQuery tests rewritten to Vue tests.
-1. You may choose to use VueX as a centralized state management. If you choose not to use VueX, you must use the *store pattern* which can be found in the [Vue.js documentation](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch).
-1. Once you have chosen a centralized state-management solution you must use it for your entire application. i.e. Don't mix and match your state-management solutions.
+ 1. If an outside jQuery Event needs to be listen to inside the Vue application, you may use
+ jQuery event listeners.
+ 1. We avoid adding new jQuery events when they are not required. Instead of adding new jQuery
+ events take a look at [different methods to do the same task](https://vuejs.org/v2/api/#vm-emit).
+1. You may query the `window` object one time, while bootstrapping your application for application
+specific data (for example, `scrollTo` is ok to access anytime). Do this access during the
+bootstrapping of your application.
+1. You may have a temporary but immediate need to create technical debt by writing code that does
+not follow our standards, to be refactored later. Maintainers need to be ok with the tech debt in
+the first place. An issue should be created for that tech debt to evaluate it further and discuss.
+In the coming months you should fix that tech debt, with its priority to be determined by maintainers.
+1. When creating tech debt you must write the tests for that code before hand and those tests may
+not be rewritten. For example, jQuery tests rewritten to Vue tests.
+1. You may choose to use VueX as a centralized state management. If you choose not to use VueX, you
+must use the *store pattern* which can be found in the
+[Vue.js documentation](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch).
+1. Once you have chosen a centralized state-management solution you must use it for your entire
+application. Don't mix and match your state-management solutions.
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 41fbd128631..b3fbb9556a9 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -22,7 +22,8 @@ All new features built with Vue.js must follow a [Flux architecture](https://fac
The main goal we are trying to achieve is to have only one data flow and only one data entry.
In order to achieve this goal we use [vuex](#vuex).
-You can also read about this architecture in Vue docs about [state management](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch)
+You can also read about this architecture in Vue docs about
+[state management](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch)
and about [one way data flow](https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow).
### Components and Store
@@ -62,14 +63,15 @@ Be sure to read about [page-specific JavaScript](performance.md#page-specific-ja
While mounting a Vue application, you might need to provide data from Rails to JavaScript.
To do that, you can use the `data` attributes in the HTML element and query them while mounting the application.
-You should only do this while initializing the application, because the mounted element is replaced with a Vue-generated DOM.
+You should only do this while initializing the application, because the mounted element is replaced
+with a Vue-generated DOM.
-The advantage of providing data from the DOM to the Vue instance through `props` in the `render` function
-instead of querying the DOM inside the main Vue component is avoiding the need to create a fixture or an HTML element in the unit test,
-which makes the tests easier.
+The advantage of providing data from the DOM to the Vue instance through `props` in the `render`
+function instead of querying the DOM inside the main Vue component is avoiding the need to create a
+fixture or an HTML element in the unit test, which makes the tests easier.
-See the following example, also, please refer to our [Vue style guide](style/vue.md#basic-rules) for additional
-information on why we explicitly declare the data being passed into the Vue app;
+See the following example, also, please refer to our [Vue style guide](style/vue.md#basic-rules) for
+additional information on why we explicitly declare the data being passed into the Vue app;
```javascript
// haml
@@ -94,13 +96,15 @@ return new Vue({
});
```
-> When adding an `id` attribute to mount a Vue application, please make sure this `id` is unique across the codebase
+> When adding an `id` attribute to mount a Vue application, please make sure this `id` is unique
+across the codebase.
#### Accessing the `gl` object
-When we need to query the `gl` object for data that doesn't change during the application's life cycle, we should do it in the same place where we query the DOM.
-By following this practice, we can avoid the need to mock the `gl` object, which makes tests easier.
-It should be done while initializing our Vue instance, and the data should be provided as `props` to the main component:
+When we need to query the `gl` object for data that doesn't change during the application's life
+cycle, we should do it in the same place where we query the DOM. By following this practice, we can
+avoid the need to mock the `gl` object, which makes tests easier. It should be done while
+initializing our Vue instance, and the data should be provided as `props` to the main component:
```javascript
return new Vue({
@@ -192,13 +196,18 @@ Check this [page](vuex.md) for more details.
In the [Vue documentation](https://vuejs.org/v2/api/#Options-Data) the Data function/object is defined as follows:
-> The data object for the Vue instance. Vue recursively converts its properties into getter/setters to make it “reactive”. The object must be plain: native objects such as browser API objects and prototype properties are ignored. A rule of thumb is that data should just be data - it is not recommended to observe objects with their own stateful behavior.
+> The data object for the Vue instance. Vue recursively converts its properties into getter/setters
+to make it “reactive”. The object must be plain: native objects such as browser API objects and
+prototype properties are ignored. A rule of thumb is that data should just be data - it is not
+recommended to observe objects with their own stateful behavior.
Based on the Vue guidance:
-- **Do not** use or create a JavaScript class in your [data function](https://vuejs.org/v2/api/#data), such as `user: new User()`.
+- **Do not** use or create a JavaScript class in your [data function](https://vuejs.org/v2/api/#data),
+such as `user: new User()`.
- **Do not** add new JavaScript class implementations.
-- **Do** use [GraphQL](../api_graphql_styleguide.md), [Vuex](vuex.md) or a set of components if cannot use simple primitives or objects.
+- **Do** use [GraphQL](../api_graphql_styleguide.md), [Vuex](vuex.md) or a set of components if
+cannot use simple primitives or objects.
- **Do** maintain existing implementations using such approaches.
- **Do** Migrate components to a pure object model when there are substantial changes to it.
- **Do** add business logic to helpers or utils, so you can test them separately from your component.
@@ -209,7 +218,8 @@ There are additional reasons why having a JavaScript class presents maintainabil
- Once a class is created, it is easy to extend it in a way that can infringe Vue reactivity and best practices.
- A class adds a layer of abstraction, which makes the component API and its inner workings less clear.
-- It makes it harder to test. Since the class is instantiated by the component data function, it is harder to 'manage' component and class separately.
+- It makes it harder to test. Since the class is instantiated by the component data function, it is
+harder to 'manage' component and class separately.
- Adding OOP to a functional codebase adds yet another way of writing code, reducing consistency and clarity.
## Style guide
@@ -231,6 +241,7 @@ Here's an example of a well structured unit test for [this Vue component](#appen
```javascript
import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { GlLoadingIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
@@ -263,19 +274,21 @@ describe('~/todos/app.vue', () => {
});
// It is very helpful to separate setting up the component from
- // its collaborators (i.e. Vuex, axios, etc.)
+ // its collaborators (for example, Vuex and axios).
const createWrapper = (props = {}) => {
- wrapper = shallowMount(App, {
- propsData: {
- path: TEST_TODO_PATH,
- ...props,
- },
- });
+ wrapper = extendedWrapper(
+ shallowMount(App, {
+ propsData: {
+ path: TEST_TODO_PATH,
+ ...props,
+ },
+ })
+ );
};
// Helper methods greatly help test maintainability and readability.
const findLoader = () => wrapper.find(GlLoadingIcon);
- const findAddButton = () => wrapper.find('[data-testid="add-button"]');
- const findTextInput = () => wrapper.find('[data-testid="text-input"]');
+ const findAddButton = () => wrapper.findByTestId('add-button');
+ const findTextInput = () => wrapper.findByTestId('text-input');
const findTodoData = () => wrapper.findAll('[data-testid="todo-item"]').wrappers.map(wrapper => ({ text: wrapper.text() }));
describe('when mounted and loading', () => {
@@ -323,11 +336,41 @@ describe('~/todos/app.vue', () => {
The main return value of a Vue component is the rendered output. In order to test the component we
need to test the rendered output. Visit the [Vue testing guide](https://vuejs.org/v2/guide/testing.html#Unit-Testing).
+### Child components
+
+1. Test any directive that defines if/how child component is rendered (for example, `v-if` and `v-for`).
+1. Test any props we are passing to child components (especially if the prop is calculated in the
+component under test, with the `computed` property, for example). Remember to use `.props()` and not `.vm.someProp`.
+1. Test we react correctly to any events emitted from child components:
+
+ ```javascript
+ const checkbox = wrapper.findByTestId('checkboxTestId');
+
+ expect(checkbox.attributes('disabled')).not.toBeDefined();
+
+ findChildComponent().vm.$emit('primary');
+ await nextTick();
+
+ expect(checkbox.attributes('disabled')).toBeDefined();
+ ```
+
+1. **Do not** test the internal implementation of the child components:
+
+ ```javascript
+ // bad
+ expect(findChildComponent().find('.error-alert').exists()).toBe(false);
+
+ // good
+ expect(findChildComponent().props('withAlertContainer')).toBe(false);
+ ```
+
### Events
-We should test for events emitted in response to an action within our component, this is useful to verify the correct events are being fired with the correct arguments.
+We should test for events emitted in response to an action within our component, this is useful to
+verify the correct events are being fired with the correct arguments.
-For any DOM events we should use [`trigger`](https://vue-test-utils.vuejs.org/api/wrapper/#trigger) to fire out event.
+For any DOM events we should use [`trigger`](https://vue-test-utils.vuejs.org/api/wrapper/#trigger)
+to fire out event.
```javascript
// Assuming SomeButton renders: <button>Some button</button>
@@ -342,7 +385,8 @@ it('should fire the click event', () => {
})
```
-When we need to fire a Vue event, we should use [`emit`](https://vuejs.org/v2/guide/components-custom-events.html) to fire our event.
+When we need to fire a Vue event, we should use [`emit`](https://vuejs.org/v2/guide/components-custom-events.html)
+to fire our event.
```javascript
wrapper = shallowMount(DropdownItem);
@@ -355,7 +399,8 @@ it('should fire the itemClicked event', () => {
})
```
-We should verify an event has been fired by asserting against the result of the [`emitted()`](https://vue-test-utils.vuejs.org/api/wrapper/#emitted) method
+We should verify an event has been fired by asserting against the result of the
+[`emitted()`](https://vue-test-utils.vuejs.org/api/wrapper/#emitted) method.
## Vue.js Expert Role
@@ -371,7 +416,8 @@ You should only apply to be a Vue.js expert when your own merge requests and you
> This section is added temporarily to support the efforts to migrate the codebase from Vue 2.x to Vue 3.x
-Currently, we recommend to minimize adding certain features to the codebase to prevent increasing the tech debt for the eventual migration:
+Currently, we recommend to minimize adding certain features to the codebase to prevent increasing
+the tech debt for the eventual migration:
- filters;
- event buses;
@@ -382,7 +428,8 @@ You can find more details on [Migration to Vue 3](vue3_migration.md)
## Appendix - Vue component subject under test
-This is the template for the example component which is tested in the [Testing Vue components](#testing-vue-components) section:
+This is the template for the example component which is tested in the
+[Testing Vue components](#testing-vue-components) section:
```html
<template>