diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f8ea5fef..13e4541c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x, 15.x] + node-version: [12.x, 14.x, 15.x] steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 24bafc4fe..4328ea69f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ The next iteration of Vue Test Utils. It targets Vue 3. - yarn: `yarn add @vue/test-utils@next --dev` - npm: `npm install @vue/test-utils@next --save-dev` +Get started with the [documentation](https://vue-test-utils.vuejs.org/v2/). + ## Coming from Vue 2 + Vue Test Utils? We are working on [some documentation to help people migrate](https://vue-test-utils.vuejs.org/v2/guide/migration.html). @@ -19,9 +21,9 @@ There is [`vue-jest`](https://github.com/vuejs/vue-jest) for loading `.vue` file If you don't want to configure things, you can download a repository with Vue 3, `@vue/test-utils@next`, `vue-jest@next` and TypeScript configured [here](https://github.com/lmiller1990/vtu-next-demo). -## Docs +## What works? -Docs are located in [this repo](https://github.com/vuejs/vue-test-utils-next-docs). Read them [here](https://vue-test-utils.vuejs.org/v2/). They are in a separate repository because running Vuepress alongside a repo with Vue 3 causes conflicts - Vuepress expects to be running against Vue 2. This seems like the most simple solution for now. +See the [docs](https://vuejs.github.io/vue-test-utils-next-docs/guide/introduction.html). ## Development @@ -29,17 +31,13 @@ It's a pretty small codebase at the moment. Get started by running `yarn install There is still some work left to do. See issues for some basic TODOs, or the table at the bottom of this page. -## What works? - -See the [docs](https://vuejs.github.io/vue-test-utils-next-docs/guide/introduction.html). - ## Contributing This is still quite alpha, but we plan on moving to RC sooner than later. If you want to add a feature, have a hack or ping someone in Discord to chat, or check out the issues and project board. There's also some [work left to do in docs](https://github.com/vuejs/vue-test-utils-next-docs/issues). -## Comparsion with Vue Test Utils beta (targeting Vue 2) +## Comparison with Vue Test Utils beta (targeting Vue 2) This is table for those coming from VTU 1, comparing the two APIs. Some things are still a work in progress. @@ -49,54 +47,53 @@ This is table for those coming from VTU 1, comparing the two APIs. Some things a ### Mounting Options -| option | status | notes | -|---------|-------|------| -data | ✅ -slots | ✅ | has not been tested vigorously. Please try it out. -mocks | ✅ | nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -propsData | ✅ | now called `props` -provide | ✅ | nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -mixins | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -plugins | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -component | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -directives | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) -stubs | ✅ -attachToDocument |✅| renamed `attachTo`. See [here](https://github.com/vuejs/vue-test-utils/pull/1492) -attrs | ✅ -scopedSlots | ⚰️ | scopedSlots are merged with slots in Vue 3 -context | ⚰️ | different from Vue 2, does not make sense anymore. -localVue | ⚰️ | may not make sense anymore since we do not mutate the global Vue instance in Vue 3. -listeners | ⚰️ | no longer exists in Vue 3 -parentComponent | ⚰️ | - +| option | status | notes | +| ---------------- | ------ | ----------------------------------------------------------------------------------------- | +| data | ✅ | +| slots | ✅ | has not been tested vigorously. Please try it out. | +| mocks | ✅ | nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| propsData | ✅ | now called `props` | +| provide | ✅ | nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| mixins | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| plugins | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| component | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| directives | ✅ | (new!) nested in [`global`](https://vuejs.github.io/vue-test-utils-next-docs/api/#global) | +| stubs | ✅ | +| attachToDocument | ✅ | renamed `attachTo`. See [here](https://github.com/vuejs/vue-test-utils/pull/1492) | +| attrs | ✅ | +| scopedSlots | ⚰️ | scopedSlots are merged with slots in Vue 3 | +| context | ⚰️ | different from Vue 2, does not make sense anymore. | +| localVue | ⚰️ | may not make sense anymore since we do not mutate the global Vue instance in Vue 3. | +| listeners | ⚰️ | no longer exists in Vue 3 | +| parentComponent | ⚰️ | ### Wrapper API (mount) -| method | status | notes | -|---------|-------|------| -attributes | ✅ -classes | ✅ -exists | ✅ -find | ✅ | only `querySelector` syntax is supported. `find(Comp)` under discussion [here](https://github.com/vuejs/vue-test-utils/issues/1498) -emitted | ✅ -findAll | ✅ | see above. `.vm` is different to Vue 2. We are exploring options. -get | ✅ -html | ✅ -setValue | ✅ | works for select, checkbox, radio button, input, textarea. Returns `nextTick`. -text | ✅ | -trigger | ✅ | returns `nextTick`. You can do `await wrapper.find('button').trigger('click')` -setProps | ✅ | -props | ✅ -setData | ✅ | -destroy | ✅ | renamed to `unmount` to match Vue 3 lifecycle hook name. -props | ✅ -isVisible | ✅ -contains | ⚰️| use `find` -emittedByOrder | ⚰️ | use `emitted` -setSelected | ⚰️ | now part of `setValue` -setChecked | ⚰️| now part of `setValue` -is | ⚰️ -isEmpty | ⚰️ | use matchers such as [this](https://github.com/testing-library/jest-dom#tobeempty) -isVueInstance | ⚰️ -name | ⚰️ | -setMethods | ⚰️ | +| method | status | notes | +| -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| attributes | ✅ | +| classes | ✅ | +| exists | ✅ | +| find | ✅ | only `querySelector` syntax is supported. `find(Comp)` under discussion [here](https://github.com/vuejs/vue-test-utils/issues/1498) | +| emitted | ✅ | +| findAll | ✅ | see above. `.vm` is different to Vue 2. We are exploring options. | +| get | ✅ | +| html | ✅ | +| setValue | ✅ | works for select, checkbox, radio button, input, textarea. Returns `nextTick`. | +| text | ✅ | +| trigger | ✅ | returns `nextTick`. You can do `await wrapper.find('button').trigger('click')` | +| setProps | ✅ | +| props | ✅ | +| setData | ✅ | +| destroy | ✅ | renamed to `unmount` to match Vue 3 lifecycle hook name. | +| props | ✅ | +| isVisible | ✅ | +| contains | ⚰️ | use `find` | +| emittedByOrder | ⚰️ | use `emitted` | +| setSelected | ⚰️ | now part of `setValue` | +| setChecked | ⚰️ | now part of `setValue` | +| is | ⚰️ | +| isEmpty | ⚰️ | use matchers such as [this](https://github.com/testing-library/jest-dom#tobeempty) | +| isVueInstance | ⚰️ | +| name | ⚰️ | +| setMethods | ⚰️ | diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js new file mode 100644 index 000000000..1dbc98df1 --- /dev/null +++ b/docs/.vitepress/config.js @@ -0,0 +1,116 @@ +/** @typedef {import('vitepress').UserConfig} UserConfig */ + +/** @type {UserConfig} */ +const config = { + // base: '/v2/', + title: 'Vue Test Utils for Vue 3', + description: 'The documentation for the official Vue Test Utils', + locales: { + '/': { + lang: 'en-US', + title: 'Vue Test Utils (2.0.0-beta.14)' + } + }, + head: [['link', { rel: 'icon', href: `/logo.png` }]], + themeConfig: { + repo: 'vuejs/vue-test-utils-next', + docsRepo: 'vuejs/vue-test-utils-next', + docsDir: 'docs', + docsBranch: 'master', + editLinks: true, + nav: [ + { text: 'Guide', link: '/introduction/' }, + { text: 'API Reference', link: '/api/' }, + { text: 'Migrating from Vue 2', link: '/migration/' }, + { + text: 'Changelog', + link: 'https://github.com/vuejs/vue-test-utils-next/releases' + } + ], + sidebar: [ + { + text: 'Introduction', + link: '/introduction/' + }, + { + text: 'Installation', + link: '/installation/' + }, + { + text: 'Essentials', + collapsable: false, + children: [ + { text: 'A Crash Course', link: '/guide/essentials/a-crash-course' }, + { + text: 'Conditional Rendering', + link: '/guide/essentials/conditional-rendering' + }, + { + text: 'Testing Emitted Events', + link: '/guide/essentials/event-handling' + }, + { text: 'Testing Forms', link: '/guide/essentials/forms' }, + { + text: 'Passing Data to Components', + link: '/guide/essentials/passing-data' + } + ] + }, + { + text: 'Vue Test Utils in depth', + collapsable: false, + children: [ + { text: 'Slots', link: '/guide/advanced/slots' }, + { + text: 'Asynchronous Behavior', + link: '/guide/advanced/async-suspense' + }, + { + text: 'Making HTTP Requests', + link: '/guide/advanced/http-requests' + }, + { text: 'Transitions', link: '/guide/advanced/transitions' }, + { + text: 'Component Instance', + link: '/guide/advanced/component-instance' + }, + { + text: 'Reusability and Composition', + link: '/guide/advanced/reusability-composition' + }, + { text: 'Testing Vuex', link: '/guide/advanced/vuex' }, + { text: 'Testing Vue Router', link: '/guide/advanced/vue-router' }, + { + text: 'Third-party integration', + link: '/guide/advanced/third-party' + }, + { + text: 'Stubs and Shallow Mount', + link: '/guide/advanced/stubs-shallow-mount' + } + ] + }, + { + text: 'Extending Vue Test Utils', + collapsable: false, + children: [ + { text: 'Plugins', link: '/guide/extending-vtu/plugins' }, + { + text: 'Community and Learning', + link: '/guide/extending-vtu/community-learning' + } + ] + }, + { + text: 'Migrating from Vue 2', + link: '/migration/' + }, + { + text: 'API Reference', + link: '/api/' + } + ] + } +} + +module.exports = config diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index 38fcf18a9..000000000 --- a/docs/API.md +++ /dev/null @@ -1,579 +0,0 @@ -# API Reference - -## Mounting Options - -When using `mount`, you can predefine the component's state using mounting options. - -### `props` - -```vue - - - -``` - -```js -test('props', () => { - const wrapper = mount(Component, { - props: { - count: 5 - } - }) - - console.log(wrapper.html()) //=> 'Count: 5' -}) -``` - -### `data` - -Overrides a component's default `data`. Must be a function: - -```vue - - - -``` - -```js -test('overrides data', () => { - const wrapper = mount(Component, { - data() { - return { - foo: 'bar' - } - } - }) - - console.log(wrapper.html()) //=> '
Foo is bar
' -}) -``` - -### `slots` - -```vue - -``` - -```js -test('slots - default and named', () => { - const wrapper = mount(Component, { - slots: { - default: 'Default', - foo: h('h1', {}, 'Named Slot') - } - }) - - console.log(wrapper.html()) //=> '

Named Slot

Default' -}) -``` - -## `provides` - -Provides data to be received in a `setup` function via `inject`. - -```vue - - - -``` - -```js -test('injects dark theme via provide mounting option', () => { - const wrapper = mount(Component, { - provides: { - 'Theme': 'dark' - } - }) - - console.log(wrapper.html()) //=>
Theme is dark
-}) -``` - -Note: If you are using a ES6 `Symbol` for your provide key, you can use it as a dynamic key: - -```js -const ThemeSymbol = Symbol() - -mount(Component, { - provides: { - [ThemeSymbol]: 'value' - } -}) -``` -### Global -You can provide properties to the App instance using the properties under the `global` mount property - -### `global.mixins` - -Applies mixins via `app.mixin(...)`. - -```vue - - - -``` - -```js -test('adds a lifecycle mixin', () => { - const mixin = { - created() { - console.log('Component was created!') - } - } - - const wrapper = mount(Component, { - global: { - mixins: [mixin] - } - }) - - // 'Component was created!' will be logged -}) -``` - -### `global.plugins` - -Installs plugins on the component. - -```vue - - - -``` - -```js -test('installs plugins via `plugins`', () => { - const installed = jest.fn() - class Plugin { - static install() { - installed() - } - } - - const installedWithOptions = jest.fn() - class PluginWithOptions { - static install(_app: App, ...args) { - installedWithOptions(...args) - } - } - - const Component = { - render() { - return h('div') - } - } - mount(Component, { - global: { - plugins: [Plugin, [PluginWithOptions, 'argument 1', 'another argument']] - } - }) - - expect(installed).toHaveBeenCalled() - expect(installedWithOptions).toHaveBeenCalledWith( - 'argument 1', - 'another argument' - ) -}) -``` - - -### `global.components` - -Registers components globally to all components - -```js -test('installs a component globally', () => { - import GlobalComponent from '@/components/GlobalComponent' - - const Component = { - template: '
' - } - const wrapper = mount(Component, { - global: { - components: { - GlobalComponent - } - } - }) - - expect(wrapper.find('.global-component').exists()).toBe(true) -}) -``` - -### `global.directives` - -Registers a directive globally to all components - -```js -test('installs a directive globally', () => { - import Directive from '@/directives/Directive' - - const Component = { - template: '
Foo
' - } - const wrapper = mount(Component, { - global: { - directives: { - Bar: Directive - } - } - }) - - expect(wrapper.classes()).toContain('added-by-bar') -}) -``` - -## Wrapper - -When you use `mount`, a `VueWrapper` is returned with a number of useful methods for testing. Methods like `find` return a `DOMWrapper`. Both implement the same API. - - -### `html` - -Returns the HTML (via `outerHTML`) of an element. Useful for debugging. - -```vue - -``` - -```js -test('html', () => { - const wrapper = mount(Component) - - console.log(wrapper.html()) //=>

Hello world

-}) -``` - -### `text` - -Find the text (via `textContent`) of an element. - -```vue - -``` - -```js -test('text', () => { - const wrapper = mount(Component) - - expect(wrapper.find('p').text()).toBe('Hello world') -}) -``` - -### `find` - -Finds an element and returns a `DOMWrapper` if one is found. You can use the same syntax `querySelector` implements - `find` is basically an alias for `querySelector`. - -```vue - -``` - -```js -test('find', () => { - const wrapper = mount(Component) - - wrapper.find('span') //=> found; returns DOMWrapper - wrapper.find('[data-test="span"]') //=> found; returns DOMWrapper - wrapper.find('p') //=> nothing found; returns ErrorWrapper -}) -``` - -### `findAll` - -Similar to `find`, but instead returns an array of `DOMWrapper`. - -```vue - -``` - -```js -test('findAll', () => { - const wrapper = mount(Component) - - wrapper.findAll('[data-test="number"]') //=> found; returns array of DOMWrapper -}) -``` - -### `findComponent` - -Finds a Vue Component instance and returns a `VueWrapper` if one is found, otherwise returns `ErrorWrapper`. - -**Supported syntax:** - -* **querySelector** - `findComponent('.component')` - Matches standard query selector. -* **Name** - `findComponent({ name: 'myComponent' })` - matches PascalCase, snake-case, camelCase -* **ref** - `findComponent({ ref: 'dropdown' })` - Can be used only on direct ref children of mounted component -* **SFC** - `findComponent(ImportedComponent)` - Pass an imported component directly. - -```vue - - -``` - -```vue - -``` - -```js -test('findComponent', () => { - const wrapper = mount(Component) - - wrapper.findComponent('.foo') //=> found; returns VueWrapper - wrapper.findComponent('[data-test="foo"]') //=> found; returns VueWrapper - wrapper.findComponent({ name: 'Foo' }) //=> found; returns VueWrapper - wrapper.findComponent({ name: 'foo' }) //=> found; returns VueWrapper - wrapper.findComponent({ ref: 'foo' }) //=> found; returns VueWrapper - wrapper.findComponent(Foo) //=> found; returns VueWrapper -}) -``` - -### `findAllComponents` - -Similar to `findComponent` but finds all Vue Component instances that match the query and returns an array of `VueWrapper`. - -**Supported syntax:** - - * **querySelector** - `findAllComponents('.component')` - * **Name** - `findAllComponents({ name: 'myComponent' })` - * **SFC** - `findAllComponents(ImportedComponent)` - -**Note** - `Ref` is not supported here. - - -```vue - -``` - -```js -test('findAllComponents', () => { - const wrapper = mount(Component) - - wrapper.findAllComponents('[data-test="number"]') //=> found; returns array of VueWrapper -}) -``` - -### `trigger` - -Simulates an event, for example `click`, `submit` or `keyup`. Since events often cause a re-render, `trigger` returs `Vue.nextTick`. If you expect the event to trigger a re-render, you should use `await` when you call `trigger` to ensure that Vue updates the DOM before you make an assertion. - -```vue - - - -``` - -```js -test('trigger', async () => { - const wrapper = mount(Component) - - await wrapper.find('button').trigger('click') - - expect(wrapper.find('span').text()).toBe('Count: 1') -}) -``` - -### `classes` - -Returns an array of classes on an element (via `classList`). - -```vue - -``` - -```js -test('classes', () => { - const wrapper = mount(Component) - - expect(wrapper.find('.my-span').classes()).toContain('my-span') -}) -``` - -### `exists` - -Verify whether or not an element found via `find` exists or not. - -```vue - -``` - -```js -test('exists', () => { - const wrapper = mount(Component) - - expect(wrapper.find('span').exists()).toBe(true) -}) -``` - -### `emitted` - -Returns an object mapping events emitted from the `wrapper`. The arguments are stored in an array, so you can verify which arguments were emitted each time the event is emitted. - -```vue - - - +``` + +`Component.spec.js`: + +```js +test('mounts on a specific element', () => { + // in a JSDOM environment, such as Jest + document.body.innerHTML = ` +
+

Non Vue app

+
+
+ ` + const wrapper = mount(Component, { + attachTo: document.getElementById('app') + }) + + console.log(document.body.innerHTML) + /* + *
+ *

Non Vue app

+ *
Vue Component
+ *
+ */ +}) +``` + +### `attrs` + +Assigns attributes to component. + +```js +test('assigns extra attributes on components', () => { + const wrapper = mount(Component, { + attrs: { + id: 'hello', + disabled: true + } + }) + + expect(wrapper.attributes()).toEqual({ + disabled: 'true', + id: 'hello' + }) +}) +``` + +Notice that setting a prop will always trump an attribute: + +```js +test('attribute is overridden by a prop with the same name', () => { + const wrapper = mount(Component, { + props: { + message: 'Hello World' + }, + attrs: { + message: 'this will get overridden' + } + }) + + expect(wrapper.props()).toEqual({ message: 'Hello World' }) + + expect(wrapper.attributes()).toEqual({}) +}) +``` + +### `data` + +Overrides a component's default `data`. Must be a function. + +`Component.vue` + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('overrides data', () => { + const wrapper = mount(Component, { + data() { + return { + foo: 'bar' + } + } + }) + + expect(wrapper.html()).toContain('Foo is bar') +}) +``` + +### `props` + +Used to set props on a component when mounted. + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('props', () => { + const wrapper = mount(Component, { + props: { + count: 5 + } + }) + + expect(wrapper.html()).toContain('Count: 5') +}) +``` + +### `slots` + +Provide values for slots on a component. Slots can be a component imported from a `.vue` file or a render function. Currently providing an object with a `template` key is not supported. This may be supported in the future. + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +import Bar from './Bar.vue' + +test('renders slots content', () => { + const wrapper = mount(Component, { + slots: { + default: 'Default', + foo: h('h1', {}, 'Named Slot'), + bar: Bar + } + }) + + console.log(wrapper.html()) //=> '

Named Slot

Default
Bar
' +}) +``` + +### `global.components` + +Registers components globally to all components + +`Component.spec.js`: + +```js +import GlobalComponent from '@/components/GlobalComponent' + +test('installs a component globally', () => { + const Component = { + template: '
' + } + + const wrapper = mount(Component, { + global: { + components: { + GlobalComponent + } + } + }) + + expect(wrapper.find('.global-component').exists()).toBe(true) +}) +``` + +### `global.directives` + +Registers a directive globally to all components + +`Component.spec.js`: + +```js +import Directive from '@/directives/Directive' + +test('installs a directive globally', () => { + const Component = { + template: '
Foo
' + } + + const wrapper = mount(Component, { + global: { + directives: { + Bar: Directive + } + } + }) + + expect(wrapper.classes()).toContain('added-by-bar') +}) +``` + +### `global.mixins` + +Applies mixins via `app.mixin(...)`. + +`Component.spec.js`: + +```js +test('adds a lifecycle mixin', () => { + const mixin = { + created() { + console.log('Component was created!') + } + } + + const Component = { template: '
' } + + const wrapper = mount(Component, { + global: { + mixins: [mixin] + } + }) + + // 'Component was created!' will be logged +}) +``` + +### `global.mocks` + +Mocks a global instance property. Can be used for mocking out `this.$store`, `this.$router` etc. + +::: warning +This is designed to mock variables injected by third party plugins, not Vue's native properties such as $root, $children, etc. +::: + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('mocks a vuex store', async () => { + const $store = { + state: { count: 1 }, + dispatch: jest.fn() + } + + const wrapper = mount(Component, { + global: { + mocks: { + $store + } + } + }) + + expect(wrapper.html()).toContain('count: 1') + + await wrapper.find('button').trigger('click') + + expect($store.dispatch).toHaveBeenCalledWith('inc') +}) +``` + +### `global.plugins` + +Installs plugins on the component. + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('installs a plugin via `plugins`', () => { + const installed = jest.fn() + class Plugin { + static install() { + installed() + } + } + const options = { option1: true } + const testString = 'hello' + mount(Component, { + global: { + plugins: [Plugin] + } + }) + + expect(installed).toHaveBeenCalled() +}) +``` + +To use plugin with options, an array of options can be passed. + +`Component.spec.js`: + +```js +test('installs plugins with and without options', () => { + const installed = jest.fn() + class Plugin { + static install() { + installed() + } + } + + const installedWithOptions = jest.fn() + class PluginWithOptions { + static install(_app: App, ...args) { + installedWithOptions(...args) + } + } + + const Component = { + render() { + return h('div') + } + } + mount(Component, { + global: { + plugins: [Plugin, [PluginWithOptions, 'argument 1', 'another argument']] + } + }) + + expect(installed).toHaveBeenCalled() + expect(installedWithOptions).toHaveBeenCalledWith( + 'argument 1', + 'another argument' + ) +}) +``` + +### `global.provide` + +Provides data to be received in a `setup` function via `inject`. + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('injects dark theme via provide mounting option', () => { + const wrapper = mount(Component, { + global: { + provide: { + Theme: 'dark' + } + } + }) + + console.log(wrapper.html()) //=>
Theme is dark
+}) +``` + +Note: If you are using a ES6 `Symbol` for your provide key, you can use it as a dynamic key: + +`Component.spec.js`: + +```js +const ThemeSymbol = Symbol() + +mount(Component, { + global: { + provide: { + [ThemeSymbol]: 'value' + } + } +}) +``` + +### `global.stubs` + +Stubs a component for all Vue Instances. + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('stubs a component using an array', () => { + const wrapper = mount(Component, { + global: { + stubs: ['Foo'] + } + }) + + expect(wrapper.html()).toEqual('
') +}) + +test('stubs a component using an Object boolean syntax', () => { + const wrapper = mount(Component, { + global: { + stubs: { Foo: true } + } + }) + + expect(wrapper.html()).toEqual('
') +}) + +test('stubs a component using a custom component', () => { + const FooMock = { + name: 'Foo', + template: 'FakeFoo' + } + const wrapper = mount(Component, { + global: { + stubs: { Foo: FooMock } + } + }) + + expect(wrapper.html()).toEqual('
FakeFoo
') +}) +``` + +### `global.config` + +Configures [Vue's application global configuration](https://v3.vuejs.org/api/application-config.html#application-config). + +### `global.renderStubDefaultSlot` + +Renders the `default` slot content, even when using `shallow` or `shallowMount`. + +Due to technical limitations, this behavior cannot be extended to slots other than the default one. + +```js +import { config, mount } from '@vue/test-utils' + +beforeAll(() => { + config.renderStubDefaultSlot = true +}) + +afterAll(() => { + config.renderStubDefaultSlot = false +}) + +test('shallow with stubs', () => { + const Component = { + template: `
` + } + + const wrapper = mount(Component, { + shallow: true + }) + + expect(wrapper.html()).toContain('Content from the default slot') +}) +``` + +::: tip +This behavior is global, not on a mount by mount basis. Remember to enable/disable it before and after each test. +::: + +### `shallow` + +Stubs out out all child components from the components under testing. + +```js +test('stubs all components automatically using { shallow: true }', () => { + const Component = { + template: ` + + + `, + components: { + CustomComponent, + AnotherComponent + } + } + + const wrapper = mount(Component, { shallow: true }) + + expect(wrapper.html()).toEqual( + `` + ) +} +``` + +::: tip +`shallowMount` is an alias to mounting a component with `shallow: true`. +::: + +## Wrapper methods + +When you use `mount`, a `VueWrapper` is returned with a number of useful methods for testing. A `VueWrapper` is a thin wrapper around your component instance. Methods like `find` return a `DOMWrapper`, which is a thin wrapper around the DOM nodes in your component and it's children. Both implement a similar same API. + +### `attributes` + +Returns attributes on a DOM node (via `element.attributes`). + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('attributes', () => { + const wrapper = mount(Component) + + expect(wrapper.attributes('id')).toBe('foo') + expect(wrapper.attributes('class')).toBe('bar') +}) +``` + +### `classes` + +Returns an array of classes on an element (via `classList`). + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('classes', () => { + const wrapper = mount(Component) + + expect(wrapper.find('.my-span').classes()).toContain('my-span') +}) +``` + +### `emitted` + +A function that returns an object mapping events emitted from the `wrapper`. The arguments are stored in an array, so you can verify which arguments were emitted along with each event. + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('emitted', () => { + const wrapper = mount(Component) + + console.log(wrapper.emitted()) + // { + // greet: [ ['hello'], ['goodbye'] ] + // } + + expect(wrapper.emitted()).toHaveProperty('greet') + expect(wrapper.emitted().greet[0]).toEqual(['hello']) + expect(wrapper.emitted().greet[1]).toEqual(['goodbye']) +}) +``` + +### `exists` + +Verify whether or not an element found via `find` exists or not. + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('exists', () => { + const wrapper = mount(Component) + + expect(wrapper.find('span').exists()).toBe(true) + expect(wrapper.find('p').exists()).toBe(false) +}) +``` + +### `find` + +Finds an element and returns a `DOMWrapper` if one is found. You can use the same syntax `querySelector` implements - `find` is basically an alias for `querySelector`. + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('find', () => { + const wrapper = mount(Component) + + wrapper.find('span') //=> found; returns DOMWrapper + wrapper.find('[data-test="span"]') //=> found; returns DOMWrapper + wrapper.find('p') //=> nothing found; returns ErrorWrapper +}) +``` + +### `findAll` + +Similar to `find`, but instead returns an array of `DOMWrapper`. + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('findAll', () => { + const wrapper = mount(Component) + + wrapper.findAll('[data-test="number"]') //=> found; returns array of DOMWrapper +}) +``` + +### `findComponent` + +Finds a Vue Component instance and returns a `VueWrapper` if one is found, otherwise returns `ErrorWrapper`. + +**Supported syntax:** + +- **querySelector** - `findComponent('.component')` - Matches standard query selector. +- **Name** - `findComponent({ name: 'myComponent' })` - matches PascalCase, snake-case, camelCase +- **ref** - `findComponent({ ref: 'dropdown' })` - Can be used only on direct ref children of mounted component +- **SFC** - `findComponent(ImportedComponent)` - Pass an imported component directly. + +`Foo.vue` + +```vue + + + +``` + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js` + +```js +import Foo from '@/Foo.vue' + +test('findComponent', () => { + const wrapper = mount(Component) + + // All the following queries would return a VueWrapper + + // Using a standard querySelector query + wrapper.findComponent('.foo') + wrapper.findComponent('[data-test="foo"]') + + // Using component's name + wrapper.findComponent({ name: 'Foo' }) + + // Using ref attribute. Can be used only on direct children of the mounted component + wrapper.findComponent({ ref: 'foo' }) + + // Using imported component + wrapper.findComponent(Foo) +}) +``` + +### `findAllComponents` + +Similar to `findComponent` but finds all Vue Component instances that match the query. Returns an array of `VueWrapper`. + +:::warning +`Ref` syntax is not supported in `findAllComponents`. All other query syntaxes are valid. +::: + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('findAllComponents', () => { + const wrapper = mount(Component) + + // Returns an array of VueWrapper + wrapper.findAllComponents('[data-test="number"]') +}) +``` + +### `get` + +Similar to `find`, `get` looks for an element and returns a `DOMWrapper` if one is found. Otherwise it throws an error. As a rule of thumb, always use get except when you are asserting something doesn't exist. In that case use [`find`](#find). + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('get', () => { + const wrapper = mount(Component) + + wrapper.get('span') //=> found; returns DOMWrapper + wrapper.get('[data-test="span"]') //=> found; returns DOMWrapper, fails if no matching element is found +}) +``` + +### `getComponent` + +Similar to `findComponent`, `getComponent` looks for a Vue Component instance and returns a `VueWrapper` if one is found. Otherwise it throws an error. + +**Supported syntax:** + +- **querySelector** - `getComponent('.component')` - Matches standard query selector. +- **Name** - `getComponent({ name: 'myComponent' })` - matches PascalCase, snake-case, camelCase +- **ref** - `getComponent({ ref: 'dropdown' })` - Can be used only on direct ref children of mounted component +- **SFC** - `getComponent(ImportedComponent)` - Pass an imported component directly. + +`Foo.vue` + +```vue + + + +``` + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js` + +```js +import Foo from '@/Foo.vue' + +test('getComponent', () => { + const wrapper = mount(Component) + + wrapper.getComponent({ name: 'foo' }) // returns a VueWrapper + wrapper.getComponent(Foo) // returns a VueWrapper + + expect(() => wrapper.getComponent('.not-there')).toThrowError() +}) +``` + +### `html` + +Returns the HTML (via `outerHTML`) of an element. Useful for debugging. + +`Component.vue`: + +```vue + +``` + +`Component.spec.js`: + +```js +test('html', () => { + const wrapper = mount(Component) + + console.log(wrapper.html()) //=>

