Skip to content

Commit

Permalink
Update readme with examples
Browse files Browse the repository at this point in the history
  • Loading branch information
dfcook committed Apr 7, 2018
1 parent 7d3ad72 commit 295c870
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 50 deletions.
54 changes: 46 additions & 8 deletions README.md
@@ -1,7 +1,7 @@
<div align="center">
<h1>vue-testing-library</h1>

<p>Port of the ultra-low surface area testing library <a href="https://github.com/kentcdodds/react-testing-library/">react-testing-library</a> to <a href="https://github.com/vuejs/vue">Vue.js</a> using <a href="https://github.com/vuejs/vue-test-utils">@vue/test-utils</a></p>
<p>Lightweight adapter allowing <a href="https://github.com/kentcdodds/dom-testing-library/">dom-testing-library</a> to be used to test <a href="https://github.com/vuejs/vue">Vue.js</a> components built on top of <a href="https://github.com/vuejs/vue-test-utils">@vue/test-utils</a></p>

</div>

Expand All @@ -16,14 +16,15 @@

## This library

The `vue-testing-library` is a very light-weight solution for testing Vue
components. It provides light utility functions on top of `@vue/test-utils`, in a way that encourages better testing practices.
It's primary guiding principle is:

The more your tests resemble the way your software is used, the more confidence they can give you.

The intention is to closely track react-test-utils and its capabilities. To this end it uses the marvellous `dom-testing-utils` library for querying the DOM.
The `vue-testing-library` is a an adapter that enables Vue testing using the framework-agnostic DOM testing library `dom-testing-library`

