Skip to content

Commit

Permalink
docs: add more expects and guides (#1236)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed May 5, 2022
1 parent 3d8e846 commit 93422cb
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 7 deletions.
8 changes: 8 additions & 0 deletions docs/.vitepress/config.ts
Expand Up @@ -132,6 +132,14 @@ export default defineConfig({
text: 'Test Context',
link: '/guide/test-context',
},
{
text: 'Extending Matchers',
link: '/guide/extending-matchers',
},
{
text: 'Snapshot Serializer',
link: '/guide/snapshot-serializer',
},
{
text: 'IDE Integration',
link: '/guide/ide',
Expand Down
109 changes: 102 additions & 7 deletions docs/api/index.md
Expand Up @@ -9,7 +9,7 @@ type TestFunction = () => Awaitable<void>
When a test function returns a promise, the runner will wait until it is resolved to collect async expectations. If the promise is rejected, the test will fail.
::: tip
::: tip
In Jest, `TestFunction` can also be of type `(done: DoneCallback) => void`. If this form is used, the test will not be concluded until `done` is called. You can achieve the same using an `async` function, see the [Migration guide Done Callback section](../guide/migration#done-callback).
:::
Expand Down Expand Up @@ -957,7 +957,7 @@ When you use `test` in the top level of file, they are collected as part of the

test('matches inline snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) }
// Vitest will updates following content when updating the snapshot
// Vitest will update following content when updating the snapshot
expect(data).toMatchInlineSnapshot(`
{
"foo": Set {
Expand All @@ -969,10 +969,22 @@ When you use `test` in the top level of file, they are collected as part of the
})
```

<!--

### toThrowErrorMatchingSnapshot

### toThrowErrorMatchingInlineSnapshot -->
- **Type:** `(snapshot?: string) => void`

The same as `[toMatchSnapshot](#toMatchSnapshot)`, but expects the same value as `[toThrowError](#toThrowError)`.

If the function throws an `Error`, the snapshot will be the error message. Otherwise, snapshot will be the value thrown by the function.

### toThrowErrorMatchingInlineSnapshot

- **Type:** `(snapshot?: string) => void`

The same as `[toMatchInlineSnapshot](#toMatchInlineSnapshot)`, but expects the same value as `[toThrowError](#toThrowError)`.

If the function throws an `Error`, the snapshot will be the error message. Otherwise, snapshot will be the value thrown by the function.

### toHaveBeenCalled

Expand Down Expand Up @@ -1203,6 +1215,27 @@ When you use `test` in the top level of file, they are collected as part of the
})
```

### toSatisfy

- **Type:** `(predicate: (value: any) => boolean) => Awaitable<void>`

This assertion checks if a value satisfies a certain predicate.

```ts
describe('toSatisfy()', () => {
const isOdd = (value: number) => value % 2 !== 0

it('pass with 0', () => {
expect(1).toSatisfy(isOdd)
})

it('pass with negotiation', () => {
expect(2).not.toSatisfy(isOdd)
})
})
```
<!-- toSatisfy -->

### resolves

- **Type:** `Promisify<Assertions>`
Expand Down Expand Up @@ -1337,10 +1370,72 @@ When you use `test` in the top level of file, they are collected as part of the
### expect.stringContaining
### expect.not.stringContaining
### expect.stringMatching
### expect.not.stringMatching
### expect.not.stringMatching -->

### expect.addSnapshotSerializer
### expect.extend -->

- **Type:** `(plugin: PrettyFormatPlugin) => void`

This method adds custom serializers that are called when creating a snapshot. This is advanced feature - if you want to know more, please read a [guide on custom serializers](/guide/snapshot-serializer).

If you are adding custom serializers, you should call this method inside [`setupFiles`](/config/#setupFiles). This will affect every snapshot.

:::tip
If you previously used Vue CLI with Jest, you might want to install [jest-serializer-vue](https://www.npmjs.com/package/jest-serializer-vue). Otherwise, your snapshots will be wrapped in a string, which cases `"` to be escaped.
:::

### expect.extend

- **Type:** `(matchers: MatchersObject) => void`

You can extend default matchers with your own. This function is used to extend the matchers object with custom matchers.

When you define matchers that way, you also create asymmetric matchers that can be used like `expect.stringContaining`.

```ts
import { expect, test } from 'vitest'

test('custom matchers', () => {
expect.extend({
toBeFoo: (received, expected) => {
if (received !== 'foo') {
return {
message: () => `expected ${received} to be foo`,
pass: false,
}
}
},
})

expect('foo').toBeFoo()
expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() })
})
```

> If you want your matchers to appear in every test, you should call this method inside [`setupFiles`](/config/#setupFiles).
This function is compatible with Jest's `expect.extend`, so any library that uses it to create custom matchers will work with Vitest.

If you are using TypeScript, you can extend default Matchers interface with the code bellow:

```ts
interface CustomMatchers<R = unknown> {
toBeFoo(): R
}

declare global {
namespace Vi {
interface Assertion extends CustomMatchers {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
}
```

> Note: augmenting jest.Matchers interface will also work.
:::tip
If you want to know more, checkout [guide on extending matchers](/guide/extending-matchers).
:::

## Setup and Teardown

Expand Down Expand Up @@ -1375,7 +1470,7 @@ These functions allow you to hook into the life cycle of tests to avoid repeatin
beforeEach(async () => {
// called once before all tests run
await prepareSomething()

// clean up function, called once after all tests run
return async () => {
await resetSomething()
Expand Down
68 changes: 68 additions & 0 deletions docs/guide/extending-matchers.md
@@ -0,0 +1,68 @@
# Extending Matchers

Since Vitest is compatible with both Chai and Jest, you can use either `chai.use` API or `expect.extend`, whichever you prefer.

This guide will explore extending matchers with `expect.extend`. If you are interested in Chai API, check [their guide](https://www.chaijs.com/guide/plugins/).

To extend default matchers, call `expect.extend` with an object containing your matchers.

```ts
expect.extend({
toBeFoo(received, expected) {
const { isNot } = this
return {
// do not alter your "pass" based on isNot. Vitest does it for you
pass: received === 'foo',
message: () => `${received} is${isNot ? ' not' : ''} foo`
}
}
})
```

The return value of a matcher should be compatible with the following interface:

```ts
interface MatcherResult {
pass: boolean
message: () => string
// If you pass these, they will automatically appear inside a diff,
// if the matcher will not pass, so you don't need to print diff yourself
actual?: unknown
expected?: unknown
}
```

::: warning
If you create an asynchronous matcher, don't forget to `await` the result (`await expect('foo').toBeFoo()`) in the test itself.
:::

The first argument inside a matchers function is received value (the one inside `expect(received)`). The rest are arguments passed directly to the matcher.

Matcher function have access to `this` context with the following properties:

- `isNot`

Returns true, if matcher was called on `not` (`expect(received).not.toBeFoo()`).

- `promise`

If matcher was called on `resolved/rejected`, this value will contain the name of modifier. Otherwise, it will be an empty string.

- `equals`

This is utility function that allows you to compare two values. It will return `true` if values are equal, `false` otherwise. This function is used internally for almost every matcher.
It supports objects with asymmetric matchers by default.

- `utils`

This contains a set of utility functions that you can use to display messages.

`this` context also contains information about the current test. You can also get it by calling `expect.getState()`. The most useful properties are:

- `currentTestName`

Full name of the current test (including describe block).

- `testPath`

Path to the current test.
43 changes: 43 additions & 0 deletions docs/guide/snapshot-serializer.md
@@ -0,0 +1,43 @@
# Snapshot Serializer

You can add your own logic to alter how your snapshots are serialized. Like Jest, Vitest has default serializers for built-in JavaScript types, HTML elements, ImmutableJS and for React elements.

Example serializer module:

```ts
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` is a function that serializes a value using existing plugins.
return `Pretty foo: ${printer(val.foo)}`
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo')
},
})
```

After adding a test like this:

```ts
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
}

expect(bar).toMatchSnapshot()
})
```

You will get the following snapshot:

```
Pretty foo: Object {
"x": 1,
"y": 2,
}
```

We are using Jest's `pretty-format` for serializing snapshots. You can read more about it here: [pretty-format](https://github.com/facebook/jest/blob/main/packages/pretty-format/README.md#serialize).

0 comments on commit 93422cb

Please sign in to comment.