diff --git a/README.md b/README.md index f01e3547..e8d01d2e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@

Vue Testing Library

+
+ -[**Read the Docs**][docs] | [Edit the docs][docs-edit] +[**Read the docs**][docs] | [Edit the docs][docs-edit]
@@ -36,6 +38,9 @@

Table of Contents

+ + + - [Installation](#installation) - [A simple example](#a-simple-example) - [More examples](#more-examples) @@ -44,9 +49,11 @@ - [License](#license) - [Contributors](#contributors) + + ## Installation -This module is distributed via npm and should be installed as one of your +This module is distributed via `npm` and should be installed as one of your project's `devDependencies`: ``` @@ -57,7 +64,7 @@ This library has `peerDependencies` listings for `Vue` and `vue-template-compiler`. 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/testing-library/jest-dom#readme). ## A simple example @@ -90,7 +97,7 @@ import {render, fireEvent} from '@testing-library/vue' import TestComponent from './TestComponent.vue' test('increments value on click', async () => { - // The render method returns a collection of utilities to query your component. + // The render method returns a collection of utilities to query the component. const {getByText} = render(TestComponent) // getByText returns the first matching node for the provided text, and @@ -100,7 +107,7 @@ test('increments value on click', async () => { // `button` is the actual DOM element. const button = getByText('increment') - // Dispatch a native click event. + // Dispatch a couple of native click events. await fireEvent.click(button) await fireEvent.click(button) @@ -124,12 +131,12 @@ Feel free to contribute with more examples! ## Docs -[**Read the Docs**][docs] | [Edit the docs][docs-edit] +[**Read the docs**][docs] | [Edit the docs][docs-edit] ## Typings The TypeScript type definitions are in the -[DefinitelyTyped repo](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/testing-library__vue) +[DefinitelyTyped repo][types] and bundled with Vue Testing Library. ## License @@ -138,17 +145,21 @@ and bundled with Vue Testing Library. ## Contributors -[![dfcook](https://avatars0.githubusercontent.com/u/10348212?v=3&s=170)](https://github.com/dfcook) -[![afontcu](https://avatars3.githubusercontent.com/u/9197791?s=170&v=3)](https://github.com/afontcu) -[![eunjae-lee](https://avatars0.githubusercontent.com/u/499898?v=3&s=170)](https://github.com/eunjae-lee) -[![tim-maguire](https://avatars0.githubusercontent.com/u/29452317?v=3&s=170)](https://github.com/tim-maguire) -[![samdelacruz](https://avatars0.githubusercontent.com/u/2040007?v=3&s=170)](https://github.com/samdelacruz) -[![ankitsinghaniyaz](https://avatars0.githubusercontent.com/u/11331989?v=3&s=170)](https://github.com/ankitsinghaniyaz) -[![lindgr3n](https://avatars0.githubusercontent.com/u/24882614?v=3&s=170)](https://github.com/lindgr3n) -[![kentcdodds](https://avatars0.githubusercontent.com/u/1500684?v=3&s=170)](https://github.com/kentcdodds) -[![brennj](https://avatars2.githubusercontent.com/u/29227924?v=3&s=170)](https://github.com/brennj) -[![makeupsomething](https://avatars2.githubusercontent.com/u/7676733?v=3&s=170)](https://github.com/makeupsomething) -[![mb200](https://avatars2.githubusercontent.com/u/22549525?v=3&s=170)](https://github.com/mb200) +[![dfcook](https://avatars0.githubusercontent.com/u/10348212?v=3&s=120)](https://github.com/dfcook) +[![afontcu](https://avatars3.githubusercontent.com/u/9197791?v=3&s=120)](https://github.com/afontcu) +[![eunjae-lee](https://avatars0.githubusercontent.com/u/499898?v=3&s=120)](https://github.com/eunjae-lee) +[![tim-maguire](https://avatars0.githubusercontent.com/u/29452317?v=3&s=120)](https://github.com/tim-maguire) +[![samdelacruz](https://avatars0.githubusercontent.com/u/2040007?v=3&s=120)](https://github.com/samdelacruz) +[![ankitsinghaniyaz](https://avatars0.githubusercontent.com/u/11331989?v=3&s=120)](https://github.com/ankitsinghaniyaz) +[![lindgr3n](https://avatars0.githubusercontent.com/u/24882614?v=3&s=120)](https://github.com/lindgr3n) +[![kentcdodds](https://avatars0.githubusercontent.com/u/1500684?v=3&s=120)](https://github.com/kentcdodds) +[![brennj](https://avatars2.githubusercontent.com/u/29227924?v=3&s=120)](https://github.com/brennj) +[![makeupsomething](https://avatars2.githubusercontent.com/u/7676733?v=3&s=120)](https://github.com/makeupsomething) +[![mb200](https://avatars2.githubusercontent.com/u/22549525?v=3&s=120)](https://github.com/mb200) +[![Oluwasetemi](https://avatars2.githubusercontent.com/u/10030028?v=3&s=120)](https://github.com/Oluwasetemi) +[![cimbul](https://avatars2.githubusercontent.com/u/927923?v=3&s=120)](https://github.com/cimbul) +[![alexkrolick](https://avatars2.githubusercontent.com/u/1571667?v=3&s=120)](https://github.com/alexkrolick) +[![edufarre](https://avatars2.githubusercontent.com/u/25011566?v=3&s=120)](https://github.com/edufarre) [build-badge]: https://travis-ci.org/testing-library/vue-testing-library.svg?branch=master @@ -163,13 +174,14 @@ and bundled with Vue Testing Library. [npm]: https://badge.fury.io/js/%40testing-library%2Fvue [license-badge]: https://img.shields.io/github/license/testing-library/vue-testing-library.svg [license]: https://github.com/testing-library/vue-testing-library/blob/master/LICENSE +[types]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/testing-library__vue [docs]: https://testing-library.com/vue [docs-edit]: https://github.com/testing-library/testing-library-docs -[test-directory]: https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__ -[vuex-example]: https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/vuex.js -[vue-router-example]: https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/vue-router.js -[vee-validate-example]: https://github.com/testing-library/vue-testing-library/tree/master/tests/__tests__/validate-plugin.js -[vue-i18n-example]: https://github.com/testing-library/vue-testing-library/blob/master/tests/__tests__/vueI18n.js +[test-directory]: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__ +[vuex-example]: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/vuex.js +[vue-router-example]: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/vue-router.js +[vee-validate-example]: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/validate-plugin.js +[vue-i18n-example]: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/vueI18n.js diff --git a/package-lock.json b/package-lock.json index 0949b841..ba279c77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4399,9 +4399,9 @@ } }, "eslint-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", - "integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", "dev": true, "requires": { "eslint-visitor-keys": "^1.0.0" @@ -11690,6 +11690,12 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuetify": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.0.19.tgz", + "integrity": "sha512-zBskf77Z+RH8+Qs1q0NIDv/1enVkOoVH2dcdjcs+ZUNOhnlG0IkDedmqE2+PNm0JvJdgpOaV8wq+Pl69TGD2Hg==", + "dev": true + }, "vuex": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.1.tgz", diff --git a/package.json b/package.json index 1a15eba0..cde5f72d 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "vue-jest": "^3.0.4", "vue-router": "^3.1.2", "vue-template-compiler": "^2.6.10", + "vuetify": "^2.0.19", "vuex": "^3.1.1" }, "peerDependencies": { diff --git a/src/__tests__/__snapshots__/axios-mock.js.snap b/src/__tests__/__snapshots__/axios-mock.js.snap deleted file mode 100644 index 8083a688..00000000 --- a/src/__tests__/__snapshots__/axios-mock.js.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`makes an API call and displays the greeting when load-greeting is clicked 1`] = ` -
- hello there -
-`; diff --git a/src/__tests__/auto-cleanup-skip.js b/src/__tests__/auto-cleanup-skip.js index 5423ef52..bcd4595a 100644 --- a/src/__tests__/auto-cleanup-skip.js +++ b/src/__tests__/auto-cleanup-skip.js @@ -5,8 +5,8 @@ beforeAll(async () => { render = vtl.render }) -// This one verifies that if VTL_SKIP_AUTO_CLEANUP is set -// then we DON'T auto-wire up the afterEach for folks +// This test verifies that if VTL_SKIP_AUTO_CLEANUP is set then we DON'T +// auto-wire up the afterEach cleanup for folks. test('first test render a vue component', () => { render({ template: `

Hello World

`, diff --git a/src/__tests__/auto-cleanup.js b/src/__tests__/auto-cleanup.js index 37fa8a29..cf3ebbdf 100644 --- a/src/__tests__/auto-cleanup.js +++ b/src/__tests__/auto-cleanup.js @@ -1,9 +1,8 @@ import {render} from '@testing-library/vue' import '@testing-library/jest-dom/extend-expect' -// This just verifies that by importing VTL in an -// environment which supports afterEach (like jest) -// we'll get automatic cleanup between tests. +// This just verifies that by importing VTL in an environment which supports +// afterEach (like jest) we'll get automatic cleanup between tests. test('render the first component', () => { render({ template: `

Hello World

`, diff --git a/src/__tests__/axios-mock.js b/src/__tests__/axios-mock.js index 93eff0f3..5687692e 100644 --- a/src/__tests__/axios-mock.js +++ b/src/__tests__/axios-mock.js @@ -1,9 +1,9 @@ +import '@testing-library/jest-dom/extend-expect' import axiosMock from 'axios' import {render, fireEvent} from '@testing-library/vue' import Component from './components/Fetch.vue' -import '@testing-library/jest-dom/extend-expect' -test('makes an API call and displays the greeting when load-greeting is clicked', async () => { +test('mocks an API call when load-greeting is clicked', async () => { axiosMock.get.mockImplementationOnce(() => Promise.resolve({ data: {greeting: 'hello there'}, @@ -12,7 +12,6 @@ test('makes an API call and displays the greeting when load-greeting is clicked' const {html, getByText} = render(Component, {props: {url: '/greeting'}}) - // Act await fireEvent.click(getByText('Fetch')) expect(axiosMock.get).toHaveBeenCalledTimes(1) @@ -23,5 +22,11 @@ test('makes an API call and displays the greeting when load-greeting is clicked' // that Snapshot Testing should not be treated as a replacement for regular // tests. // More about the topic: https://twitter.com/searls/status/919594505938112512 - expect(html()).toMatchSnapshot() + expect(html()).toMatchInlineSnapshot(` +
+ hello there +
+ `) }) diff --git a/src/__tests__/components/Collapsible.vue b/src/__tests__/components/Collapsible.vue new file mode 100644 index 00000000..33766853 --- /dev/null +++ b/src/__tests__/components/Collapsible.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/__tests__/components/Translations.vue b/src/__tests__/components/Translations.vue new file mode 100644 index 00000000..0510b5d8 --- /dev/null +++ b/src/__tests__/components/Translations.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/__tests__/components/Vuetify.vue b/src/__tests__/components/Vuetify.vue new file mode 100644 index 00000000..3add64fa --- /dev/null +++ b/src/__tests__/components/Vuetify.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/__tests__/debug.js b/src/__tests__/debug.js index f3a33c56..89df1bb3 100644 --- a/src/__tests__/debug.js +++ b/src/__tests__/debug.js @@ -34,7 +34,7 @@ test('debug pretty prints the provided parameter', () => { expect.stringContaining('Lorem ipsum dolor sit amet'), ) - // Notice the 'not' particle + // Notice the 'not' particle. expect(console.log).not.toHaveBeenCalledWith( expect.stringContaining('Hello World'), ) diff --git a/src/__tests__/disappearance.js b/src/__tests__/disappearance.js index 228afe94..66ca8ed5 100644 --- a/src/__tests__/disappearance.js +++ b/src/__tests__/disappearance.js @@ -9,7 +9,8 @@ test('waits for the data to be loaded', async () => { getByText('Loading...') expect(queryByText(/Loaded this message/)).not.toBeInTheDocument() - // Line reads as follows: "Wait until element with text 'Loading...' is gone." + // Following line reads as follows: + // "Wait until element with text 'Loading...' is gone." await waitForElementToBeRemoved(() => queryByText('Loading...')) // It is equivalent to: // @@ -17,7 +18,7 @@ test('waits for the data to be loaded', async () => { // expect(queryByText('Loading...')).not.toBeInTheDocument() // }) // - // `wait()` waits until the callback function passes or times out. + // because `wait()` waits until the callback function passes or times out. // After 'Loading...' is gone, we can assert that fetched data is rendered. expect(queryByTestId('message')).toHaveTextContent(/Hello World/) diff --git a/src/__tests__/form.js b/src/__tests__/form.js index 2e984a9c..4d26da2f 100644 --- a/src/__tests__/form.js +++ b/src/__tests__/form.js @@ -24,7 +24,7 @@ test('Review form submits', async () => { const submitButton = getByText('Submit') - // Initially the submit button should be disabled + // Initially the submit button should be disabled. expect(submitButton).toBeDisabled() const titleInput = getByLabelText(/title of the movie/i) @@ -33,7 +33,7 @@ test('Review form submits', async () => { const reviewTextarea = getByPlaceholderText('Write an awesome review') await fireEvent.update(reviewTextarea, fakeReview.review) - // Rating Radio buttons + // Rating Radio buttons. const initiallySelectedInput = getByLabelText('Awful') const ratingSelect = getByLabelText('Wonderful') @@ -45,23 +45,20 @@ test('Review form submits', async () => { expect(ratingSelect.checked).toBe(true) expect(initiallySelectedInput.checked).toBe(false) - // Get the Input element by its implicit ARIA role + // Get the Input element by its implicit ARIA role. const recommendInput = getByRole('checkbox') expect(recommendInput.checked).toBe(false) await fireEvent.update(recommendInput) expect(recommendInput.checked).toBe(true) - // NOTE: in jsdom, it's not possible to trigger a form submission - // by clicking on the submit button. This is really unfortunate. - // So the next best thing is to fireEvent a submit on the form itself - // then ensure that there's a submit button + // Make sure the submit button is enabled. expect(submitButton).toBeEnabled() expect(submitButton).toHaveAttribute('type', 'submit') await fireEvent.click(submitButton) - // Assert event has been emitted + // Assert the right event has been emitted. expect(emitted()).toHaveProperty('submit') expect(emitted().submit[0][0]).toMatchObject(fakeReview) }) diff --git a/src/__tests__/functional.js b/src/__tests__/functional.js index 033677b1..06a58d41 100644 --- a/src/__tests__/functional.js +++ b/src/__tests__/functional.js @@ -8,13 +8,13 @@ const Functional = { }, } -test('renders functional comp', () => { +test('renders functional component', () => { const {getByText} = render(Functional) getByText('Hi!') }) -test('renders functional SFC comp', () => { +test('renders functional SFC component', () => { const {getByText} = render(FunctionalSFC) getByText('Hi!') diff --git a/src/__tests__/render.js b/src/__tests__/render.js new file mode 100644 index 00000000..4a48772a --- /dev/null +++ b/src/__tests__/render.js @@ -0,0 +1,77 @@ +import {render} from '@testing-library/vue' +import '@testing-library/jest-dom/extend-expect' + +test('baseElement defaults to document.body', () => { + const {baseElement} = render({template: '
'}) + expect(baseElement).toBe(document.body) +}) + +test('renders custom baseElement', () => { + const Component = {template: ''} + + const {baseElement, container} = render(Component, { + baseElement: document.createElement('blink'), + }) + + expect(baseElement).toMatchInlineSnapshot(` + +
+ +
+
+ `) + + expect(container).toMatchInlineSnapshot(` +
+ +
+ `) +}) + +test('renders container', () => { + const {container, getByTestId} = render({ + template: '
my content
', + }) + + expect(container.firstChild).toHaveTextContent( + getByTestId('myDiv').textContent, + ) +}) + +test('container defaults to div', () => { + const {container} = render({template: '
'}) + + expect(container.tagName).toBe('DIV') +}) + +test('renders custom container', () => { + const blink = document.createElement('blink') + const Component = {template: '
'} + + const {container} = render(Component, { + container: document.body.appendChild(blink), + }) + + expect(container).toBe(blink) +}) + +test('baseElement matches container if not custom baseElement is provided', () => { + const blink = document.createElement('blink') + const Component = {template: '
'} + + const {container, baseElement} = render(Component, { + container: document.body.appendChild(blink), + }) + + expect(container).toMatchInlineSnapshot(` + +
+ + `) + + expect(baseElement).toMatchInlineSnapshot(` + +
+ + `) +}) diff --git a/src/__tests__/select.js b/src/__tests__/select.js index 437488a8..f83a8512 100644 --- a/src/__tests__/select.js +++ b/src/__tests__/select.js @@ -2,25 +2,25 @@ import {render, fireEvent} from '@testing-library/vue' import '@testing-library/jest-dom/extend-expect' import Select from './components/Select' -// In this test file we showcase several ways to interact with a Select element +// In this test file we showcase several ways to interact with a Select element. test('Select component', async () => { let optionElement const {getByDisplayValue, getByText} = render(Select) - // Get the Select element by using the initially displayed value + // Get the Select element by using the initially displayed value. const select = getByDisplayValue('Tyrannosaurus') expect(select.value).toBe('dino1') - // Update it by manually sending a valid option value + // Update it by manually sending a valid option value. await fireEvent.update(select, 'dino2') expect(select.value).toBe('dino2') - // We can trigger an update event by directly getting the + // ...even if option is within an . optionElement = getByText('Diplodocus') await fireEvent.update(optionElement) expect(select.value).toBe('dino4') diff --git a/src/__tests__/simple-button.js b/src/__tests__/simple-button.js index f02299b9..e8216dfd 100644 --- a/src/__tests__/simple-button.js +++ b/src/__tests__/simple-button.js @@ -5,28 +5,28 @@ import '@testing-library/jest-dom/extend-expect' test('renders button with text', () => { const text = "Click me; I'm sick" - // Set the prop value by using the second argument of `render()` + // Set the prop value by using the second argument of `render()`. const {getByRole} = render(Button, { props: {text}, }) - // Get the only element with a 'button' role + // Get the only element with a 'button' role. const button = getByRole('button') expect(button).toHaveTextContent(text) }) -test('click event is emitted when button is clicked', async () => { +test('emits click event when button is clicked', async () => { const text = 'Click me' const {getByRole, emitted} = render(Button, { props: {text}, }) - // Send a click event + // Send a click event. await fireEvent.click(getByRole('button')) // Expect that the event emitted a "click" event. We should test for emitted - // events has they are part of the public API of the component + // events has they are part of the public API of the component. expect(emitted()).toHaveProperty('click') }) diff --git a/src/__tests__/stopwatch.js b/src/__tests__/stopwatch.js index 3eba0c94..f57a68f1 100644 --- a/src/__tests__/stopwatch.js +++ b/src/__tests__/stopwatch.js @@ -1,22 +1,6 @@ +import '@testing-library/jest-dom/extend-expect' import {render, wait, fireEvent} from '@testing-library/vue' import StopWatch from './components/StopWatch.vue' -import '@testing-library/jest-dom/extend-expect' - -test('unmounts a component', async () => { - jest.spyOn(console, 'error').mockImplementation(() => {}) - - const {unmount, isUnmounted, getByText} = render(StopWatch) - await fireEvent.click(getByText('Start')) - - // Destroys a Vue component instance. - unmount() - - expect(isUnmounted()).toBe(true) - - await wait() - - expect(console.error).not.toHaveBeenCalled() -}) test('updates component state', async () => { const {getByTestId, getByText} = render(StopWatch) @@ -42,3 +26,19 @@ test('updates component state', async () => { // content has changed. expect(elapsedTime).not.toHaveTextContent('0ms') }) + +test('unmounts a component', async () => { + jest.spyOn(console, 'error').mockImplementation(() => {}) + + const {unmount, isUnmounted, getByText} = render(StopWatch) + await fireEvent.click(getByText('Start')) + + // Destroys a Vue component instance. + unmount() + + expect(isUnmounted()).toBe(true) + + await wait() + + expect(console.error).not.toHaveBeenCalled() +}) diff --git a/src/__tests__/update-props.js b/src/__tests__/update-props.js index 5bc2728c..19f13056 100644 --- a/src/__tests__/update-props.js +++ b/src/__tests__/update-props.js @@ -1,7 +1,11 @@ +import '@testing-library/jest-dom/extend-expect' import {render} from '@testing-library/vue' import NumberDisplay from './components/NumberDisplay.vue' -import '@testing-library/jest-dom/extend-expect' +// It'd probably be better if you test the component that's doing the prop +// updating to ensure that the props are being updated correctly. +// That said, if you'd prefer to update the props of a rendered component, this +// function can be used to update props of the rendered component. test('calling render with the same component but different props does not remount', async () => { const {getByTestId, updateProps} = render(NumberDisplay, { props: {number: 1}, diff --git a/src/__tests__/visibility.js b/src/__tests__/visibility.js new file mode 100644 index 00000000..322c11eb --- /dev/null +++ b/src/__tests__/visibility.js @@ -0,0 +1,28 @@ +import {render, fireEvent} from '@testing-library/vue' +import '@testing-library/jest-dom/extend-expect' +import Collapsible from './components/Collapsible' + +// Using the query `getByText` here is completely right because +// we use `v-show` in the component, which means that the element +// will be rendered but not visible, whereas if we use `v-if` instead +// we should use the `queryByText` and expect it to be `null` because +// the element won't be rendered +test('Collapsible component', async () => { + const {getByText} = render(Collapsible) + + // Check that text element is not initially visible. + expect(getByText('Text')).not.toBeVisible() + + // Click button in order to display the collapsed text element + const button = getByText('Click me') + await fireEvent.click(button) + + // Check that text element is visible + expect(getByText('Text')).toBeVisible() + + // Click button to hide the visible text element + await fireEvent.click(button) + + // Check that text element is not visible again + expect(getByText('Text')).not.toBeVisible() +}) diff --git a/src/__tests__/vue-i18n.js b/src/__tests__/vue-i18n.js new file mode 100644 index 00000000..70ab8f21 --- /dev/null +++ b/src/__tests__/vue-i18n.js @@ -0,0 +1,40 @@ +import '@testing-library/jest-dom/extend-expect' +import {render, fireEvent} from '@testing-library/vue' +import Vuei18n from 'vue-i18n' +import Translations from './components/Translations' + +const messages = { + en: { + Hello: 'Hello', + }, + ja: { + Hello: 'こんにちは', + }, +} + +test('renders translations', async () => { + const {queryByText, getByText} = render(Translations, {}, vue => { + // Let's register and configure Vuei18n normally + vue.use(Vuei18n) + + const i18n = new Vuei18n({ + locale: 'en', + fallbackLocale: 'en', + messages, + }) + + // Notice how we return an object from the callback function. It will be + // merged as an additional option on the created Vue instance. + return { + i18n, + } + }) + + expect(getByText('Hello')).toBeInTheDocument() + + await fireEvent.click(getByText('Japanese')) + + expect(getByText('こんにちは')).toBeInTheDocument() + + expect(queryByText('Hello')).toBeNull() +}) diff --git a/src/__tests__/vuetify.js b/src/__tests__/vuetify.js new file mode 100644 index 00000000..17d30e86 --- /dev/null +++ b/src/__tests__/vuetify.js @@ -0,0 +1,33 @@ +import Vue from 'vue' +import {render, fireEvent} from '@testing-library/vue' +import Vuetify from 'vuetify' +import VuetifyDemoComponent from './components/Vuetify' + +// We need to use a global Vue instance, otherwise Vuetify will complain about +// read-only attributes. +// More info: https://github.com/vuetifyjs/vuetify/issues/4068 +// https://vuetifyjs.com/en/getting-started/unit-testing +Vue.use(Vuetify) + +// Vuetify requires you to wrap you app with a v-app component that provides +// a
node. So you can do that, or you can also set the +// attribute to the DOM. +document.body.setAttribute('data-app', true) +// Another solution is to create a custom renderer that provides all the +// environment required by Vuetify. + +test('renders a Vuetify-powered component', async () => { + const {getByText} = render(VuetifyDemoComponent, { + vuetify: new Vuetify(), + }) + + await fireEvent.click(getByText('open')) + + expect(getByText('Lorem ipsum dolor sit amet.')).toMatchInlineSnapshot(` +
+ Lorem ipsum dolor sit amet. +
+ `) +}) diff --git a/src/vue-testing-library.js b/src/vue-testing-library.js index 77316df0..acfb1176 100644 --- a/src/vue-testing-library.js +++ b/src/vue-testing-library.js @@ -11,9 +11,19 @@ const mountedWrappers = new Set() function render( TestComponent, - {store = null, routes = null, ...mountOptions} = {}, + { + store = null, + routes = null, + container: customContainer, + baseElement: customBaseElement, + ...mountOptions + } = {}, configurationCb, ) { + const div = document.createElement('div') + const baseElement = customBaseElement || customContainer || document.body + const container = customContainer || baseElement.appendChild(div) + const localVue = createLocalVue() let vuexStore = null let router = null @@ -53,15 +63,12 @@ function render( }) mountedWrappers.add(wrapper) - - const div = document.createElement('div') - wrapper.element.parentNode.insertBefore(div, wrapper.element) - div.appendChild(wrapper.element) + container.appendChild(wrapper.element) return { - container: wrapper.element.parentNode, - baseElement: document.body, - debug: (el = wrapper.element) => logDOM(el), + container, + baseElement, + debug: (el = baseElement) => logDOM(el), unmount: () => wrapper.destroy(), isUnmounted: () => wrapper.vm._isDestroyed, html: () => wrapper.html(), @@ -70,7 +77,7 @@ function render( wrapper.setProps(_) return wait() }, - ...getQueriesForElement(wrapper.element.parentNode), + ...getQueriesForElement(baseElement), } } @@ -116,7 +123,7 @@ fireEvent.touch = async elem => { // Small utility to provide a better experience when working with v-model. // Related upstream issue: https://github.com/vuejs/vue-test-utils/issues/345#issuecomment-380588199 -// Examples: https://github.com/testing-library/vue-testing-library/blob/master/tests/__tests__/form.js +// Examples: https://github.com/testing-library/vue-testing-library/blob/master/src/__tests__/form.js fireEvent.update = (elem, value) => { const tagName = elem.tagName const type = elem.type