Skip to content

Feedback #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jun 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 22 additions & 8 deletions src/guide/conditional-rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,46 @@ In the `<Nav>` component, a link to the user's profile is shown. In addition, if

## Using `get()`

`wrapper` has a `get()` method for asserting an element exists. It uses [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) syntax. We can assert the profile link is present using `get()`:
`wrapper` has a `get()` method that searches for an existing element. It uses [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector) syntax.

We can assert the profile link content by using `get()`:

```js
test('renders a profile link', () => {
const wrapper = mount(Nav)
wrapper.get('#profile')

// Here we are implicitly asserting that the
// element #profile exists.
const profileLink = wrapper.get('#profile')

expect(profileLink.text()).toEqual('My Profile')
Comment on lines +41 to +45
Copy link
Member Author

@afontcu afontcu Jun 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They didn't get why this test had no assertions

})
```

If `get()` does not return an element matching the selector, it will raise an error, and your test will fail.

## Using `find()` and `exists()`

`get()` works for asserting elements do exist, because it throws an error when it can't find an element, you can't use it to assert elements don't exist. For this, we can use `find()` and `exists()`. The next test asserts that if `admin` is `false` (which is it by default), the admin link is not present:
`get()` works for asserting elements do exist. However, as we mentioned, it throws an error when it can't find an element, so you can't use it to assert whether if elements exist.

To do so, we use `find()` and `exists()`. The next test asserts that if `admin` is `false` (which is it by default), the admin link is not present:

```js
test('does not render an admin link', () => {
const wrapper = mount(Nav)
const adminLink = wrapper.find('#admin')
expect(adminLink.exists()).toBe(false)

// Using `wrapper.get` would throw and make the test fail.
expect(wrapper.find('#admin').exists()).toBe(false)
})
```

Notice we are calling `exists()` on the value returned from `.find()`? `find()`, like `mount()`, also returns a wrapper, similar to `mount()`. `mount()` has a few extra methods, because it's wrapping a Vue component, and `find()` only returns a regular DOM node, but many of the methods are shared between both. Some other methods include `classes()`, which gets the classes a DOM node has, and `trigger()` for simulating user interaction. You can find a list of methods supported [here](/api/#wrapper-methods).

## Using `data`

The final test is to assert that the admin link is rendered when `admin` is `true`. It's default by `false`, but we can override that using the second argument to `mount()`, the [`mounting options`](/api/#mount-options). For `data`, we use the aptly named `data` option:
The final test is to assert that the admin link is rendered when `admin` is `true`. It's `false` by default, but we can override that using the second argument to `mount()`, the [`mounting options`](/api/#mount-options).

For `data`, we use the aptly named `data` option:

```js
test('renders an admin link', () => {
Expand All @@ -68,8 +80,10 @@ test('renders an admin link', () => {
}
}
})
const adminLink = wrapper.find('#admin')
expect(adminLink.exists()).toBe(true)

// Again, by using `get()` we are implicitly asserting that
// the element exists.
expect(wrapper.get('#admin').text()).toEqual('Admin')
})
```

Expand Down
138 changes: 89 additions & 49 deletions src/guide/event-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ Vue components interact with each other via props and by emitting events by call

## The Counter component

Here is a simple `<Counter>` component. It has a button that increments a `count` property when it is clicked. It also emits an `increment` event with the latest value of `count` by calling `this.$emit('increment', this.count)`:
Here is a simple `<Counter>` component. It features a button that, when clicked, increments an internal count variable and emits its value:

```js
const Counter = {
template: `
<button @click="handleClick">Increment</button>
<div>Count: {{ count }}</div>
`,
template: '<button @click="handleClick">Increment</button>',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The <div>Count: {{ count }}</div> part is not used during tests, so I removed it

data() {
return {
count: 0
Expand All @@ -26,84 +23,127 @@ const Counter = {
}
```

To fully test this component, we should verify that when the button is clicked, the `count` shown in the template is updated. We can also verify that an `increment` event with the latest `count` value is emitted. We will start with the latter.
To fully test this component, we should verify that an `increment` event with the latest `count` value is emitted.

## `VueWrapper.emitted()`
## Asserting the emitted events
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here people got lost because we moved from explaining thing from a "user perspective" to an "API perspective"


