From fda8760f8d352de0e9f35a20675213445e44b0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sat, 1 Jun 2019 23:56:08 +0200 Subject: [PATCH 1/5] Add improved Vue docs --- docs/vue-testing-library/api.md | 218 +++++++++++++++++++++++++++ docs/vue-testing-library/examples.md | 114 ++++++++++++++ docs/vue-testing-library/intro.md | 152 +++++-------------- docs/vue-testing-library/setup.md | 31 ++++ website/sidebars.json | 11 +- 5 files changed, 414 insertions(+), 112 deletions(-) create mode 100644 docs/vue-testing-library/api.md create mode 100644 docs/vue-testing-library/examples.md create mode 100644 docs/vue-testing-library/setup.md diff --git a/docs/vue-testing-library/api.md b/docs/vue-testing-library/api.md new file mode 100644 index 000000000..2e45d2b56 --- /dev/null +++ b/docs/vue-testing-library/api.md @@ -0,0 +1,218 @@ +--- +id: api +title: API +--- + +`Vue Testing Library` re-exports everything from `DOM Testing Library`. + +It also exposes these methods: + +- [`render(Component, options, callback)`](#rendercomponent-options-callback) + - [Parameters](#parameters) + - [Component](#component) + - [Options](#options) + - [Callback Function](#callback-function) + - [`render` result](#render-result) + - [`...queries`](#queries) + - [`container`](#container) + - [`baseElement`](#baseelement) + - [`debug()`](#debug) + - [`unmount()`](#unmount) + - [`isUnmounted()`](#isunmounted) + - [`html()`](#html) + - [`emitted()`](#emitted) + - [`updateProps(props)`](#updatepropsprops) +- [`fireEvent`](#fireevent) + - [`touch(elem, value)`](#touchelem-value) + - [`update(elem, value)`](#updateelem-value) +- [`cleanup`](#cleanup) + +--- + +## `render(Component, options, callback)` + +The `render` function is the only way of rendering components in Vue Testing +Library. + +It takes up to 3 parameters and returns an object with some helper methods. + +```js +function render(Component, options, callbackFunction) { + return { + ...DOMTestingLibraryQueries, + container, + baseElement, + debug, + unmount, + isUnmounted, + html, + emitted, + updateProps, + } +} +``` + +### Parameters + +#### Component + +The valid Vue Component to be tested. + +#### Options + +An object containing additional information to be passed to `@vue/test-utils` +[mount](https://vue-test-utils.vuejs.org/api/options.html#context). + +Additionally, the options object can also include the following three keys: + +1. `store` - The object definition of a [Vuex](https://vuex.vuejs.org/) store. +2. `routes` - A set of routes for [Vue Router](https://router.vuejs.org/). +3. `props` - It will be merged with `propsData`. + +If a `store` object is provided, `Vue Testing Library` will import and configure +a Vuex store. + +Similarly, if `routes` is provided, the library will import and configure Vue +Router. + +#### Callback Function + +```js +function callbackFunction(vueInstance, vuexStore, router) {} +``` + +A callback function that receives the Vue instance as a parameter. This allows +3rd party plugins to be installed prior to mount. + +The function will also receive the Store or the Router object if the associated +option was passed in during render. + +### `render` result + +The `render` method returns an object that has a few properties: + +#### `...queries` + +The most important feature of `render` is that the queries from +[DOM Testing Library](dom-testing-library/api-queries.md) are automatically +returned with their first argument bound to the [baseElement](#baseelement), +which defaults to `document.body`. + +See [Queries](dom-testing-library/api-queries.md) for a complete list of +available methods. + +```js +const { getByLabelText, queryAllByTestId } = render(Component) +``` + +#### `container` + +The containing DOM node of your rendered Vue Component. It's a `div`. This is a +regular DOM node, so you can call `container.querySelector` etc. to inspect the +children. + +> Tip: To get the root element of your rendered element, use +> `container.firstChild`. + +#### `baseElement` + +Returns `document.body`, the DOM node where your Vue component is rendered. + +#### `debug()` + +This method is a shortcut for `console.log(prettyDOM(baseElement))`. + +```jsx +import { render } from '@testing-library/vue' + +const HelloWorldComponent { + template: `

Hello World

` +} + +const { debug } = render(HelloWorldComponent) +debug() +//
+//

Hello World

+//
+ +// you can also pass an element: debug(getByTestId('messages')) +``` + +This is a simple wrapper around `prettyDOM` which is also exposed and comes from +[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library/blob/master/README.md#prettydom). + +#### `unmount()` + +An alias for `@vue/test-utils` +[destroy](https://vue-test-utils.vuejs.org/api/wrapper/#destroy). + +#### `isUnmounted()` + +Returns whether if a Vue component has been destroyed. + +#### `html()` + +An alias for `@vue/test-utils` +[html](https://vue-test-utils.vuejs.org/api/wrapper/#html). + +#### `emitted()` + +An alias for `@vue/test-utils` +[emitted](https://vue-test-utils.vuejs.org/api/wrapper/#emitted). + +#### `updateProps(props)` + +An alias for `@vue/test-utils` +[setProps](https://vue-test-utils.vuejs.org/api/wrapper/#setprops). + +It returns a Promise through `wait()`, so you can `await updateProps(...)`. + +--- + +## `fireEvent` + +Vue Testing Library alters the original `fireEvent` from DOM Testing Library so +that all events are async (ie: `await fireEvent.click(button)`). + +Vue Testing Library exposes two additional methods: + +### `touch(elem, value)` + +It triggers both `focus()` and `blur()` events. + +### `update(elem, value)` + +Properly handles inputs controlled by `v-model`. It updates the +input/select/textarea inner value while emitting the appropiate native event. + +See the [v-model test](/docs/vue-testing-library/examples#example-using-v-model) +in the examples page. + +--- + +## `cleanup` + +Unmounts Vue trees that were mounted with [render](#render). + +```jsx +import { cleanup, render } from '@testing-library/vue' +import Component from './Component.vue' + +afterEach(cleanup) // <-- add this + +test('renders into document', () => { + render(Component) + // ... +}) + +// ... more tests ... +``` + +Failing to call `cleanup` when you've called `render` could result in a memory +leak and tests which are not "idempotent" (which can lead to difficult to debug +errors in your tests). + +**If you don't want to add this to _every single test file_** then we recommend +that you configure your test framework to run a file before your tests which +does this automatically. See the [setup](./setup) section for guidance on how to +set up your framework. diff --git a/docs/vue-testing-library/examples.md b/docs/vue-testing-library/examples.md new file mode 100644 index 000000000..9f38616ff --- /dev/null +++ b/docs/vue-testing-library/examples.md @@ -0,0 +1,114 @@ +--- +id: examples +title: Examples +--- + +## Basic example + +```html + + + + +``` + +```js +// src/TestComponent.spec.js +import { render, fireEvent, cleanup } from '@testing-library/vue' +import TestComponent from './TestComponent.vue' + +// automatically unmount and cleanup DOM after the test is finished. +afterEach(cleanup) + +it('increments value on click', async () => { + // The render method returns a collection of utilities to query your component. + const { getByText } = render(TestComponent) + + // getByText returns the first matching node for the provided text, and + // throws an error if no elements match or if more than one match is found. + getByText('Times clicked: 0') + + const button = getByText('increment') + + // Dispatch a native click event to our button element. + await fireEvent.click(button) + await fireEvent.click(button) + + getByText('Times clicked: 2') +}) +``` + +## Example using `v-model`: + +```html + + + +``` + +```js +import { render, fireEvent, cleanup } from '@testing-library/vue' +import Component from './Component' + +afterEach(cleanup) + +test('properly handles v-model', async () => { + const { getByLabelText, getByText } = render(Component) + + // Asserts initial state. + getByText('Hi, my name is empty') + + const usernameInput = getByLabelText(/username/i) + + // Updates the value and triggers an `input` event. + // fireEvent.input() would make the test fail. + await fireEvent.update(usernameInput, 'Alice') + + getByText('Hi, my name is Alice') +}) +``` + +## More examples + +You'll find examples of testing with different libraries in +[the test directory](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__). + +Some included are: + +- [`vuex`](https://github.com/testing-library/vue-testing-library/blob/master/tests/__tests__/vuex.js) +- [`vue-router`](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/vue-router.js) +- [`vee-validate`](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/validate-plugin.js) diff --git a/docs/vue-testing-library/intro.md b/docs/vue-testing-library/intro.md index 0aa9e9e06..c4e85f401 100644 --- a/docs/vue-testing-library/intro.md +++ b/docs/vue-testing-library/intro.md @@ -1,137 +1,67 @@ --- id: intro -title: Vue Testing Library +title: Intro --- -[`Vue Testing Library`][gh] is a lightweight adapter allowing -`DOM Testing Library` to be used to test [Vue](https://vuejs.org/) components -built on top of `@vue/test-utils`. - -``` -npm install --save-dev vue @testing-library/vue -``` +Vue Testing Library builds on top of `DOM Testing Library` by adding APIs for +working with Vue components. It is built on top of +[@vue/test-utils](https://github.com/vuejs/vue-test-utils), the official testing +library for Vue. - [Vue Testing Library on GitHub][gh] -## Usage +In short, Vue Testing Library does three things: -``` -npm install --save-dev @testing-library/vue - jest - vue-jest - babel-jest - babel-preset-env - babel-plugin-transform-runtime -``` +1. Re-exports query utilities and helpers from `DOM Testing Library`. +2. Hides `@vue/test-utils` methods that are in conflict with Testing Library + [Guiding Principle](/docs/guiding-principles). +3. Tweaks some methods from both sources to match Vue requirements. + +## Quickstart -```json -// package.json -"scripts": { - "test": "jest" -} - -"jest": { - "moduleDirectories": [ - "node_modules", - "src" - ], - "moduleFileExtensions": [ - "js", - "vue" - ], - "testPathIgnorePatterns": [ - "/node_modules/" - ], - "transform": { - "^.+\\.js$": "/node_modules/babel-jest", - ".*\\.(vue)$": "/node_modules/vue-jest" - } -} -``` -``` -// .babelrc -{ - "presets": [ - ["env", { - "modules": false, - "targets": { - "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] - } - }] - ], - "plugins": [ - "transform-runtime" - ], - "env": { - "test": { - "presets": ["env"] - } - } -} -``` -```html -// src/TestComponent.vue - ``` -```js -// src/TestComponent.spec.js -import 'jest-dom/extend-expect' -import { render } from '@testing-library/vue' -import TestComponent from './TestComponent' - -test('should render HelloWorld', () => { - const { queryByTestId } = render(TestComponent) - expect(queryByTestId('test1')).toHaveTextContent('Hello World') -}) +npm install --save-dev @testing-library/vue ``` You can now use all of `DOM Testing Library`'s `getBy`, `getAllBy`, `queryBy` and `queryAllBy` commands. See [here](dom-testing-library/api-queries.md) for usage. -### render - -The `render` function takes up to 3 parameters and returns an object with some -helper methods - -1. Component - the Vue component to be tested. -2. RenderOptions - an object containing additional information to be passed to - @vue/test-utils mount. This can include: - -- store - The object definition of a Vuex store. If present, `render` will - configure a Vuex store and pass to mount. -- routes - A set of routes. If present, render will configure VueRouter and pass - to mount. +You may also be interested in installing `jest-dom` so you can use +[the custom Jest matchers](https://github.com/gnapse/jest-dom#readme). - All additional render options are passed to the vue-test-utils mount function - in its options. +## The problem -3. configurationCb - A callback to be called passing the Vue instance when - created. This allows 3rd party plugins to be installed prior to mount. +You want to write maintainable tests for your Vue components. As a part of this +goal, you want your tests to avoid including implementation details of your +components and rather focus on making your tests give you the confidence for +which they are intended. As part of this, you want your testbase to be +maintainable in the long run so refactors of your components (changes to +implementation but not functionality) don't break your tests and slow you and +your team down. -### Forwarded methods from DOM Testing Library +## This solution -Vue Testing Library forwards all exports from `DOM Testing Library` but it alters -`fireEvent` so that all events are async (ie: `await fireEvent.click(button)`) +`Vue Testing Library` is a very light-weight solution for testing Vue +components. It provides light utility functions on top of `@vue/test-utils`, in +a way that encourages better testing practices. Its primary guiding principle +is: -In particular, the `wait` utility can be very important in Vue components, -@vue/test-utils has succeeded in making the majority of updates happen -synchronously however there are occasions when wait will allow the DOM to -update. For example, see -[`here`](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/end-to-end.js). +> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.md) -## Examples +So rather than dealing with instances of rendered Vue components, your tests +will work with actual DOM nodes. -You'll find examples of testing with different libraries in -[the test directory](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__). -Some included are: +The utilities this library provides facilitate querying the DOM in the same way +the user would. Finding for elements by their label text (just like a user +would), finding links and buttons from their text (like a user would). It also +exposes a recommended way to find elements by a `data-testid` as an "escape +hatch" for elements where the text content and label do not make sense or is not +practical. -- [`vuex`](https://github.com/testing-library/vue-testing-library/blob/master/tests/__tests__/vuex.js) -- [`vue-router`](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/vue-router.js) -- [`vee-validate`](https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/validate-plugin.js) +This library encourages your applications to be more accessible and allows you +to get your tests closer to using your components the way a user will, which +allows your tests to give you more confidence that your application will work +when a real user uses it. [gh]: https://github.com/testing-library/vue-testing-library diff --git a/docs/vue-testing-library/setup.md b/docs/vue-testing-library/setup.md new file mode 100644 index 000000000..e20d0d246 --- /dev/null +++ b/docs/vue-testing-library/setup.md @@ -0,0 +1,31 @@ +--- +id: setup +title: Setup +--- + +`Vue Testing Library` does not require any configuration to be used. + +However, there are some things you can do when configuring your testing +framework to reduce some boilerplate. In these docs we'll demonstrate +configuring Jest. + +## Global Config + +Adding options to your global test config can simplify the setup and teardown of +tests in individual files. + +### Cleanup + +You can ensure [`cleanup`](./api#cleanup) is called after each test and import +additional assertions by adding it to the setup configuration in Jest. + +In Jest 24 and up, add the +[`setupFilesAfterEnv`](https://jestjs.io/docs/en/configuration.html#setupfilesafterenv-array) +option to your Jest config: + +```javascript +// jest.config.js +module.exports = { + setupFilesAfterEnv: ['@testing-library/vue/cleanup-after-each'], +} +``` diff --git a/website/sidebars.json b/website/sidebars.json index f984aeced..533282ed7 100755 --- a/website/sidebars.json +++ b/website/sidebars.json @@ -50,9 +50,18 @@ "native-testing-library/cheatsheet" ] }, + { + "type": "subcategory", + "label": "Vue Testing Library", + "ids": [ + "vue-testing-library/intro", + "vue-testing-library/examples", + "vue-testing-library/setup", + "vue-testing-library/api" + ] + }, "cypress-testing-library/intro", "svelte-testing-library/intro", - "vue-testing-library/intro", "angular-testing-library/intro", "pptr-testing-library/intro", "testcafe-testing-library/intro" From 2fccc8750df8183ae1c183d261ffde5fd5ac39c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sun, 2 Jun 2019 00:04:06 +0200 Subject: [PATCH 2/5] Update example --- docs/vue-testing-library/examples.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/vue-testing-library/examples.md b/docs/vue-testing-library/examples.md index 9f38616ff..cd78dec50 100644 --- a/docs/vue-testing-library/examples.md +++ b/docs/vue-testing-library/examples.md @@ -73,7 +73,7 @@ it('increments value on click', async () => { export default { data() { return { - user: 'empty', + user: 'Alice', } }, } @@ -90,15 +90,15 @@ test('properly handles v-model', async () => { const { getByLabelText, getByText } = render(Component) // Asserts initial state. - getByText('Hi, my name is empty') + getByText('Hi, my name is Alice') const usernameInput = getByLabelText(/username/i) // Updates the value and triggers an `input` event. // fireEvent.input() would make the test fail. - await fireEvent.update(usernameInput, 'Alice') + await fireEvent.update(usernameInput, 'Bob') - getByText('Hi, my name is Alice') + getByText('Hi, my name is Bob') }) ``` From a131893720805e569ef33557c8bbf0261dab6065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sun, 2 Jun 2019 07:29:30 +0200 Subject: [PATCH 3/5] Improve fireEvent descriptions --- docs/vue-testing-library/api.md | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/vue-testing-library/api.md b/docs/vue-testing-library/api.md index 2e45d2b56..83cff5600 100644 --- a/docs/vue-testing-library/api.md +++ b/docs/vue-testing-library/api.md @@ -23,7 +23,7 @@ It also exposes these methods: - [`emitted()`](#emitted) - [`updateProps(props)`](#updatepropsprops) - [`fireEvent`](#fireevent) - - [`touch(elem, value)`](#touchelem-value) + - [`touch(elem)`](#touchelem) - [`update(elem, value)`](#updateelem-value) - [`cleanup`](#cleanup) @@ -171,22 +171,35 @@ It returns a Promise through `wait()`, so you can `await updateProps(...)`. ## `fireEvent` -Vue Testing Library alters the original `fireEvent` from DOM Testing Library so -that all events are async (ie: `await fireEvent.click(button)`). +Vue Testing Library re-exports all DOM Testing Library +[firing events](https://deploy-preview-132--testing-library.netlify.com/docs/dom-testing-library/api-events). +However, it alters them so that all events are async. -Vue Testing Library exposes two additional methods: +```js +await fireEvent.click(getByText('Click me')) +``` -### `touch(elem, value)` +Additionally, Vue Testing Library exposes two useful methods: + +### `touch(elem)` It triggers both `focus()` and `blur()` events. +```js +await fireEvent.touch(getByLabelText('username')) + +// Same as: +await fireEvent.focus(getByLabelText('username')) +await fireEvent.blur(getByLabelText('username')) +``` + ### `update(elem, value)` Properly handles inputs controlled by `v-model`. It updates the input/select/textarea inner value while emitting the appropiate native event. -See the [v-model test](/docs/vue-testing-library/examples#example-using-v-model) -in the examples page. +See a working example of `update` in the +[v-model example test](/docs/vue-testing-library/examples#example-using-v-model). --- From ba46ade1107bf8e257375f797c2e4a791890c2eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sun, 2 Jun 2019 09:26:18 +0200 Subject: [PATCH 4/5] Simplify wording --- docs/vue-testing-library/intro.md | 44 ++++++++++++++----------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/docs/vue-testing-library/intro.md b/docs/vue-testing-library/intro.md index c4e85f401..243bd5e45 100644 --- a/docs/vue-testing-library/intro.md +++ b/docs/vue-testing-library/intro.md @@ -15,7 +15,7 @@ In short, Vue Testing Library does three things: 1. Re-exports query utilities and helpers from `DOM Testing Library`. 2. Hides `@vue/test-utils` methods that are in conflict with Testing Library [Guiding Principle](/docs/guiding-principles). -3. Tweaks some methods from both sources to match Vue requirements. +3. Tweaks some methods from both sources. ## Quickstart @@ -24,44 +24,40 @@ npm install --save-dev @testing-library/vue ``` You can now use all of `DOM Testing Library`'s `getBy`, `getAllBy`, `queryBy` -and `queryAllBy` commands. See [here](dom-testing-library/api-queries.md) for -usage. +and `queryAllBy` commands. See here the +[full llist of queries](dom-testing-library/api-queries.md). You may also be interested in installing `jest-dom` so you can use -[the custom Jest matchers](https://github.com/gnapse/jest-dom#readme). +[the custom Jest matchers](https://github.com/gnapse/jest-dom#readme) for the +DOM. ## The problem You want to write maintainable tests for your Vue components. As a part of this -goal, you want your tests to avoid including implementation details of your -components and rather focus on making your tests give you the confidence for -which they are intended. As part of this, you want your testbase to be -maintainable in the long run so refactors of your components (changes to -implementation but not functionality) don't break your tests and slow you and -your team down. +goal, **you want your tests to avoid including implementation details** of your +components. You'd rather focus on making your tests give you the confidence for +which they are intended. ## This solution `Vue Testing Library` is a very light-weight solution for testing Vue components. It provides light utility functions on top of `@vue/test-utils`, in -a way that encourages better testing practices. Its primary guiding principle -is: +a way that encourages better testing practices. + +Its primary guiding principle is: > [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.md) -So rather than dealing with instances of rendered Vue components, your tests -will work with actual DOM nodes. +So rather than dealing with instances of rendered Vue components, **your tests +will work with actual DOM nodes**. The utilities this library provides facilitate querying the DOM in the same way -the user would. Finding for elements by their label text (just like a user -would), finding links and buttons from their text (like a user would). It also -exposes a recommended way to find elements by a `data-testid` as an "escape -hatch" for elements where the text content and label do not make sense or is not -practical. - -This library encourages your applications to be more accessible and allows you -to get your tests closer to using your components the way a user will, which -allows your tests to give you more confidence that your application will work -when a real user uses it. +the user would. They allow you to find elements by their label text, finding +links and buttons from their text, and assert that your application is +**accessible**. + +It also exposes a recommended way to find elements by a `data-testid` as an +"escape hatch" for elements where the text content and label do not make sense +or is not practical. [gh]: https://github.com/testing-library/vue-testing-library From 29e895d8cc29a5ef43fc3ed5b7851d38462cd050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Fontcuberta?= Date: Sun, 2 Jun 2019 09:28:02 +0200 Subject: [PATCH 5/5] Cleanup examples --- docs/vue-testing-library/examples.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/vue-testing-library/examples.md b/docs/vue-testing-library/examples.md index cd78dec50..409ce7780 100644 --- a/docs/vue-testing-library/examples.md +++ b/docs/vue-testing-library/examples.md @@ -6,7 +6,6 @@ title: Examples ## Basic example ```html -