├── src
├── guide
│ ├── transitions.md
│ ├── reusability-composition.md
│ ├── community-learning.md
│ ├── component-instance.md
│ ├── third-party.md
│ ├── installation.md
│ ├── introduction.md
│ ├── plugins.md
│ ├── slots.md
│ ├── passing-data.md
│ ├── async-suspense.md
│ ├── event-handling.md
│ ├── migration.md
│ ├── conditional-rendering.md
│ ├── vuex.md
│ ├── http-requests.md
│ ├── stubs-shallow-mount.md
│ ├── a-crash-course.md
│ ├── vue-router.md
│ └── forms.md
├── .vuepress
│ ├── public
│ │ └── logo.png
│ └── config.js
├── README.md
└── api
│ └── README.md
├── .prettierrc
├── README.md
├── .gitignore
└── package.json
/src/guide/transitions.md:
--------------------------------------------------------------------------------
1 | # Transitions
2 |
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true
4 | }
--------------------------------------------------------------------------------
/src/.vuepress/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vuejs/test-utils-docs/HEAD/src/.vuepress/public/logo.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DEPRECATED:
2 |
3 | Docs are ported to VTU-next main repo: https://github.com/vuejs/vue-test-utils-next/
4 |
5 |
--------------------------------------------------------------------------------
/src/guide/reusability-composition.md:
--------------------------------------------------------------------------------
1 | # Reusability & Composition
2 |
3 | Mostly:
4 |
5 | - `global.provide`.
6 | - `global.mixins`.
7 | - `global.directives`.
--------------------------------------------------------------------------------
/src/guide/community-learning.md:
--------------------------------------------------------------------------------
1 | # Community and Learning
2 |
3 | Links to a future plugin repository
4 |
5 | Links to vue testing lib
6 |
7 | Links to other resources?
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
3 | .vuepress/dist
4 |
5 | .yarn-integrity
6 |
7 | logs
8 | *.log
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | .idea
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | home: true
3 | heroImage: /logo.png
4 | heroText: Vue Test Utils Next
5 | tagline: The official testing suite utils for Vue.js 3
6 | actionText: Get Started →
7 | actionLink: /guide/introduction
8 |
--------------------------------------------------------------------------------
/src/guide/component-instance.md:
--------------------------------------------------------------------------------
1 | # Component Instance
2 |
3 | Mostly `findComponent()`, `.props()` et al.
4 |
5 | Also why `.vm` is not available and yet another recommendation to test outputs instead of implementation details.
--------------------------------------------------------------------------------
/src/guide/third-party.md:
--------------------------------------------------------------------------------
1 | # Third-party Integration
2 |
3 | Overall usage of mocking tools such as `jest.mock()`. (Not only *how*, but mostly *why* and *when*).
4 |
5 | Also…
6 |
7 | * Vuetify
8 | * BootstrapVue
9 |
10 | (each of them could be a subpage?)
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "node-sass": "^4.13.1",
4 | "sass-loader": "^8.0.2",
5 | "vuepress": "^1.3.0"
6 | },
7 | "scripts": {
8 | "serve": "vuepress dev src",
9 | "build:dev": "vuepress build src",
10 | "build": "./build.sh"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/guide/installation.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | - npm: `npm install --save-dev @vue/test-utils@next`
4 | - yarn: `yarn add --dev @vue/test-utils@next`
5 |
6 | ### Support for `.vue` files
7 |
8 | To load `.vue` files with Jest, you will need `vue-jest`. `vue-jest` v5 is the one that supports Vue 3. It is still in alpha, much like the rest of the Vue.js 3 ecosystem, so if you find a bug please report it [here](https://github.com/vuejs/vue-jest/) and specify you are using `vue-jest` v5.
9 |
10 | You can install it with `vue-jest@next`. Then you need to configure it with Jest's [transform](https://jestjs.io/docs/en/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object) option.
11 |
12 | ## Usage
13 |
14 | Vue Test Utils is framework agnostic - you can use it with whichever test runner you like. The easiest way to try it out is using [Jest](https://jestjs.io/), a popular test runner.
15 |
16 | If you don't want to configure it yourself, you can get a minimal repository with everything set up [here](https://github.com/lmiller1990/vtu-next-demo).
--------------------------------------------------------------------------------
/src/guide/introduction.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 |
3 | Welcome to Vue Test Utils, the official testing utility library for Vue.js!
4 |
5 |
6 | This is the documentation for Vue Test Utils v2, which targets Vue 3.
7 |
8 | In short:
9 | * [Vue Test Utils 1](https://github.com/vuejs/vue-test-utils/) targets [Vue 2](https://github.com/vuejs/vue/).
10 | * [Vue Test Utils 2](https://github.com/vuejs/vue-test-utils-next/) targets [Vue 3](https://github.com/vuejs/vue-next/).
11 |
12 | ## What is Vue Test Utils?
13 |
14 | Vue Test Utils (VTU) is a set of utility functions aimed to simplify testing Vue.js components. It provides some methods to mount and interact with Vue components in an isolated manner.
15 |
16 | Let's see an example:
17 |
18 | ```js
19 | import { mount } from '@vue/test-utils'
20 |
21 | // The component to test
22 | const MessageComponent = {
23 | template: '
{{ msg }}
',
24 | props: ['msg'],
25 | }
26 |
27 | test('displays message', () => {
28 | const wrapper = mount(MessageComponent, {
29 | props: {
30 | msg: 'Hello world'
31 | }
32 | })
33 |
34 | // Assert the rendered text of the component
35 | expect(wrapper.text()).toContain('Hello world')
36 | })
37 | ```
38 |
39 | ## What Next?
40 |
41 | To see Vue Test Utils in action, [take the Crash Course](./a-crash-course/), where we build a simple Todo app using a test-first approach.
42 |
43 | Docs are split into two main sections:
44 |
45 | * **Essentials**, to cover common uses cases you'll face when testing Vue components.
46 | * **Vue Test Utils in Depth**, to explore other advanced features of the library.
47 |
48 | You can also explore the full [API](../api/).
49 |
50 | Alternatively, if you prefer to learn via video, there is [a number of lectures available here](https://www.youtube.com/playlist?list=PLC2LZCNWKL9ahK1IoODqYxKu5aA9T5IOA).
51 |
52 |
--------------------------------------------------------------------------------
/src/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | const sidebar = {
2 | guide: [
3 | {
4 | title: 'Essentials',
5 | collapsable: false,
6 | children: [
7 | '/guide/installation',
8 | '/guide/introduction',
9 | '/guide/a-crash-course',
10 | '/guide/conditional-rendering',
11 | '/guide/event-handling',
12 | '/guide/passing-data',
13 | '/guide/forms'
14 | ]
15 | },
16 | {
17 | title: 'Vue Test Utils in depth',
18 | collapsable: false,
19 | children: [
20 | '/guide/slots',
21 | '/guide/async-suspense',
22 | '/guide/http-requests',
23 | '/guide/transitions',
24 | '/guide/component-instance',
25 | '/guide/reusability-composition',
26 | '/guide/vuex',
27 | '/guide/vue-router',
28 | '/guide/third-party',
29 | '/guide/stubs-shallow-mount'
30 | ]
31 | },
32 | {
33 | title: 'Extending Vue Test Utils',
34 | collapsable: false,
35 | children: ['/guide/plugins', '/guide/community-learning']
36 | },
37 | {
38 | title: 'Migration to Vue Test Utils 2',
39 | collapsable: false,
40 | children: ['/guide/migration']
41 | },
42 | {
43 | title: 'API Reference',
44 | collapsable: false,
45 | children: ['/api/']
46 | }
47 | ],
48 | api: [
49 | {
50 | title: 'API Reference',
51 | collapsable: false,
52 | children: ['/api/']
53 | }
54 | ]
55 | }
56 |
57 | module.exports = {
58 | base: '/v2/',
59 | configureWebpack(config) {
60 | return {
61 | output: {
62 | publicPath: '/v2/'
63 | }
64 | }
65 | },
66 | title: 'Vue Test Utils',
67 | locales: {
68 | '/': {
69 | lang: 'en-US',
70 | title: 'Vue Test Utils (2.0.0-beta.14)'
71 | }
72 | },
73 | themeConfig: {
74 | editLinks: true,
75 | sidebarDepth: 2,
76 | sidebar: {
77 | '/guide/': sidebar.guide,
78 | '/api/': sidebar.api
79 | },
80 | nav: [
81 | { text: 'Guide', link: '/guide/introduction' },
82 | { text: 'API Reference', link: '/api/' },
83 | { text: 'Migration from VTU 1', link: '/guide/migration' },
84 | { text: 'GitHub', link: 'https://github.com/vuejs/vue-test-utils-next' }
85 | ]
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/guide/plugins.md:
--------------------------------------------------------------------------------
1 | ## Plugins
2 |
3 | Plugins add global-level functionality to Vue Test Utils' API. This is the
4 | official way to extend Vue Test Utils' API with custom logic, methods, or
5 | functionality.
6 |
7 | If you're missing a bit of functionality, consider writing a plugin to
8 | extend Vue Test Utils' API.
9 |
10 | Some use cases for plugins:
11 | 1. Aliasing existing public methods
12 | 1. Attaching matchers to the Wrapper instance
13 | 1. Attaching functionality to the Wrapper
14 |
15 | ## Using a Plugin
16 |
17 | Install plugins by calling the `config.plugins.VueWrapper.install()` method
18 | . This has to be done before you call `mount`.
19 |
20 | The `install()` method will receive an instance of `WrapperAPI` containing both
21 | public and private properties of the instance.
22 |
23 | ```js
24 | // setup.js file
25 | import { config } from '@vue/test-utils'
26 |
27 | // locally defined plugin, see "Writing a Plugin"
28 | import MyPlugin from './myPlugin'
29 |
30 | // Install a plugin onto VueWrapper
31 | config.plugins.VueWrapper.install(MyPlugin)
32 | ```
33 |
34 | You can optionally pass in some options:
35 | ```js
36 | config.plugins.VueWrapper.install(MyPlugin, { someOption: true })
37 | ```
38 |
39 | Your plugin should be installed once. If you are using Jest, this should be in your Jest config's `setupFiles` or `setupFilesAfterEnv` file.
40 |
41 | Some plugins automatically call `config.plugins.VueWrapper.install()` when
42 | they're imported. This is common if they're extending multiple interfaces at
43 | once. Follow the instructions of the plugin you're installing.
44 |
45 | Check out the [Vue Community Guide](https://vue-community.org/v2/guide/ecosystem/testing.html) or [awesome-vue](https://github.com/vuejs/awesome-vue#test
46 | ) for a collection of community-contributed plugins and libraries.
47 |
48 | ## Writing a Plugin
49 |
50 | A Vue Test Utils plugin is simply a function that receives the mounted
51 | `VueWrapper` or `DOMWrapper` instance and can modify it.
52 |
53 | ### Basic Plugin
54 |
55 | Below is a simple plugin to add a convenient alias to map `wrapper.element` to `wrapper.$el`
56 |
57 | ```js
58 | // setup.js
59 | import { config } from '@vue/test-utils'
60 |
61 | const myAliasPlugin = (wrapper) => {
62 | return {
63 | $el: wrapper.element // simple aliases
64 | }
65 | }
66 |
67 | // Call install on the type you want to extend
68 | // You can write a plugin for any value inside of config.plugins
69 | config.plugins.VueWrapper.install(myAliasPlugin)
70 | ```
71 |
72 | And in your spec, you'll be able to use your plugin after `mount`.
73 | ```js
74 | // component.spec.js
75 | const wrapper = mount({ template: `
🔌 Plugin
` })
76 | console.log(wrapper.$el.innerHTML) // 🔌 Plugin
77 | ```
78 |
79 | ### Data Test ID Plugin
80 |
81 | The below plugin adds a method `findByTestId` to the `VueWrapper` instance. This encourages using a selector strategy relying on test-only attributes on your Vue Components.
82 |
83 | Usage:
84 |
85 | `MyComponent.vue`:
86 |
87 | ```vue
88 |
89 |
90 |
91 |
92 |
93 | ```
94 |
95 | `MyComponent.spec.js`:
96 |
97 | ```js
98 | const wrapper = mount(MyComponent)
99 | wrapper.findByTestId('name-input') // returns a VueWrapper or DOMWrapper
100 | ```
101 |
102 | Implementation of the plugin:
103 |
104 | ```js
105 | import { config } from '@vue/test-utils-next'
106 |
107 | const DataTestIdPlugin = (wrapper) => {
108 | function findByTestId(selector) {
109 | const dataSelector = `[data-testid='${selector}']`
110 | const element = wrapper.element.querySelector(dataSelector)
111 | if (element) {
112 | return new DOMWrapper(element)
113 | }
114 |
115 | return createWrapperError('DOMWrapper')
116 | }
117 |
118 | return {
119 | findByTestId
120 | }
121 | }
122 |
123 | config.plugins.VueWrapper.install(DataTestIdPlugin)
124 | ```
125 |
126 | ## Featuring Your Plugin
127 |
128 | If you're missing functionality, consider writing a plugin to extend Vue Test
129 | Utils and submit it to be featured at [Vue Community Guide](https://vue-community.org/v2/guide/ecosystem/testing.html) or [awesome-vue](https://github.com/vuejs/awesome-vue#test).
130 |
--------------------------------------------------------------------------------
/src/guide/slots.md:
--------------------------------------------------------------------------------
1 | # Slots
2 |
3 | Vue Test Utils provides some useful features for testing components using `slots`.
4 |
5 | ## A Simple Example
6 |
7 | You might have a generic `` component that uses a default slot to render some content. For example:
8 |
9 | ```js
10 | const Layout = {
11 | template: `
12 |
13 |
Welcome!
14 |
15 |
16 |
17 |
20 |
21 | `
22 | }
23 | ```
24 |
25 | You might want to write a test to ensure the default slot content is rendered. VTU provides the `slots` mounting option for this purpose:
26 |
27 | ```js
28 | test('layout default slot', () => {
29 | const wrapper = mount(Layout, {
30 | slots: {
31 | default: 'Main Content'
32 | }
33 | })
34 |
35 | expect(wrapper.html()).toContain('Main Content')
36 | })
37 | ```
38 |
39 | It passes! In this example, we are passing some text content to the default slot. If you want to be even more specific, and verify the default slot content is rendered inside ``, you could change the assertion:
40 |
41 | ```js
42 | test('layout default slot', () => {
43 | const wrapper = mount(Layout, {
44 | slots: {
45 | default: 'Main Content'
46 | }
47 | })
48 |
49 | expect(wrapper.find('main').text()).toContain('Main Content')
50 | })
51 | ```
52 |
53 | ## Named Slots
54 |
55 | You may have more complex `` component with some named slots. For example:
56 |
57 | ```js
58 | const Layout = {
59 | template: `
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
71 |
72 | `
73 | }
74 | ```
75 |
76 | VTU also supports this. You can write a test as follows. Note that in this example we are passing HTML instead of text content to the slots.
77 |
78 | ```js
79 | test('layout full page layout', () => {
80 | const wrapper = mount(Layout, {
81 | slots: {
82 | header: '
')
114 | })
115 | ```
116 |
117 | Note: passing a component using `{ template: ' }` is not supported. Use a HTML string, render function, plain text, or an SFC.
118 |
119 | ## Scoped Slots
120 |
121 | [Scoped slots](https://v3.vuejs.org/v2/guide/component-slots.html#scoped-slots) and bindings are also supported.
122 |
123 | ```js
124 | const ComponentWithSlots = {
125 | template: `
126 |
127 |
128 |
129 | `,
130 | data() {
131 | return {
132 | msg: 'world'
133 | }
134 | }
135 | }
136 |
137 | test('scoped slots', () => {
138 | const wrapper = mount(ComponentWithSlots, {
139 | slots: {
140 | scoped: `
141 | Hello {{ params.msg }}
142 |
143 | `
144 | }
145 | })
146 |
147 | expect(wrapper.html()).toContain('Hello world')
148 | })
149 | ```
150 |
151 | ## Conclusion
152 |
153 | - Use the `slots` mounting option to test components using `` are rendering content correctly.
154 | - Content can either be a string, a render function or an imported SFC.
155 | - Use `default` for the default slot, and the correct name for a named slots.
156 | - scoped slots and the `#` shorthand is also supported.
157 |
--------------------------------------------------------------------------------
/src/guide/passing-data.md:
--------------------------------------------------------------------------------
1 | # Passing Data to Components
2 |
3 | Vue Test Utils provides several ways to set data and props on a component, to allow you to fully test the component's behavior in different scenarios.
4 |
5 | In this section, we explore the `data` and `props` mounting options, as well as `VueWrapper.setProps()` to dynamically update the props a component receives.
6 |
7 | ## The Password Component
8 |
9 | We will demonstrate the above features by building a `` component. This component verifies a password means certain criteria, such as length and complexity. We will start with the following and add features, as well as tests to make sure the features are working correctly:
10 |
11 | ```js
12 | const Password = {
13 | template: `
14 |
15 |
16 |
17 | `,
18 | data() {
19 | return {
20 | password: ''
21 | }
22 | }
23 | }
24 | ```
25 |
26 | The first requirement we will add is a minimum length.
27 |
28 | ## Using `props` to set a minimum length
29 |
30 | We want to reuse this component in all our projects, each of which may have different requirements. For this reason, we will make the `minLength` a **prop** which we pass to ``:
31 |
32 | We will show an error is `password` is less than `minLength`. We can do this by creating an `error` computed property, and conditionally rendering it using `v-if`:
33 |
34 | ```js
35 | const Password = {
36 | template: `
37 |
38 |
39 |
{{ error }}
40 |
41 | `,
42 | props: {
43 | minLength: {
44 | type: Number
45 | }
46 | },
47 | computed: {
48 | error() {
49 | if (this.password.length < this.minLength) {
50 | return `Password must be at least ${this.minLength} characters.`
51 | }
52 | return
53 | }
54 | }
55 | }
56 | ```
57 |
58 | To test this, we need to set the `minLength`, as well as a `password` that is less than that number. We can do this using the `data` and `props` mounting options. Finally, we will assert the correct error message is rendered:
59 |
60 | ```js
61 | test('renders an error if length is too short', () => {
62 | const wrapper = mount(Password, {
63 | props: {
64 | minLength: 10
65 | },
66 | data() {
67 | return {
68 | password: 'short'
69 | }
70 | }
71 | })
72 |
73 | expect(wrapper.html()).toContain('Password must be at least 10 characters')
74 | })
75 | ```
76 |
77 | Writing a test for a `maxLength` rule is left as an exercise for the reader! Another way to write this would be using `setValue` to update the input with a password that is too short. You can learn more in [Forms]./forms).
78 |
79 | ## Using `setProps`
80 |
81 | Sometimes you may need to write a test for a side effect of a prop changing. This simple `` component renders a greeting if the `show` prop is `true`.
82 |
83 | ```vue
84 |
85 |
{{ greeting }}
86 |
87 |
88 |
105 | ```
106 |
107 | To test this fully, we might want to verify that `greeting` is rendered by default. We are able to update the `show` prop using `setProps()`, which causes `greeting` to be hidden:
108 |
109 | ```js
110 | import { mount } from '@vue/test-utils'
111 | import Show from './Show.vue'
112 |
113 | test('renders a greeting when show is true', async () => {
114 | const wrapper = mount(Show)
115 | expect(wrapper.html()).toContain('Hello')
116 |
117 | await wrapper.setProps({ show: false })
118 |
119 | expect(wrapper.html()).not.toContain('Hello')
120 | })
121 | ```
122 |
123 | We also use the `await` keyword when calling `setProps()`, to ensure that the DOM has been updated before the assertions run.
124 |
125 | ## Conclusion
126 |
127 | - use the `props` and `data` mounting options to pre-set the state of a component.
128 | - Use `setProps()` to update a prop during a test.
129 | - Use the `await` keyword before `setProps()` to ensure the Vue will update the DOM before the test continues.
130 | - Directly interacting with your component can give you greater coverage. Consider using `setValue` or `trigger` in combination with `data` to ensure everything works correctly.
131 |
--------------------------------------------------------------------------------
/src/guide/async-suspense.md:
--------------------------------------------------------------------------------
1 | # Asynchronous Behavior
2 |
3 | You may have noticed some other parts of the guide using `await` when calling some methods on `wrapper`, such as `trigger` and `setValue`. What's that all about?
4 |
5 | You might know Vue updates reactively; when you change a value, the DOM is automatically updated to reflect the latest value. Vue does this *asynchronously*. In contrast, a test runner like Jest runs *synchronously*. This can cause some surprising results in tests. Let's look at some strategies to ensure Vue is updating the DOM as expected when we run our tests.
6 |
7 | ## A Simple Example - Updating with `trigger`
8 |
9 | Let's re-use the `` component from [event handling]./event-handling) with one change; we now render the `count` in the `template`.
10 |
11 | ```js
12 | const Counter = {
13 | template: `
14 | Count: {{ count }}
15 |
16 | `,
17 | data() {
18 | return {
19 | count: 0
20 | }
21 | },
22 | methods: {
23 | handleClick() {
24 | this.count += 1
25 | }
26 | }
27 | }
28 | ```
29 |
30 | Let's write a test to verify the `count` is increasing:
31 |
32 | ```js
33 | test('increments by 1', () => {
34 | const wrapper = mount(Counter)
35 |
36 | wrapper.find('button').trigger('click')
37 |
38 | expect(wrapper.html()).toContain('Count: 1')
39 | })
40 | ```
41 |
42 | Surprisingly, this fails! The reason is although `count` is increased, Vue will not update the DOM until the next "tick" or "render cycle". For this reason, the assertion will be called before Vue updates the DOM. This has to do with the concept of "macrotasks", "microtasks" and the JavaScript Event Loop. You can read more details and see a simple example [here](https://javascript.info/event-loop#macrotasks-and-microtasks).
43 |
44 | Implementation details aside, how can we fix this? Vue actually provides a way for us to wait until the DOM is updated: `nextTick`:
45 |
46 | ```js {7}
47 | import { nextTick } from 'vue'
48 |
49 | test('increments by 1', async () => {
50 | const wrapper = mount(Counter)
51 |
52 | wrapper.find('button').trigger('click')
53 | await nextTick()
54 |
55 | expect(wrapper.html()).toContain('Count: 1')
56 | })
57 | ```
58 |
59 | Now the test will pass, because we ensure the next "tick" has executed updated the DOM before the assertion runs. Since `await nextTick()` is common, VTU provides a shortcut. Methods than cause the DOM to update, such as `trigger` and `setValue` return `nextTick`! So you can just `await` those directly:
60 |
61 | ```js {4}
62 | test('increments by 1', async () => {
63 | const wrapper = mount(Counter)
64 |
65 | await wrapper.find('button').trigger('click')
66 |
67 | expect(wrapper.html()).toContain('Count: 1')
68 | })
69 | ```
70 |
71 | ## Resolving Other Asynchronous Behavior
72 |
73 | `nextTick` is useful to ensure some change in reactivty data is reflected in the DOM before continuing the test. However, sometimes you may want to ensure other, non Vue-related asynchronous behavior is completed, too. A common example is a function that returns a `Promise` that will lead to a change in the DOM. Perhaps you mocked your `axios` HTTP client using `jest.mock`:
74 |
75 | ```js
76 | jest.mock('axios', () => ({
77 | get: () => Promise.resolve({ data: 'some mocked data!' })
78 | }))
79 | ```
80 |
81 | In this case, Vue has no knowledge of the unresolved Promise, so calling `nextTick` will not work - your assertion may run before it is resolved. For scenarios like this, you can use `[flush-promises](https://www.npmjs.com/package/flush-promises)`, which causes all outstanding promises to resolve immediately.
82 |
83 | Let's see an example:
84 |
85 | ```js
86 | import flushPromises from 'flush-promises'
87 | import axios from 'axios'
88 |
89 | jest.mock('axios', () => ({
90 | get: () => new Promise(resolve => {
91 | resolve({ data: 'some mocked data!' })
92 | })
93 | }))
94 |
95 |
96 | test('uses a mocked axios HTTP client and flush-promises', async () => {
97 | // some component that makes a HTTP called in `created` using `axios`
98 | const wrapper = mount(AxiosComponent)
99 |
100 | await flushPromises() // axios promise is resolved immediately!
101 |
102 | // assertions!
103 | })
104 |
105 | ```
106 |
107 | > If you haven't tested Components with API requests before, you can learn more in [HTTP Requests]./http-requests).
108 | ## Conclusion
109 |
110 | - Vue updates the DOM asynchronously; tests runner execute code synchronously.
111 | - Use `await nextTick()` to ensure the DOM has updated before the test continues
112 | - Functions that might update the DOM, like `trigger` and `setValue` return `nextTick`, so you should `await` them.
113 | - Use `flush-promises` to resolve any unresolved promises from non-Vue dependencies.
114 |
--------------------------------------------------------------------------------
/src/guide/event-handling.md:
--------------------------------------------------------------------------------
1 | # Event Handling
2 |
3 | Vue components interact with each other via props and by emitting events by calling `$emit`. In this guide, we look at how to verify events are correctly emitted using the `emitted()` function.
4 |
5 | This article is also available as a [short video](https://www.youtube.com/watch?v=U_j-nDur4oU&list=PLC2LZCNWKL9ahK1IoODqYxKu5aA9T5IOA&index=14).
6 |
7 | ## The Counter component
8 |
9 | Here is a simple `` component. It features a button that, when clicked, increments an internal count variable and emits its value:
10 |
11 | ```js
12 | const Counter = {
13 | template: '',
14 | data() {
15 | return {
16 | count: 0
17 | }
18 | },
19 | methods: {
20 | handleClick() {
21 | this.count += 1
22 | this.$emit('increment', this.count)
23 | }
24 | }
25 | }
26 | ```
27 |
28 | To fully test this component, we should verify that an `increment` event with the latest `count` value is emitted.
29 |
30 | ## Asserting the emitted events
31 |
32 | To do so, we will rely on the `emitted()` method. It **returns an object with all the events the component has emitted**, and their arguments in an array. Let's see how it works:
33 |
34 | ```js
35 | test('emits an event when clicked', () => {
36 | const wrapper = mount(Counter)
37 |
38 | wrapper.find('button').trigger('click')
39 | wrapper.find('button').trigger('click')
40 |
41 | expect(wrapper.emitted()).toHaveProperty('increment')
42 | })
43 | ```
44 |
45 | > If you haven't seen `trigger()` before, don't worry. It's used to simulate user interaction. You can learn more in [Forms](./forms).
46 |
47 | The first thing to notice is that `emitted()` returns an object, where each key matches an emitted event. In this case, `increment`.
48 |
49 | This test should pass. We made sure we emitted an event with the appropriate name.
50 |
51 | ## Asserting the arguments of the event
52 |
53 | This is good - but we can do better! We need to check that we emit the right arguments when `this.$emit('increment', this.count)` is called.
54 |
55 | Our next step is to assert that the event contains the `count` value. We do so by passing an argument to `emitted()`.
56 |
57 | ```js {9}
58 | test('emits an event with count when clicked', () => {
59 | const wrapper = mount(Counter)
60 |
61 | wrapper.find('button').trigger('click')
62 | wrapper.find('button').trigger('click')
63 |
64 | // `emitted()` accepts an argument. It returns an array with all the
65 | // occurrences of `this.$emit('increment')`.
66 | const incrementEvent = wrapper.emitted('increment')
67 |
68 | // We have "clicked" twice, so the array of `increment` should
69 | // have two values.
70 | expect(incrementEvent).toHaveLength(2)
71 |
72 | // Assert the result of the first click.
73 | // Notice that the value is an array.
74 | expect(incrementEvent[0]).toEqual([1])
75 |
76 | // Then, the result of the second one.
77 | expect(incrementEvent[1]).toEqual([2])
78 | })
79 | ```
80 |
81 | Let's recap and break down the output of `emitted()`. Each of these keys contains the different values emitted during the test:
82 |
83 | ```js
84 | // console.log(wrapper.emitted('increment'))
85 | [
86 | [ 1 ], // first time it is called, `count` is 1
87 | [ 2 ], // second time it is called, `count` is 2
88 | ]
89 | ```
90 |
91 | ## Asserting complex events
92 |
93 | Imagine that now our `` component needs to emit an object with additional information. For instance, we need to tell any parent component listening to the `@increment` event if `count` is even or odd:
94 |
95 | ```js {12-15}
96 | const Counter = {
97 | template: ``,
98 | data() {
99 | return {
100 | count: 0
101 | }
102 | },
103 | methods: {
104 | handleClick() {
105 | this.count += 1
106 |
107 | this.$emit('increment', {
108 | count: this.count,
109 | isEven: this.count % 2 === 0
110 | })
111 | }
112 | }
113 | }
114 | ```
115 |
116 | As we did before, we need to trigger the `click` event on the `