diff --git a/package-lock.json b/package-lock.json
index 2c6f66cb..0e3aa9e2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1491,12 +1491,13 @@
"integrity": "sha512-vTCdPp/T/Q3oSqwHmZ5Kpa9oI7iLtGl3RQaA/NyLHikvcrPxACkkKVr/XzkSPJWXHRhKGzVvb0urJsbMlRxi1Q=="
},
"@testing-library/dom": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.0.1.tgz",
- "integrity": "sha512-HmyN4b/PmSaSB1ku0tWjgnTtyrwNBXEpp44wgfNaDhyj6IJTCWp1GAf4AANoLGItgMsYjepwWOdMyuJ/8iyStQ==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-5.2.0.tgz",
+ "integrity": "sha512-nFaZes/bzDfMqwZpQXdiPyj3WXU16FYf5k5NCFu/qJM4JdRJLHEtSRYtrETmk7nCf+qLVoHCqRduGi/4KE83Gw==",
"requires": {
"@babel/runtime": "^7.4.5",
"@sheerun/mutationobserver-shim": "^0.3.2",
+ "aria-query": "3.0.0",
"pretty-format": "^24.8.0",
"wait-for-expect": "^1.2.0"
}
@@ -1721,6 +1722,15 @@
"sprintf-js": "~1.0.2"
}
},
+ "aria-query": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
+ "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+ "requires": {
+ "ast-types-flow": "0.0.7",
+ "commander": "^2.11.0"
+ }
+ },
"arr-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -1803,6 +1813,11 @@
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
"dev": true
},
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0="
+ },
"astral-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
@@ -2541,8 +2556,7 @@
"commander": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
- "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
- "dev": true
+ "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
},
"component-emitter": {
"version": "1.3.0",
diff --git a/package.json b/package.json
index 982c50f2..e69913f4 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
"author": "Daniel Cook",
"license": "MIT",
"dependencies": {
- "@testing-library/dom": "^5.0.1",
+ "@testing-library/dom": "^5.2.0",
"@vue/test-utils": "^1.0.0-beta.29",
"vue": "^2.6.10",
"vue-template-compiler": "^2.6.10"
diff --git a/tests/__tests__/__snapshots__/axios-mock.js.snap b/tests/__tests__/__snapshots__/axios-mock.js.snap
new file mode 100644
index 00000000..8083a688
--- /dev/null
+++ b/tests/__tests__/__snapshots__/axios-mock.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`makes an API call and displays the greeting when load-greeting is clicked 1`] = `
+
+ Fetch
+
+ hello there
+
+`;
diff --git a/tests/__tests__/__snapshots__/fetch.js.snap b/tests/__tests__/__snapshots__/fetch.js.snap
deleted file mode 100644
index b2894825..00000000
--- a/tests/__tests__/__snapshots__/fetch.js.snap
+++ /dev/null
@@ -1,9 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Fetch makes an API call and displays the greeting when load-greeting is clicked 1`] = `
-
- Fetch
-
- hello there
-
-`;
diff --git a/tests/__tests__/axios-mock.js b/tests/__tests__/axios-mock.js
new file mode 100644
index 00000000..dd62a910
--- /dev/null
+++ b/tests/__tests__/axios-mock.js
@@ -0,0 +1,27 @@
+import axiosMock from 'axios'
+import { render, fireEvent } from '@testing-library/vue'
+import Component from './components/Fetch.vue'
+import 'jest-dom/extend-expect'
+
+test('makes an API call and displays the greeting when load-greeting is clicked', async () => {
+ axiosMock.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ data: { greeting: 'hello there' }
+ })
+ )
+
+ const { html, getByText } = render(Component, { props: { url: '/greeting' } })
+
+ // Act
+ await fireEvent.click(getByText('Fetch'))
+
+ expect(axiosMock.get).toHaveBeenCalledTimes(1)
+ expect(axiosMock.get).toHaveBeenCalledWith('/greeting')
+ getByText('hello there')
+
+ // You can render component snapshots by using html(). However, bear in mind
+ // 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()
+})
diff --git a/tests/__tests__/components/Button.vue b/tests/__tests__/components/Button.vue
index 7b9ea7ee..cfbf37f1 100644
--- a/tests/__tests__/components/Button.vue
+++ b/tests/__tests__/components/Button.vue
@@ -1,5 +1,5 @@
- {{ text }}
+ {{ text }}
diff --git a/tests/__tests__/router/programmatic-routing/components/LocationDisplay.vue b/tests/__tests__/router/programmatic-routing/components/LocationDisplay.vue
deleted file mode 100644
index eecf2959..00000000
--- a/tests/__tests__/router/programmatic-routing/components/LocationDisplay.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
- {{ $route.fullPath }}
-
diff --git a/tests/__tests__/router/programmatic-routing/components/NoMatch.vue b/tests/__tests__/router/programmatic-routing/components/NoMatch.vue
deleted file mode 100644
index 8118731c..00000000
--- a/tests/__tests__/router/programmatic-routing/components/NoMatch.vue
+++ /dev/null
@@ -1,3 +0,0 @@
-
- No match
-
diff --git a/tests/__tests__/router/programmatic-routing/index.js b/tests/__tests__/router/programmatic-routing/index.js
deleted file mode 100644
index 7396f3ff..00000000
--- a/tests/__tests__/router/programmatic-routing/index.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import 'jest-dom/extend-expect'
-
-import App from './components/App.vue'
-import Home from './components/Home.vue'
-import About from './components/About.vue'
-
-import { render, fireEvent } from '@testing-library/vue'
-
-const routes = [
- { path: '/', component: Home },
- { path: '/about', component: About },
- { path: '*', redirect: '/about' }
-]
-
-test('navigating programmatically', async () => {
- const { queryByTestId } = render(App, { routes })
-
- expect(queryByTestId('location-display')).toHaveTextContent('/')
- await fireEvent.click(queryByTestId('go-to-about'))
-
- expect(queryByTestId('location-display')).toHaveTextContent('/about')
-})
diff --git a/tests/__tests__/simple-button.js b/tests/__tests__/simple-button.js
index 7112dbaf..27da8ac2 100644
--- a/tests/__tests__/simple-button.js
+++ b/tests/__tests__/simple-button.js
@@ -1,22 +1,29 @@
import { render, cleanup, fireEvent } from '@testing-library/vue'
-import SimpleButton from './components/Button'
+import Button from './components/Button'
+import 'jest-dom/extend-expect'
afterEach(cleanup)
test('renders button with text', () => {
- const buttonText = "Click me; I'm sick"
- const { getByText } = render(SimpleButton, {
- props: { text: buttonText }
+ const text = "Click me; I'm sick"
+
+ // Set the prop value by using the second argument of `render()`
+ const { getByRole } = render(Button, {
+ props: { text }
})
- getByText(buttonText)
+ expect(getByRole('button')).toHaveTextContent(text)
})
-test('click event is emitted when button is clicked', () => {
+test('click event is emitted when button is clicked', async () => {
const text = 'Click me'
- const { getByText, emitted } = render(SimpleButton, {
+
+ const { getByRole, emitted } = render(Button, {
props: { text }
})
- fireEvent.click(getByText(text))
+
+ // Send a click event to the element with a 'button' role
+ await fireEvent.click(getByRole('button'))
+
expect(emitted().click).toHaveLength(1)
})
diff --git a/tests/__tests__/stopwatch.js b/tests/__tests__/stopwatch.js
index 363f860d..505f4ce8 100644
--- a/tests/__tests__/stopwatch.js
+++ b/tests/__tests__/stopwatch.js
@@ -10,10 +10,13 @@ test('unmounts a component', async () => {
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()
})
@@ -23,11 +26,21 @@ test('updates component state', async () => {
const startButton = getByText('Start')
const elapsedTime = getByTestId('elapsed')
+ // Assert initial state.
expect(elapsedTime).toHaveTextContent('0ms')
+ getByText('Start')
await fireEvent.click(startButton)
+
+ getByText('Stop')
+
+ // Wait for one tick of the event loop.
await wait()
+
+ // Stop the timer.
await fireEvent.click(startButton)
+ // We can't assert a specific amount of time. Instead, we assert that the
+ // content has changed.
expect(elapsedTime).not.toHaveTextContent('0ms')
})
diff --git a/tests/__tests__/update-props.js b/tests/__tests__/update-props.js
new file mode 100644
index 00000000..a732ce89
--- /dev/null
+++ b/tests/__tests__/update-props.js
@@ -0,0 +1,19 @@
+import NumberDisplay from './components/NumberDisplay.vue'
+import { render } from '@testing-library/vue'
+import 'jest-dom/extend-expect'
+
+test('calling render with the same component but different props does not remount', async () => {
+ const { getByTestId, updateProps } = render(NumberDisplay, {
+ props: { number: 1 }
+ })
+
+ expect(getByTestId('number-display')).toHaveTextContent('1')
+
+ await updateProps({ number: 2 })
+
+ expect(getByTestId('number-display')).toHaveTextContent('2')
+
+ // Assert that, even after updating props, the component hasn't remounted,
+ // meaning we are testing the same component instance we rendered initially.
+ expect(getByTestId('instance-id')).toHaveTextContent('1')
+})
diff --git a/tests/__tests__/validate-plugin.js b/tests/__tests__/validate-plugin.js
index 9b933a94..d562f20a 100644
--- a/tests/__tests__/validate-plugin.js
+++ b/tests/__tests__/validate-plugin.js
@@ -5,14 +5,24 @@ import { render, fireEvent } from '@testing-library/vue'
import Validate from './components/Validate'
test('can validate using plugin', async () => {
- const { getByPlaceholderText, queryByTestId } = render(Validate, {}, vue =>
- vue.use(VeeValidate, { events: 'blur' })
+ // The third argument of `render` is a callback function that receives the
+ // Vue instance as a parameter. This way, we can register plugins such as
+ // VeeValidate.
+ const { getByPlaceholderText, queryByTestId, getByTestId } = render(
+ Validate,
+ {},
+ vue => vue.use(VeeValidate, { events: 'blur' })
)
+ // Assert error messages are not in the DOM when rendering the component.
+ expect(queryByTestId('username-errors')).toBeNull()
+
const usernameInput = getByPlaceholderText('Username...')
await fireEvent.touch(usernameInput)
- expect(queryByTestId('username-errors')).toHaveTextContent(
+ // After "touching" the input (focusing and blurring), validation error
+ // should appear.
+ expect(getByTestId('username-errors')).toHaveTextContent(
'The username field is required.'
)
})
diff --git a/tests/__tests__/vue-router.js b/tests/__tests__/vue-router.js
index 76fd0dee..e4509796 100644
--- a/tests/__tests__/vue-router.js
+++ b/tests/__tests__/vue-router.js
@@ -15,16 +15,20 @@ const routes = [
afterEach(cleanup)
test('full app rendering/navigating', async () => {
+ // Notice how we pass a `routes` object to our render function.
const { queryByTestId } = render(App, { routes })
- // normally I'd use a data-testid, but just wanted to show this is also possible
expect(queryByTestId('location-display')).toHaveTextContent('/')
+
await fireEvent.click(queryByTestId('about-link'))
expect(queryByTestId('location-display')).toHaveTextContent('/about')
})
test('setting initial route', () => {
+ // The callback function receives three parameters: the Vue instance where
+ // the component is mounted, the store instance (if any) and the router
+ // object.
const { queryByTestId } = render(App, { routes }, (vue, store, router) => {
router.push('/about')
})