-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
Copy pathexpect.ts
145 lines (120 loc) · 4.16 KB
/
expect.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as matchers from '@testing-library/jest-dom/matchers';
import type { TestingLibraryMatchers } from '@testing-library/jest-dom/matchers';
import type {
AsymmetricMatchersContaining,
ExpectStatic,
JestAssertion,
MatcherState,
MatchersObject,
} from '@vitest/expect';
import {
GLOBAL_EXPECT,
JestAsymmetricMatchers,
JestChaiExpect,
JestExtend,
getState,
setState,
} from '@vitest/expect';
import * as chai from 'chai';
import type { PromisifyObject } from './utils';
type Matchers<T> = PromisifyObject<JestAssertion<T>> &
TestingLibraryMatchers<ReturnType<ExpectStatic['stringContaining']>, Promise<void>>;
// We only expose the jest compatible API for now
export interface Assertion<T> extends Matchers<T> {
toHaveBeenCalledOnce(): Promise<void>;
toSatisfy<E>(matcher: (value: E) => boolean, message?: string): Promise<void>;
resolves: Assertion<T>;
rejects: Assertion<T>;
not: Assertion<T>;
}
export interface Expect extends AsymmetricMatchersContaining {
<T>(actual: T, message?: string): Assertion<T>;
unreachable(message?: string): Promise<never>;
soft<T>(actual: T, message?: string): Assertion<T>;
extend(expects: MatchersObject): void;
assertions(expected: number): Promise<void>;
hasAssertions(): Promise<void>;
anything(): any;
any(constructor: unknown): any;
getState(): MatcherState;
setState(state: Partial<MatcherState>): void;
not: AsymmetricMatchersContaining;
}
export function createExpect() {
chai.use(JestExtend);
chai.use(JestChaiExpect);
chai.use(JestAsymmetricMatchers);
const expect = ((value: unknown, message?: string) => {
const { assertionCalls } = getState(expect);
setState({ assertionCalls: assertionCalls + 1, soft: false }, expect);
return chai.expect(value, message);
}) as ExpectStatic;
Object.assign(expect, chai.expect);
// The below methods are added to make chai jest compatible
expect.getState = () => getState<MatcherState>(expect);
expect.setState = (state) => setState(state as Partial<MatcherState>, expect);
// @ts-expect-error chai.extend is not typed
expect.extend = (expects: MatchersObject) => chai.expect.extend(expect, expects);
// @ts-ignore tsup borks here for some reason
expect.soft = (...args) => {
// @ts-ignore tsup borks here for some reason
const assert = expect(...args);
expect.setState({
soft: true,
});
return assert;
};
// @ts-ignore tsup borks here for some reason
expect.unreachable = (message?: string): never => {
chai.assert.fail(`expected${message ? ` "${message}" ` : ' '}not to be reached`);
};
function assertions(expected: number) {
const errorGen = () =>
new Error(
`expected number of assertions to be ${expected}, but got ${
expect.getState().assertionCalls
}`
);
if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(errorGen(), assertions);
}
expect.setState({
expectedAssertionsNumber: expected,
expectedAssertionsNumberErrorGen: errorGen,
});
}
function hasAssertions() {
const error = new Error('expected any number of assertion, but got none');
if ('captureStackTrace' in Error && typeof Error.captureStackTrace === 'function') {
Error.captureStackTrace(error, hasAssertions);
}
expect.setState({
isExpectingAssertions: true,
isExpectingAssertionsError: error,
});
}
setState<MatcherState>(
{
// this should also add "snapshotState" that is added conditionally
assertionCalls: 0,
isExpectingAssertions: false,
isExpectingAssertionsError: null,
expectedAssertionsNumber: null,
expectedAssertionsNumberErrorGen: null,
},
expect
);
chai.util.addMethod(expect, 'assertions', assertions);
chai.util.addMethod(expect, 'hasAssertions', hasAssertions);
expect.extend(matchers);
return expect as unknown as Expect;
}
const expect: Expect = createExpect();
// @vitest/expect expects this to be set
Object.defineProperty(globalThis, GLOBAL_EXPECT, {
value: expect,
writable: true,
configurable: true,
});
export { expect };