* [Installation](#installation)
* [Usage](#usage)
* [`render`](#render)
* [`Simulate`](#simulate)
* [`wait`](#wait)
* [Examples](#examples)
* [LICENSE](#license)

## Installation

Expand Down Expand Up @@ -108,6 +109,43 @@ test('should render HelloWorld', () => {

```

### render

The `render` function takes up to 3 parameters and returns an object with some helper methods

1. Component - the Vue component to be tested.
2. RenderOptions - an object containing additional information to be passed to @vue/test-utils mount. This can be:
* props - The component props to be passed to TestComponent
* store - The object definition of a Vuex store, if present `render` will configure a Vuex store and pass to mount.
* routes - A set of routes, if present render will configure VueRouter and pass to mount.
3. configurationCb - A callback to be called passing the Vue instance when created. This allows 3rd party plugins to be installed prior to mount.

### Simulate

Lightweight wrapper around DOM element methods

### wait

When in need to wait for non-deterministic periods of time you can use `wait`,
to wait for your expectations to pass. The `wait` function is a small wrapper
around the
[`wait-for-expect`](https://github.com/TheBrainFamily/wait-for-expect) module.

Waiting can be very important in Vue components, @vue/test-utils has succeeded in making the majority of updates happen
synchronously however there are occasions when wait will allow the DOM to update. For example, see [`here`](https://github.com/dfcook/vue-testing-library/tree/master/tests/__tests__/validate-plugin.js)

## Examples

You'll find examples of testing with different libraries in
[the test directory](https://github.com/dfcook/vue-testing-library/tree/master/tests/__tests__).
Some included are:

* [`vuex`](https://github.com/dfcook/vue-testing-library/tree/master/tests/__tests__/vuex.js)
* [`vue-router`](https://github.com/dfcook/vue-testing-library/tree/master/__tests__/vue-router.js)
* [`vee-validate`](https://github.com/dfcook/vue-testing-library/tree/master/tests/__tests__/validate-plugin.js)

Feel free to contribute more!

## LICENSE

MIT
23 changes: 5 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions src/Simulate.js
Expand Up @@ -12,14 +12,18 @@ const simulate = (event, elem, ...params) => {

const click = simulate.bind(null, 'click')
const submit = simulate.bind(null, 'submit')
const change = simulate.bind(null, 'change')
const focus = simulate.bind(null, 'focus')
const blur = simulate.bind(null, 'blur')

const touch = (elem) => {
focus(elem)
blur(elem)
}

export default {
blur,
click,
submit,
change,
focus,
blur
submit,
touch
}
2 changes: 1 addition & 1 deletion tests/__tests__/components/Login.vue
Expand Up @@ -34,7 +34,7 @@ export default {
password: ''
}
},
methods: {
methods: {
submit () {
this.onSubmit({
username: this.username,
Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/components/StopWatch.vue
@@ -1,6 +1,6 @@
<template>
<div>
<span>{{ lapse }}ms</span>
<span data-testid="elapsed">{{ lapse }}ms</span>
<button @click="handleRunClick" data-testid="start-stop-button">
{{ running ? 'Stop' : 'Start' }}
</button>
Expand Down
6 changes: 1 addition & 5 deletions tests/__tests__/components/Validate.vue
@@ -1,5 +1,5 @@
<template>
<form @submit.prevent="submit">
<form>
<label for="username-input">Username</label>
<input
id="username-input"
Expand Down Expand Up @@ -31,10 +31,6 @@ export default {
username: '',
password: ''
}
},
methods: {
submit () {
}
}
}
</script>
2 changes: 1 addition & 1 deletion tests/__tests__/end-to-end.js
Expand Up @@ -2,7 +2,7 @@ import { render, wait } from '../../src'
import EndToEnd from './components/EndToEnd'

test('it waits for the data to be loaded', async () => {
const { html, queryByText, queryByTestId } = render(EndToEnd)
const { queryByText, queryByTestId } = render(EndToEnd)

expect(queryByText('Loading...')).toBeTruthy()

Expand Down
5 changes: 2 additions & 3 deletions tests/__tests__/form.js
@@ -1,11 +1,11 @@
import Vue from 'vue'
import { render, Simulate, wait } from '../../src'
import { html, render, Simulate, wait } from '../../src'
import Login from './components/Login'

test('login form submits', async () => {
const fakeUser = {username: 'jackiechan', password: 'hiya! 🥋'}
const handleSubmit = jest.fn()
const {updateState, getByLabelText, getByText} = render(
const { updateState, getByLabelText, getByText } = render(
Login, { props: { onSubmit: handleSubmit } }
)

Expand All @@ -15,7 +15,6 @@ test('login form submits', async () => {

// Act - this is waiting on an issue in @vue/test-utils to allow v-model to be updated by
// changes to DOM elements

updateState(fakeUser)

// NOTE: in jsdom, it's not possible to trigger a form submission
Expand Down
28 changes: 22 additions & 6 deletions tests/__tests__/stopwatch.js
@@ -1,18 +1,34 @@
import Vue from 'vue'
import StopWatch from './components/StopWatch.vue'
import { render, select, Simulate } from '../../src'

const wait = time => new Promise(resolve => setTimeout(resolve, time))
import { render, select, wait, Simulate } from '../../src'

test('unmounts a component', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {})

const { unmount, isUnmounted, getByText, wrapper } = render(StopWatch)
const { unmount, isUnmounted, getByText } = render(StopWatch)
Simulate.click(getByText('start'))

unmount()
await wait()

unmount()
expect(isUnmounted()).toBe(true)

await wait()
await wait()
expect(console.error).not.toHaveBeenCalled()
})

test('updates component state', async () => {
const { getByTestId, getByText } = render(StopWatch)

const startButton = getByText('start')
const elapsedTime = getByTestId('elapsed')

expect(elapsedTime.textContent).toBe('0ms')

Simulate.click(startButton)
await wait()
Simulate.click(startButton)

expect(elapsedTime.textContent).not.toBe('0ms')
})

3 changes: 1 addition & 2 deletions tests/__tests__/validate-plugin.js
Expand Up @@ -9,8 +9,7 @@ test('can validate using plugin', async () => {
vue => vue.use(VeeValidate, { events: 'blur' }))

const usernameInput = getByPlaceholderText('Username...')
Simulate.focus(usernameInput)
Simulate.blur(usernameInput)
Simulate.touch(usernameInput)

await wait()

Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/vue-router.js
Expand Up @@ -11,7 +11,7 @@ const routes = [
]

test('full app rendering/navigating', () => {
const { wrapper, queryByTestId } = render(App, { routes })
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').textContent).toBe('/')
Simulate.click(queryByTestId('about-link'))
Expand Down

0 comments on commit 295c870

Please sign in to comment.