`VueWrapper` has an `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:
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:

```js
test('emits and event with count when clicked', () => {
test('emits an event when clicked', () => {
const wrapper = mount(Counter)
wrapper.find('button').trigger('click')

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

console.log(wrapper.emitted()) // { increment: [ [ 1 ], [ 2 ], [ 3 ] ] }
expect(wrapper.emitted()).toHaveProperty('increment')
})
```

> If you haven't seen `trigger()` before, don't worry. It's used to simulate user interaction. You can learn more in [Forms](/guide/forms).
> If you haven't seen `trigger()` before, don't worry. It's used to simulate user interaction. You can learn more in [Forms](/guide/forms).

The output of `emitted()` might be a little confusing at first. In this test, we are only interested in the `increment` event, so we can access that with `wrapper.emitted('increment')`. This would return `[ [ 1 ], [ 2 ], [ 3 ] ]`. Let's format it a bit more nicely to see what's going on:
The first thing to notice is that `emitted()` returns an object, where each key matches an emitted event. In this case, `increment`.

```js
// console.log(wrapper.emitted('increment'))
[
[ 1 ], // first time is it called, `count` is 1
[ 2 ], // second time is it called, `count` is 2
[ 3 ], // third time is it called, `count` is 3
]
This test should pass. We made sure we emitted an event with the appropriate name.

## Asserting the arguments of the event

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.

Our next step is to assert that the event contains the `count` value. We do so by passing an argument to `emitted()`.

```js {9}
test('emits an event with count when clicked', () => {
const wrapper = mount(Counter)

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

// `emitted()` accepts an argument. It returns an array with all the
// occurrences of `this.$emit('increment')`.
const incrementEvent = wrapper.emitted('increment')

// We have "clicked" twice, so the array of `increment` should
// have two values.
expect(incrementEvent).toHaveLength(2)

// Assert the result of the first click.
// Notice that the value is an array.
expect(incrementEvent[0]).toEqual([1])

// Then, the result of the second one.
expect(incrementEvent[1]).toEqual([2])
})
```

Each entry in the array represents one `increment` event that was emitted. Each entry in the array represents an argument to `this.$emit()`. For example, if the code was `this.$emit('increment, this.count, { status: 'success' })`, and the button was clicked twice, `emitted('increment')` would be:
Let's recap and break down the output of `emitted()`. Each of these keys contains the different values emitted during the test:

```js
[
[ // first `increment` event
1, // first argument
{ status: 'success' } // second argument
],
[ // second `increment` event
2, // first argument
{ status: 'success' } // second argument
]
// console.log(wrapper.emitted('increment'))
[
[ 1 ], // first time it is called, `count` is 1
[ 2 ], // second time it is called, `count` is 2
]
```

Each element in the array corresponds to an argument in `this.$emit`.

## Writing a Test
## Asserting complex events

Now we know that `emitted('eventName')` captures the events, we can write a simple test to assert an `increment` event is emitted:

```js
test('emits and event with count when clicked', () => {
const wrapper = mount(Counter)
Imagine that now our `<Counter>` 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:

wrapper.find('button').trigger('click')
```js {12-15}
const Counter = {
template: `<button @click="handleClick">Increment</button>`,
data() {
return {
count: 0
}
},
methods: {
handleClick() {
this.count += 1

expect(wrapper.emitted()).toHaveProperty('increment')
})
this.$emit('increment', {
count: this.count,
isEven: this.count % 2 === 0
})
}
}
}
```

This is good - but we can do better. With the knowledge that `increment` will return an array, where each element represents an event and it's arguments, we can fully test the component by making assertions against the arguments passed when `this.$emit('increment')` is called:
As we did before, we need to trigger the `click` event on the `<button>` element. Then, we use `emitted('increment')` to make sure the right values are emitted.

```js
test('emits and event with count when clicked', () => {
test('emits an event with count when clicked', () => {
const wrapper = mount(Counter)

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

expect(wrapper.emitted('increment')).toHaveLength(3)
expect(wrapper.emitted('increment')[0]).toEqual([1])
expect(wrapper.emitted('increment')[1]).toEqual([2])
expect(wrapper.emitted('increment')[2]).toEqual([3])
// We have "clicked" twice, so the array of `increment` should
// have two values.
expect(wrapper.emitted('increment')).toHaveLength(2)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced clicks from 3 to 2 because otherwise the snippet grows too large


// Then, we can make sure each element of `wrapper.emitted('increment')`
// contains an array with the expected object.
expect(wrapper.emitted('increment')[0]).toEqual([
{
count: 1,
isEven: false
}
])

expect(wrapper.emitted('increment')[1]).toEqual([
{
count: 2,
isEven: true
}
])
})
```

Testing complex event payloads such as Objects is no different from testing simple values such as numbers or strings.

## Composition API

If you are using the Composition API, you will be calling `context.emit()` instead of `this.$emit()`. `emitted()` captures events from both, so you can test your component using the same techniques described here.
Expand All @@ -112,4 +152,4 @@ If you are using the Composition API, you will be calling `context.emit()` inste

- Use `emitted()` to access the events emitted from a Vue component.
- `emitted(eventName)` returns an array, where each element represents one event emitted.
- Arguments are stored in `emitted(eventName)[index]` in an array, in the same order they are emitted.
- Arguments are stored in `emitted(eventName)[index]` in an array in the same order they are emitted.
Loading