Skip to content

Commit 63bf909

Browse files
authored
docs: clean up expect.extend documentation (#13788)
1 parent ea5e47e commit 63bf909

File tree

6 files changed

+146
-160
lines changed

6 files changed

+146
-160
lines changed

docs/ExpectAPI.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -203,40 +203,7 @@ expect.extend({
203203
});
204204
```
205205

206-
:::note
207-
208-
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
209-
210-
```ts title="toBeWithinRange.ts"
211-
expect.extend({
212-
toBeWithinRange(received: number, floor: number, ceiling: number) {
213-
// ...
214-
},
215-
});
216-
declare global {
217-
namespace jest {
218-
interface Matchers<R> {
219-
toBeWithinRange(a: number, b: number): R;
220-
}
221-
}
222-
}
223-
```
224-
225-
If you want to move the typings to a separate file (e.g. `types/jest/index.d.ts`), you may need to an export, e.g.:
226-
227-
```ts
228-
interface CustomMatchers<R = unknown> {
229-
toBeWithinRange(floor: number, ceiling: number): R;
230-
}
231-
declare global {
232-
namespace jest {
233-
interface Expect extends CustomMatchers {}
234-
interface Matchers<R> extends CustomMatchers<R> {}
235-
interface InverseAsymmetricMatchers extends CustomMatchers {}
236-
}
237-
}
238-
export {};
239-
```
206+
:::
240207

241208
#### Async Matchers
242209

website/versioned_docs/version-28.x/ExpectAPI.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ test('numeric ranges', () => {
7171

7272
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
7373

74-
:::
75-
7674
```ts
7775
expect.extend({
7876
toBeWithinRange(received, floor, ceiling) {
@@ -106,6 +104,8 @@ declare global {
106104
export {};
107105
```
108106

107+
:::
108+
109109
#### Async Matchers
110110

111111
`expect.extend` also supports async matchers. Async matchers return a Promise so you will need to await the returned value. Let's use an example matcher to illustrate the usage of them. We are going to implement a matcher called `toBeDivisibleByExternalValue`, where the divisible number is going to be pulled from an external source.

website/versioned_docs/version-29.0/ExpectAPI.md

Lines changed: 140 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ When you're writing tests, you often need to check that values meet certain cond
77

88
For additional Jest matchers maintained by the Jest Community check out [`jest-extended`](https://github.com/jest-community/jest-extended).
99

10+
import TypeScriptExamplesNote from './_TypeScriptExamplesNote.md';
11+
12+
<TypeScriptExamplesNote />
13+
1014
## Methods
1115

1216
import TOCInline from '@theme/TOCInline';
@@ -37,52 +41,166 @@ The argument to `expect` should be the value that your code produces, and any ar
3741

3842
You can use `expect.extend` to add your own matchers to Jest. For example, let's say that you're testing a number utility library and you're frequently asserting that numbers appear within particular ranges of other numbers. You could abstract that into a `toBeWithinRange` matcher:
3943

40-
```js
44+
```js tab={"span":3} title="toBeWithinRange.js"
45+
import {expect} from '@jest/globals';
46+
47+
function toBeWithinRange(actual, floor, ceiling) {
48+
if (
49+
typeof actual !== 'number' ||
50+
typeof floor !== 'number' ||
51+
typeof ceiling !== 'number'
52+
) {
53+
throw new Error('These must be of type number!');
54+
}
55+
56+
const pass = actual >= floor && actual <= ceiling;
57+
if (pass) {
58+
return {
59+
message: () =>
60+
`expected ${this.utils.printReceived(
61+
actual,
62+
)} not to be within range ${this.utils.printExpected(
63+
`${floor} - ${ceiling}`,
64+
)}`,
65+
pass: true,
66+
};
67+
} else {
68+
return {
69+
message: () =>
70+
`expected ${this.utils.printReceived(
71+
actual,
72+
)} to be within range ${this.utils.printExpected(
73+
`${floor} - ${ceiling}`,
74+
)}`,
75+
pass: false,
76+
};
77+
}
78+
}
79+
4180
expect.extend({
42-
toBeWithinRange(received, floor, ceiling) {
43-
const pass = received >= floor && received <= ceiling;
81+
toBeWithinRange,
82+
});
83+
```
84+
85+
```js title="__tests__/ranges.test.js"
86+
import {expect, test} from '@jest/globals';
87+
import '../toBeWithinRange';
88+
89+
test('is within range', () => expect(100).toBeWithinRange(90, 110));
90+
91+
test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));
92+
93+
test('asymmetric ranges', () => {
94+
expect({apples: 6, bananas: 3}).toEqual({
95+
apples: expect.toBeWithinRange(1, 10),
96+
bananas: expect.not.toBeWithinRange(11, 20),
97+
});
98+
});
99+
```
100+
101+
```ts title="toBeWithinRange.d.ts"
102+
// optionally add a type declaration, e.g. it enables autocompletion in IDEs
103+
declare module 'expect' {
104+
interface AsymmetricMatchers {
105+
toBeWithinRange(floor: number, ceiling: number): void;
106+
}
107+
interface Matchers<R> {
108+
toBeWithinRange(floor: number, ceiling: number): R;
109+
}
110+
}
111+
112+
export {};
113+
```
114+
115+
```ts tab={"span":2} title="toBeWithinRange.ts"
116+
import {expect} from '@jest/globals';
117+
import type {MatcherFunction} from 'expect';
118+
119+
const toBeWithinRange: MatcherFunction<[floor: unknown, ceiling: unknown]> =
120+
// `floor` and `ceiling` get types from the line above
121+
// it is recommended to type them as `unknown` and to validate the values
122+
function (actual, floor, ceiling) {
123+
if (
124+
typeof actual !== 'number' ||
125+
typeof floor !== 'number' ||
126+
typeof ceiling !== 'number'
127+
) {
128+
throw new Error('These must be of type number!');
129+
}
130+
131+
const pass = actual >= floor && actual <= ceiling;
44132
if (pass) {
45133
return {
46134
message: () =>
47-
`expected ${received} not to be within range ${floor} - ${ceiling}`,
135+
// `this` context will have correct typings
136+
`expected ${this.utils.printReceived(
137+
actual,
138+
)} not to be within range ${this.utils.printExpected(
139+
`${floor} - ${ceiling}`,
140+
)}`,
48141
pass: true,
49142
};
50143
} else {
51144
return {
52145
message: () =>
53-
`expected ${received} to be within range ${floor} - ${ceiling}`,
146+
`expected ${this.utils.printReceived(
147+
actual,
148+
)} to be within range ${this.utils.printExpected(
149+
`${floor} - ${ceiling}`,
150+
)}`,
54151
pass: false,
55152
};
56153
}
57-
},
154+
};
155+
156+
expect.extend({
157+
toBeWithinRange,
58158
});
59159

60-
test('numeric ranges', () => {
61-
expect(100).toBeWithinRange(90, 110);
62-
expect(101).not.toBeWithinRange(0, 100);
160+
declare module 'expect' {
161+
interface AsymmetricMatchers {
162+
toBeWithinRange(floor: number, ceiling: number): void;
163+
}
164+
interface Matchers<R> {
165+
toBeWithinRange(floor: number, ceiling: number): R;
166+
}
167+
}
168+
```
169+
170+
```ts tab title="__tests__/ranges.test.ts"
171+
import {expect, test} from '@jest/globals';
172+
import '../toBeWithinRange';
173+
174+
test('is within range', () => expect(100).toBeWithinRange(90, 110));
175+
176+
test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));
177+
178+
test('asymmetric ranges', () => {
63179
expect({apples: 6, bananas: 3}).toEqual({
64180
apples: expect.toBeWithinRange(1, 10),
65181
bananas: expect.not.toBeWithinRange(11, 20),
66182
});
67183
});
68184
```
69185

70-
:::note
186+
:::tip
71187

72-
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
188+
The type declaration of the matcher can live in a `.d.ts` file or in an imported `.ts` module (see JS and TS examples above respectively). If you keep the declaration in a `.d.ts` file, make sure that it is included in the program and that it is a valid module, i.e. it has at least an empty `export {}`.
73189

74-
```ts
75-
interface CustomMatchers<R = unknown> {
76-
toBeWithinRange(floor: number, ceiling: number): R;
77-
}
190+
:::
78191

79-
declare global {
80-
namespace jest {
81-
interface Expect extends CustomMatchers {}
82-
interface Matchers<R> extends CustomMatchers<R> {}
83-
interface InverseAsymmetricMatchers extends CustomMatchers {}
84-
}
85-
}
192+
:::tip
193+
194+
Instead of importing `toBeWithinRange` module to the test file, you can enable the matcher for all tests by moving the `expect.extend` call to a [`setupFilesAfterEnv`](Configuration.md/#setupfilesafterenv-array) script:
195+
196+
```js
197+
import {expect} from '@jest/globals';
198+
// remember to export `toBeWithinRange` as well
199+
import {toBeWithinRange} from './toBeWithinRange';
200+
201+
expect.extend({
202+
toBeWithinRange,
203+
});
86204
```
87205

88206
:::

website/versioned_docs/version-29.1/ExpectAPI.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -203,40 +203,7 @@ expect.extend({
203203
});
204204
```
205205

206-
:::note
207-
208-
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
209-
210-
```ts title="toBeWithinRange.ts"
211-
expect.extend({
212-
toBeWithinRange(received: number, floor: number, ceiling: number) {
213-
// ...
214-
},
215-
});
216-
declare global {
217-
namespace jest {
218-
interface Matchers<R> {
219-
toBeWithinRange(a: number, b: number): R;
220-
}
221-
}
222-
}
223-
```
224-
225-
If you want to move the typings to a separate file (e.g. `types/jest/index.d.ts`), you may need to an export, e.g.:
226-
227-
```ts
228-
interface CustomMatchers<R = unknown> {
229-
toBeWithinRange(floor: number, ceiling: number): R;
230-
}
231-
declare global {
232-
namespace jest {
233-
interface Expect extends CustomMatchers {}
234-
interface Matchers<R> extends CustomMatchers<R> {}
235-
interface InverseAsymmetricMatchers extends CustomMatchers {}
236-
}
237-
}
238-
export {};
239-
```
206+
:::
240207

241208
#### Async Matchers
242209

website/versioned_docs/version-29.2/ExpectAPI.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -203,40 +203,7 @@ expect.extend({
203203
});
204204
```
205205

206-
:::note
207-
208-
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
209-
210-
```ts title="toBeWithinRange.ts"
211-
expect.extend({
212-
toBeWithinRange(received: number, floor: number, ceiling: number) {
213-
// ...
214-
},
215-
});
216-
declare global {
217-
namespace jest {
218-
interface Matchers<R> {
219-
toBeWithinRange(a: number, b: number): R;
220-
}
221-
}
222-
}
223-
```
224-
225-
If you want to move the typings to a separate file (e.g. `types/jest/index.d.ts`), you may need to an export, e.g.:
226-
227-
```ts
228-
interface CustomMatchers<R = unknown> {
229-
toBeWithinRange(floor: number, ceiling: number): R;
230-
}
231-
declare global {
232-
namespace jest {
233-
interface Expect extends CustomMatchers {}
234-
interface Matchers<R> extends CustomMatchers<R> {}
235-
interface InverseAsymmetricMatchers extends CustomMatchers {}
236-
}
237-
}
238-
export {};
239-
```
206+
:::
240207

241208
#### Async Matchers
242209

website/versioned_docs/version-29.3/ExpectAPI.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -203,40 +203,7 @@ expect.extend({
203203
});
204204
```
205205

206-
:::note
207-
208-
In TypeScript, when using `@types/jest` for example, you can declare the new `toBeWithinRange` matcher in the imported module like this:
209-
210-
```ts title="toBeWithinRange.ts"
211-
expect.extend({
212-
toBeWithinRange(received: number, floor: number, ceiling: number) {
213-
// ...
214-
},
215-
});
216-
declare global {
217-
namespace jest {
218-
interface Matchers<R> {
219-
toBeWithinRange(a: number, b: number): R;
220-
}
221-
}
222-
}
223-
```
224-
225-
If you want to move the typings to a separate file (e.g. `types/jest/index.d.ts`), you may need to an export, e.g.:
226-
227-
```ts
228-
interface CustomMatchers<R = unknown> {
229-
toBeWithinRange(floor: number, ceiling: number): R;
230-
}
231-
declare global {
232-
namespace jest {
233-
interface Expect extends CustomMatchers {}
234-
interface Matchers<R> extends CustomMatchers<R> {}
235-
interface InverseAsymmetricMatchers extends CustomMatchers {}
236-
}
237-
}
238-
export {};
239-
```
206+
:::
240207

241208
#### Async Matchers
242209

0 commit comments

Comments
 (0)