Hello world

+}) +``` + +### `isVisible` + +Verify whether or not a found element is visible or not. + +```js +test('isVisible', () => { + const Comp = { + template: `
` + } + const wrapper = mount(Comp) + + expect(wrapper.find('span').isVisible()).toBe(false) +}) +``` + +### `props` + +Returns props applied on a Vue Component. This should be used mostly to assert props applied to a stubbed component. + +**Note:** Props on a normally mounted Vue Component should be asserted by their side effects on the DOM or other. + +`Component.vue`: + +```js +// Foo.vue +export default { + name: 'Foo', + props: { + truthy: Boolean, + object: Object, + string: String + } +} +``` + +```vue + + + +``` + +`Component.spec.js`: + +```js +test('props', () => { + const wrapper = mount(Component, { + global: { stubs: ['Foo'] } + }) + const foo = wrapper.getComponent({ name: 'Foo' }) + + expect(foo.props('truthy')).toBe(true) + expect(foo.props('object')).toEqual({}) + expect(foo.props('notExisting')).toEqual(undefined) + expect(foo.props()).toEqual({ + truthy: true, + object: {}, + string: 'string' + }) +}) +``` + +### `setData` + +Updates component data. + +::: tip +You should use `await` when you call `setData` to ensure that Vue updates the DOM before you make an assertion. +::: + +`Component.vue`: + +```js +test('updates component data', async () => { + const Component = { + template: '
Count: {{ count }}
', + data: () => ({ count: 0 }) + } + + const wrapper = mount(Component) + expect(wrapper.html()).toContain('Count: 0') + + await wrapper.setData({ count: 1 }) + + expect(wrapper.html()).toContain('Count: 1') +}) +``` + +Notice that `setData` does not allow setting new properties that are not +defined in the component. + +Also, notice that `setData` does not modify composition API `setup()` data. + +### `setProps` + +Updates component props. + +::: tip +You should use `await` when you call `setProps` to ensure that Vue updates the DOM before you make an assertion. +::: + +`Component.vue`: + +```vue + + + +``` + +`Component.spec.js` + +```js +test('updates prop', async () => { + const wrapper = mount(Component, { + props: { + message: 'hello' + } + }) + expect(wrapper.html()).toContain('hello') + + await wrapper.setProps({ message: 'goodbye' }) + + expect(wrapper.html()).toContain('goodbye') +}) +``` + +### `setValue` + +Sets a value on DOM element. Including: + +- `` + - `type="checkbox"` and `type="radio"` are detected and will have `element.checked` set +- `