@vitest/expect 2.1.3 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,12 +5,12 @@ Jest's expect matchers as a Chai plugin.
5
5
  ## Usage
6
6
 
7
7
  ```js
8
- import * as chai from 'chai'
9
8
  import {
10
9
  JestAsymmetricMatchers,
11
10
  JestChaiExpect,
12
11
  JestExtend,
13
12
  } from '@vitest/expect'
13
+ import * as chai from 'chai'
14
14
 
15
15
  // allows using expect.extend instead of chai.use to extend plugins
16
16
  chai.use(JestExtend)
package/dist/index.d.ts CHANGED
@@ -1,9 +1,14 @@
1
+ import { stringify, Constructable } from '@vitest/utils';
1
2
  import * as tinyrainbow from 'tinyrainbow';
2
3
  import { Formatter } from 'tinyrainbow';
3
- import { stringify, Constructable } from '@vitest/utils';
4
4
  import { diff, printDiffOrStringify } from '@vitest/utils/diff';
5
5
  export { DiffOptions } from '@vitest/utils/diff';
6
6
 
7
+ declare const MATCHERS_OBJECT: unique symbol;
8
+ declare const JEST_MATCHERS_OBJECT: unique symbol;
9
+ declare const GLOBAL_EXPECT: unique symbol;
10
+ declare const ASYMMETRIC_MATCHERS_OBJECT: unique symbol;
11
+
7
12
  declare function matcherHint(matcherName: string, received?: string, expected?: string, options?: MatcherHintOptions): string;
8
13
  declare function printReceived(object: unknown): string;
9
14
  declare function printExpected(value: unknown): string;
@@ -92,55 +97,433 @@ interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersContaining {
92
97
  not: AsymmetricMatchersContaining;
93
98
  }
94
99
  interface AsymmetricMatchersContaining {
100
+ /**
101
+ * Matches if the received string contains the expected substring.
102
+ *
103
+ * @example
104
+ * expect('I have an apple').toEqual(expect.stringContaining('apple'));
105
+ * expect({ a: 'test string' }).toEqual({ a: expect.stringContaining('test') });
106
+ */
95
107
  stringContaining: (expected: string) => any;
108
+ /**
109
+ * Matches if the received object contains all properties of the expected object.
110
+ *
111
+ * @example
112
+ * expect({ a: '1', b: 2 }).toEqual(expect.objectContaining({ a: '1' }))
113
+ */
96
114
  objectContaining: <T = any>(expected: T) => any;
115
+ /**
116
+ * Matches if the received array contains all elements in the expected array.
117
+ *
118
+ * @example
119
+ * expect(['a', 'b', 'c']).toEqual(expect.arrayContaining(['b', 'a']));
120
+ */
97
121
  arrayContaining: <T = unknown>(expected: Array<T>) => any;
122
+ /**
123
+ * Matches if the received string or regex matches the expected pattern.
124
+ *
125
+ * @example
126
+ * expect('hello world').toEqual(expect.stringMatching(/^hello/));
127
+ * expect('hello world').toEqual(expect.stringMatching('hello'));
128
+ */
98
129
  stringMatching: (expected: string | RegExp) => any;
130
+ /**
131
+ * Matches if the received number is within a certain precision of the expected number.
132
+ *
133
+ * @param precision - Optional decimal precision for comparison. Default is 2.
134
+ *
135
+ * @example
136
+ * expect(10.45).toEqual(expect.closeTo(10.5, 1));
137
+ * expect(5.11).toEqual(expect.closeTo(5.12)); // with default precision
138
+ */
99
139
  closeTo: (expected: number, precision?: number) => any;
100
140
  }
101
141
  interface JestAssertion<T = any> extends jest.Matchers<void, T> {
142
+ /**
143
+ * Used when you want to check that two objects have the same value.
144
+ * This matcher recursively checks the equality of all fields, rather than checking for object identity.
145
+ *
146
+ * @example
147
+ * expect(user).toEqual({ name: 'Alice', age: 30 });
148
+ */
102
149
  toEqual: <E>(expected: E) => void;
150
+ /**
151
+ * Use to test that objects have the same types as well as structure.
152
+ *
153
+ * @example
154
+ * expect(user).toStrictEqual({ name: 'Alice', age: 30 });
155
+ */
103
156
  toStrictEqual: <E>(expected: E) => void;
157
+ /**
158
+ * Checks that a value is what you expect. It calls `Object.is` to compare values.
159
+ * Don't use `toBe` with floating-point numbers.
160
+ *
161
+ * @example
162
+ * expect(result).toBe(42);
163
+ * expect(status).toBe(true);
164
+ */
104
165
  toBe: <E>(expected: E) => void;
166
+ /**
167
+ * Check that a string matches a regular expression.
168
+ *
169
+ * @example
170
+ * expect(message).toMatch(/hello/);
171
+ * expect(greeting).toMatch('world');
172
+ */
105
173
  toMatch: (expected: string | RegExp) => void;
174
+ /**
175
+ * Used to check that a JavaScript object matches a subset of the properties of an object
176
+ *
177
+ * @example
178
+ * expect(user).toMatchObject({
179
+ * name: 'Alice',
180
+ * address: { city: 'Wonderland' }
181
+ * });
182
+ */
106
183
  toMatchObject: <E extends object | any[]>(expected: E) => void;
184
+ /**
185
+ * Used when you want to check that an item is in a list.
186
+ * For testing the items in the list, this uses `===`, a strict equality check.
187
+ *
188
+ * @example
189
+ * expect(items).toContain('apple');
190
+ * expect(numbers).toContain(5);
191
+ */
107
192
  toContain: <E>(item: E) => void;
193
+ /**
194
+ * Used when you want to check that an item is in a list.
195
+ * For testing the items in the list, this matcher recursively checks the
196
+ * equality of all fields, rather than checking for object identity.
197
+ *
198
+ * @example
199
+ * expect(items).toContainEqual({ name: 'apple', quantity: 1 });
200
+ */
108
201
  toContainEqual: <E>(item: E) => void;
202
+ /**
203
+ * Use when you don't care what a value is, you just want to ensure a value
204
+ * is true in a boolean context. In JavaScript, there are six falsy values:
205
+ * `false`, `0`, `''`, `null`, `undefined`, and `NaN`. Everything else is truthy.
206
+ *
207
+ * @example
208
+ * expect(user.isActive).toBeTruthy();
209
+ */
109
210
  toBeTruthy: () => void;
211
+ /**
212
+ * When you don't care what a value is, you just want to
213
+ * ensure a value is false in a boolean context.
214
+ *
215
+ * @example
216
+ * expect(user.isActive).toBeFalsy();
217
+ */
110
218
  toBeFalsy: () => void;
219
+ /**
220
+ * For comparing floating point numbers.
221
+ *
222
+ * @example
223
+ * expect(score).toBeGreaterThan(10);
224
+ */
111
225
  toBeGreaterThan: (num: number | bigint) => void;
226
+ /**
227
+ * For comparing floating point numbers.
228
+ *
229
+ * @example
230
+ * expect(score).toBeGreaterThanOrEqual(10);
231
+ */
112
232
  toBeGreaterThanOrEqual: (num: number | bigint) => void;
233
+ /**
234
+ * For comparing floating point numbers.
235
+ *
236
+ * @example
237
+ * expect(score).toBeLessThan(10);
238
+ */
113
239
  toBeLessThan: (num: number | bigint) => void;
240
+ /**
241
+ * For comparing floating point numbers.
242
+ *
243
+ * @example
244
+ * expect(score).toBeLessThanOrEqual(10);
245
+ */
114
246
  toBeLessThanOrEqual: (num: number | bigint) => void;
247
+ /**
248
+ * Used to check that a variable is NaN.
249
+ *
250
+ * @example
251
+ * expect(value).toBeNaN();
252
+ */
115
253
  toBeNaN: () => void;
254
+ /**
255
+ * Used to check that a variable is undefined.
256
+ *
257
+ * @example
258
+ * expect(value).toBeUndefined();
259
+ */
116
260
  toBeUndefined: () => void;
261
+ /**
262
+ * This is the same as `.toBe(null)` but the error messages are a bit nicer.
263
+ * So use `.toBeNull()` when you want to check that something is null.
264
+ *
265
+ * @example
266
+ * expect(value).toBeNull();
267
+ */
117
268
  toBeNull: () => void;
269
+ /**
270
+ * Ensure that a variable is not undefined.
271
+ *
272
+ * @example
273
+ * expect(value).toBeDefined();
274
+ */
118
275
  toBeDefined: () => void;
276
+ /**
277
+ * Ensure that an object is an instance of a class.
278
+ * This matcher uses `instanceof` underneath.
279
+ *
280
+ * @example
281
+ * expect(new Date()).toBeInstanceOf(Date);
282
+ */
119
283
  toBeInstanceOf: <E>(expected: E) => void;
120
- toBeCalledTimes: (times: number) => void;
284
+ /**
285
+ * Used to check that an object has a `.length` property
286
+ * and it is set to a certain numeric value.
287
+ *
288
+ * @example
289
+ * expect([1, 2, 3]).toHaveLength(3);
290
+ * expect('hello').toHaveLength(5);
291
+ */
121
292
  toHaveLength: (length: number) => void;
293
+ /**
294
+ * Use to check if a property at the specified path exists on an object.
295
+ * For checking deeply nested properties, you may use dot notation or an array containing
296
+ * the path segments for deep references.
297
+ *
298
+ * Optionally, you can provide a value to check if it matches the value present at the path
299
+ * on the target object. This matcher uses 'deep equality' (like `toEqual()`) and recursively checks
300
+ * the equality of all fields.
301
+ *
302
+ * @example
303
+ * expect(user).toHaveProperty('address.city', 'New York');
304
+ * expect(config).toHaveProperty(['settings', 'theme'], 'dark');
305
+ */
122
306
  toHaveProperty: <E>(property: string | (string | number)[], value?: E) => void;
307
+ /**
308
+ * Using exact equality with floating point numbers is a bad idea.
309
+ * Rounding means that intuitive things fail.
310
+ * The default for `precision` is 2.
311
+ *
312
+ * @example
313
+ * expect(price).toBeCloseTo(9.99, 2);
314
+ */
123
315
  toBeCloseTo: (number: number, numDigits?: number) => void;
316
+ /**
317
+ * Ensures that a mock function is called an exact number of times.
318
+ *
319
+ * Also under the alias `expect.toBeCalledTimes`.
320
+ *
321
+ * @example
322
+ * expect(mockFunc).toHaveBeenCalledTimes(2);
323
+ */
124
324
  toHaveBeenCalledTimes: (times: number) => void;
325
+ /**
326
+ * Ensures that a mock function is called an exact number of times.
327
+ *
328
+ * Alias for `expect.toHaveBeenCalledTimes`.
329
+ *
330
+ * @example
331
+ * expect(mockFunc).toBeCalledTimes(2);
332
+ */
333
+ toBeCalledTimes: (times: number) => void;
334
+ /**
335
+ * Ensures that a mock function is called.
336
+ *
337
+ * Also under the alias `expect.toBeCalled`.
338
+ *
339
+ * @example
340
+ * expect(mockFunc).toHaveBeenCalled();
341
+ */
125
342
  toHaveBeenCalled: () => void;
343
+ /**
344
+ * Ensures that a mock function is called.
345
+ *
346
+ * Alias for `expect.toHaveBeenCalled`.
347
+ *
348
+ * @example
349
+ * expect(mockFunc).toBeCalled();
350
+ */
126
351
  toBeCalled: () => void;
352
+ /**
353
+ * Ensure that a mock function is called with specific arguments.
354
+ *
355
+ * Also under the alias `expect.toBeCalledWith`.
356
+ *
357
+ * @example
358
+ * expect(mockFunc).toHaveBeenCalledWith('arg1', 42);
359
+ */
127
360
  toHaveBeenCalledWith: <E extends any[]>(...args: E) => void;
361
+ /**
362
+ * Ensure that a mock function is called with specific arguments.
363
+ *
364
+ * Alias for `expect.toHaveBeenCalledWith`.
365
+ *
366
+ * @example
367
+ * expect(mockFunc).toBeCalledWith('arg1', 42);
368
+ */
128
369
  toBeCalledWith: <E extends any[]>(...args: E) => void;
370
+ /**
371
+ * Ensure that a mock function is called with specific arguments on an Nth call.
372
+ *
373
+ * Also under the alias `expect.nthCalledWith`.
374
+ *
375
+ * @example
376
+ * expect(mockFunc).toHaveBeenNthCalledWith(2, 'secondArg');
377
+ */
129
378
  toHaveBeenNthCalledWith: <E extends any[]>(n: number, ...args: E) => void;
379
+ /**
380
+ * Ensure that a mock function is called with specific arguments on an Nth call.
381
+ *
382
+ * Alias for `expect.toHaveBeenNthCalledWith`.
383
+ *
384
+ * @example
385
+ * expect(mockFunc).nthCalledWith(2, 'secondArg');
386
+ */
130
387
  nthCalledWith: <E extends any[]>(nthCall: number, ...args: E) => void;
388
+ /**
389
+ * If you have a mock function, you can use `.toHaveBeenLastCalledWith`
390
+ * to test what arguments it was last called with.
391
+ *
392
+ * Also under the alias `expect.lastCalledWith`.
393
+ *
394
+ * @example
395
+ * expect(mockFunc).toHaveBeenLastCalledWith('lastArg');
396
+ */
131
397
  toHaveBeenLastCalledWith: <E extends any[]>(...args: E) => void;
398
+ /**
399
+ * If you have a mock function, you can use `.lastCalledWith`
400
+ * to test what arguments it was last called with.
401
+ *
402
+ * Alias for `expect.toHaveBeenLastCalledWith`.
403
+ *
404
+ * @example
405
+ * expect(mockFunc).lastCalledWith('lastArg');
406
+ */
132
407
  lastCalledWith: <E extends any[]>(...args: E) => void;
408
+ /**
409
+ * Used to test that a function throws when it is called.
410
+ *
411
+ * Also under the alias `expect.toThrowError`.
412
+ *
413
+ * @example
414
+ * expect(() => functionWithError()).toThrow('Error message');
415
+ * expect(() => parseJSON('invalid')).toThrow(SyntaxError);
416
+ */
133
417
  toThrow: (expected?: string | Constructable | RegExp | Error) => void;
418
+ /**
419
+ * Used to test that a function throws when it is called.
420
+ *
421
+ * Alias for `expect.toThrow`.
422
+ *
423
+ * @example
424
+ * expect(() => functionWithError()).toThrowError('Error message');
425
+ * expect(() => parseJSON('invalid')).toThrowError(SyntaxError);
426
+ */
134
427
  toThrowError: (expected?: string | Constructable | RegExp | Error) => void;
428
+ /**
429
+ * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time
430
+ *
431
+ * Alias for `expect.toHaveReturned`.
432
+ *
433
+ * @example
434
+ * expect(mockFunc).toReturn();
435
+ */
135
436
  toReturn: () => void;
437
+ /**
438
+ * Use to test that the mock function successfully returned (i.e., did not throw an error) at least one time
439
+ *
440
+ * Also under the alias `expect.toReturn`.
441
+ *
442
+ * @example
443
+ * expect(mockFunc).toHaveReturned();
444
+ */
136
445
  toHaveReturned: () => void;
446
+ /**
447
+ * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times.
448
+ * Any calls to the mock function that throw an error are not counted toward the number of times the function returned.
449
+ *
450
+ * Alias for `expect.toHaveReturnedTimes`.
451
+ *
452
+ * @example
453
+ * expect(mockFunc).toReturnTimes(3);
454
+ */
137
455
  toReturnTimes: (times: number) => void;
456
+ /**
457
+ * Use to ensure that a mock function returned successfully (i.e., did not throw an error) an exact number of times.
458
+ * Any calls to the mock function that throw an error are not counted toward the number of times the function returned.
459
+ *
460
+ * Also under the alias `expect.toReturnTimes`.
461
+ *
462
+ * @example
463
+ * expect(mockFunc).toHaveReturnedTimes(3);
464
+ */
138
465
  toHaveReturnedTimes: (times: number) => void;
466
+ /**
467
+ * Use to ensure that a mock function returned a specific value.
468
+ *
469
+ * Alias for `expect.toHaveReturnedWith`.
470
+ *
471
+ * @example
472
+ * expect(mockFunc).toReturnWith('returnValue');
473
+ */
139
474
  toReturnWith: <E>(value: E) => void;
475
+ /**
476
+ * Use to ensure that a mock function returned a specific value.
477
+ *
478
+ * Also under the alias `expect.toReturnWith`.
479
+ *
480
+ * @example
481
+ * expect(mockFunc).toHaveReturnedWith('returnValue');
482
+ */
140
483
  toHaveReturnedWith: <E>(value: E) => void;
484
+ /**
485
+ * Use to test the specific value that a mock function last returned.
486
+ * If the last call to the mock function threw an error, then this matcher will fail
487
+ * no matter what value you provided as the expected return value.
488
+ *
489
+ * Also under the alias `expect.lastReturnedWith`.
490
+ *
491
+ * @example
492
+ * expect(mockFunc).toHaveLastReturnedWith('lastValue');
493
+ */
141
494
  toHaveLastReturnedWith: <E>(value: E) => void;
495
+ /**
496
+ * Use to test the specific value that a mock function last returned.
497
+ * If the last call to the mock function threw an error, then this matcher will fail
498
+ * no matter what value you provided as the expected return value.
499
+ *
500
+ * Alias for `expect.toHaveLastReturnedWith`.
501
+ *
502
+ * @example
503
+ * expect(mockFunc).lastReturnedWith('lastValue');
504
+ */
142
505
  lastReturnedWith: <E>(value: E) => void;
506
+ /**
507
+ * Use to test the specific value that a mock function returned for the nth call.
508
+ * If the nth call to the mock function threw an error, then this matcher will fail
509
+ * no matter what value you provided as the expected return value.
510
+ *
511
+ * Also under the alias `expect.nthReturnedWith`.
512
+ *
513
+ * @example
514
+ * expect(mockFunc).toHaveNthReturnedWith(2, 'nthValue');
515
+ */
143
516
  toHaveNthReturnedWith: <E>(nthCall: number, value: E) => void;
517
+ /**
518
+ * Use to test the specific value that a mock function returned for the nth call.
519
+ * If the nth call to the mock function threw an error, then this matcher will fail
520
+ * no matter what value you provided as the expected return value.
521
+ *
522
+ * Alias for `expect.toHaveNthReturnedWith`.
523
+ *
524
+ * @example
525
+ * expect(mockFunc).nthReturnedWith(2, 'nthValue');
526
+ */
144
527
  nthReturnedWith: <E>(nthCall: number, value: E) => void;
145
528
  }
146
529
  type VitestAssertion<A, T> = {
@@ -151,15 +534,79 @@ type Promisify<O> = {
151
534
  };
152
535
  type PromisifyAssertion<T> = Promisify<Assertion<T>>;
153
536
  interface Assertion<T = any> extends VitestAssertion<Chai.Assertion, T>, JestAssertion<T> {
537
+ /**
538
+ * Ensures a value is of a specific type.
539
+ *
540
+ * @example
541
+ * expect(value).toBeTypeOf('string');
542
+ * expect(number).toBeTypeOf('number');
543
+ */
154
544
  toBeTypeOf: (expected: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => void;
545
+ /**
546
+ * Asserts that a mock function was called exactly once.
547
+ *
548
+ * @example
549
+ * expect(mockFunc).toHaveBeenCalledOnce();
550
+ */
155
551
  toHaveBeenCalledOnce: () => void;
552
+ /**
553
+ * Checks that a value satisfies a custom matcher function.
554
+ *
555
+ * @param matcher - A function returning a boolean based on the custom condition
556
+ * @param message - Optional custom error message on failure
557
+ *
558
+ * @example
559
+ * expect(age).toSatisfy(val => val >= 18, 'Age must be at least 18');
560
+ */
156
561
  toSatisfy: <E>(matcher: (value: E) => boolean, message?: string) => void;
562
+ /**
563
+ * Checks that a promise resolves successfully at least once.
564
+ *
565
+ * @example
566
+ * await expect(promise).toHaveResolved();
567
+ */
157
568
  toHaveResolved: () => void;
569
+ /**
570
+ * Checks that a promise resolves to a specific value.
571
+ *
572
+ * @example
573
+ * await expect(promise).toHaveResolvedWith('success');
574
+ */
158
575
  toHaveResolvedWith: <E>(value: E) => void;
576
+ /**
577
+ * Ensures a promise resolves a specific number of times.
578
+ *
579
+ * @example
580
+ * expect(mockAsyncFunc).toHaveResolvedTimes(3);
581
+ */
159
582
  toHaveResolvedTimes: (times: number) => void;
583
+ /**
584
+ * Asserts that the last resolved value of a promise matches an expected value.
585
+ *
586
+ * @example
587
+ * await expect(mockAsyncFunc).toHaveLastResolvedWith('finalResult');
588
+ */
160
589
  toHaveLastResolvedWith: <E>(value: E) => void;
590
+ /**
591
+ * Ensures a specific value was returned by a promise on the nth resolution.
592
+ *
593
+ * @example
594
+ * await expect(mockAsyncFunc).toHaveNthResolvedWith(2, 'secondResult');
595
+ */
161
596
  toHaveNthResolvedWith: <E>(nthCall: number, value: E) => void;
597
+ /**
598
+ * Verifies that a promise resolves.
599
+ *
600
+ * @example
601
+ * await expect(someAsyncFunc).resolves.toBe(42);
602
+ */
162
603
  resolves: PromisifyAssertion<T>;
604
+ /**
605
+ * Verifies that a promise rejects.
606
+ *
607
+ * @example
608
+ * await expect(someAsyncFunc).rejects.toThrow('error');
609
+ */
163
610
  rejects: PromisifyAssertion<T>;
164
611
  }
165
612
  declare global {
@@ -227,6 +674,10 @@ declare class StringMatching extends AsymmetricMatcher<RegExp> {
227
674
  }
228
675
  declare const JestAsymmetricMatchers: ChaiPlugin;
229
676
 
677
+ declare const JestChaiExpect: ChaiPlugin;
678
+
679
+ declare const JestExtend: ChaiPlugin;
680
+
230
681
  declare function equals(a: unknown, b: unknown, customTesters?: Array<Tester>, strictCheck?: boolean): boolean;
231
682
  declare function isAsymmetric(obj: any): boolean;
232
683
  declare function hasAsymmetric(obj: any, seen?: Set<unknown>): boolean;
@@ -248,16 +699,7 @@ declare function getObjectSubset(object: any, subset: any, customTesters: Array<
248
699
  stripped: number;
249
700
  };
250
701
 
251
- declare const MATCHERS_OBJECT: unique symbol;
252
- declare const JEST_MATCHERS_OBJECT: unique symbol;
253
- declare const GLOBAL_EXPECT: unique symbol;
254
- declare const ASYMMETRIC_MATCHERS_OBJECT: unique symbol;
255
-
256
702
  declare function getState<State extends MatcherState = MatcherState>(expect: ExpectStatic): State;
257
703
  declare function setState<State extends MatcherState = MatcherState>(state: Partial<State>, expect: ExpectStatic): void;
258
704
 
259
- declare const JestChaiExpect: ChaiPlugin;
260
-
261
- declare const JestExtend: ChaiPlugin;
262
-
263
705
  export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, type Assertion, AsymmetricMatcher, type AsymmetricMatcherInterface, type AsymmetricMatchersContaining, type AsyncExpectationResult, type ChaiPlugin, type ExpectStatic, type ExpectationResult, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, type JestAssertion, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, type MatcherHintOptions, type MatcherState, type MatchersObject, ObjectContaining, type PromisifyAssertion, type RawMatcherFn, StringContaining, StringMatching, type SyncExpectationResult, type Tester, type TesterContext, addCustomEqualityTesters, arrayBufferEquality, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { getType, stringify, isObject, assertTypes } from '@vitest/utils';
2
- import c from 'tinyrainbow';
3
2
  import { diff, printDiffOrStringify } from '@vitest/utils/diff';
3
+ import c from 'tinyrainbow';
4
4
  import { isMockFunction } from '@vitest/spy';
5
5
  import { processError } from '@vitest/utils/error';
6
6
  import { use, util } from 'chai';
@@ -12,39 +12,6 @@ const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for(
12
12
  "asymmetric-matchers-object"
13
13
  );
14
14
 
15
- if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
16
- const globalState = /* @__PURE__ */ new WeakMap();
17
- const matchers = /* @__PURE__ */ Object.create(null);
18
- const customEqualityTesters = [];
19
- const assymetricMatchers = /* @__PURE__ */ Object.create(null);
20
- Object.defineProperty(globalThis, MATCHERS_OBJECT, {
21
- get: () => globalState
22
- });
23
- Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
24
- configurable: true,
25
- get: () => ({
26
- state: globalState.get(globalThis[GLOBAL_EXPECT]),
27
- matchers,
28
- customEqualityTesters
29
- })
30
- });
31
- Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
32
- get: () => assymetricMatchers
33
- });
34
- }
35
- function getState(expect) {
36
- return globalThis[MATCHERS_OBJECT].get(expect);
37
- }
38
- function setState(state, expect) {
39
- const map = globalThis[MATCHERS_OBJECT];
40
- const current = map.get(expect) || {};
41
- const results = Object.defineProperties(current, {
42
- ...Object.getOwnPropertyDescriptors(current),
43
- ...Object.getOwnPropertyDescriptors(state)
44
- });
45
- map.set(expect, results);
46
- }
47
-
48
15
  const EXPECTED_COLOR = c.green;
49
16
  const RECEIVED_COLOR = c.red;
50
17
  const INVERTED_COLOR = c.inverse;
@@ -229,6 +196,7 @@ function eq(a, b, aStack, bStack, customTesters, hasKey2) {
229
196
  const numB = +b;
230
197
  return numA === numB || Number.isNaN(numA) && Number.isNaN(numB);
231
198
  }
199
+ // RegExps are compared by their source patterns and flags.
232
200
  case "[object RegExp]":
233
201
  return a.source === b.source && a.flags === b.flags;
234
202
  }
@@ -610,6 +578,39 @@ function getObjectSubset(object, subset, customTesters) {
610
578
  return { subset: getObjectSubsetWithContext()(object, subset), stripped };
611
579
  }
612
580
 
581
+ if (!Object.prototype.hasOwnProperty.call(globalThis, MATCHERS_OBJECT)) {
582
+ const globalState = /* @__PURE__ */ new WeakMap();
583
+ const matchers = /* @__PURE__ */ Object.create(null);
584
+ const customEqualityTesters = [];
585
+ const assymetricMatchers = /* @__PURE__ */ Object.create(null);
586
+ Object.defineProperty(globalThis, MATCHERS_OBJECT, {
587
+ get: () => globalState
588
+ });
589
+ Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
590
+ configurable: true,
591
+ get: () => ({
592
+ state: globalState.get(globalThis[GLOBAL_EXPECT]),
593
+ matchers,
594
+ customEqualityTesters
595
+ })
596
+ });
597
+ Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, {
598
+ get: () => assymetricMatchers
599
+ });
600
+ }
601
+ function getState(expect) {
602
+ return globalThis[MATCHERS_OBJECT].get(expect);
603
+ }
604
+ function setState(state, expect) {
605
+ const map = globalThis[MATCHERS_OBJECT];
606
+ const current = map.get(expect) || {};
607
+ const results = Object.defineProperties(current, {
608
+ ...Object.getOwnPropertyDescriptors(current),
609
+ ...Object.getOwnPropertyDescriptors(state)
610
+ });
611
+ map.set(expect, results);
612
+ }
613
+
613
614
  class AsymmetricMatcher {
614
615
  constructor(sample, inverse = false) {
615
616
  this.sample = sample;
@@ -907,9 +908,20 @@ const JestAsymmetricMatchers = (chai, utils) => {
907
908
  };
908
909
  };
909
910
 
910
- function recordAsyncExpect(test, promise) {
911
+ function createAssertionMessage(util, assertion, hasArgs) {
912
+ const not = util.flag(assertion, "negate") ? "not." : "";
913
+ const name = `${util.flag(assertion, "_name")}(${hasArgs ? "expected" : ""})`;
914
+ const promiseName = util.flag(assertion, "promise");
915
+ const promise = promiseName ? `.${promiseName}` : "";
916
+ return `expect(actual)${promise}.${not}${name}`;
917
+ }
918
+ function recordAsyncExpect(_test, promise, assertion, error) {
919
+ const test = _test;
911
920
  if (test && promise instanceof Promise) {
912
921
  promise = promise.finally(() => {
922
+ if (!test.promises) {
923
+ return;
924
+ }
913
925
  const index = test.promises.indexOf(promise);
914
926
  if (index !== -1) {
915
927
  test.promises.splice(index, 1);
@@ -919,13 +931,43 @@ function recordAsyncExpect(test, promise) {
919
931
  test.promises = [];
920
932
  }
921
933
  test.promises.push(promise);
934
+ let resolved = false;
935
+ test.onFinished ?? (test.onFinished = []);
936
+ test.onFinished.push(() => {
937
+ var _a;
938
+ if (!resolved) {
939
+ const processor = ((_a = globalThis.__vitest_worker__) == null ? void 0 : _a.onFilterStackTrace) || ((s) => s || "");
940
+ const stack = processor(error.stack);
941
+ console.warn([
942
+ `Promise returned by \`${assertion}\` was not awaited. `,
943
+ "Vitest currently auto-awaits hanging assertions at the end of the test, but this will cause the test to fail in Vitest 3. ",
944
+ "Please remember to await the assertion.\n",
945
+ stack
946
+ ].join(""));
947
+ }
948
+ });
949
+ return {
950
+ then(onFullfilled, onRejected) {
951
+ resolved = true;
952
+ return promise.then(onFullfilled, onRejected);
953
+ },
954
+ catch(onRejected) {
955
+ return promise.catch(onRejected);
956
+ },
957
+ finally(onFinally) {
958
+ return promise.finally(onFinally);
959
+ },
960
+ [Symbol.toStringTag]: "Promise"
961
+ };
922
962
  }
923
963
  return promise;
924
964
  }
925
965
  function wrapAssertion(utils, name, fn) {
926
966
  return function(...args) {
927
967
  var _a;
928
- utils.flag(this, "_name", name);
968
+ if (name !== "withTest") {
969
+ utils.flag(this, "_name", name);
970
+ }
929
971
  if (!utils.flag(this, "soft")) {
930
972
  return fn.apply(this, args);
931
973
  }
@@ -1464,7 +1506,7 @@ const JestChaiExpect = (chai, utils) => {
1464
1506
  ["toThrow", "toThrowError"],
1465
1507
  function(expected) {
1466
1508
  if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) {
1467
- return this.throws(expected);
1509
+ return this.throws(expected === "" ? /^$/ : expected);
1468
1510
  }
1469
1511
  const obj = this._obj;
1470
1512
  const promise = utils.flag(this, "promise");
@@ -1723,6 +1765,7 @@ const JestChaiExpect = (chai, utils) => {
1723
1765
  return result instanceof chai.Assertion ? proxy : result;
1724
1766
  }
1725
1767
  return (...args) => {
1768
+ utils.flag(this, "_name", key);
1726
1769
  const promise = obj.then(
1727
1770
  (value) => {
1728
1771
  utils.flag(this, "object", value);
@@ -1743,7 +1786,12 @@ const JestChaiExpect = (chai, utils) => {
1743
1786
  throw _error;
1744
1787
  }
1745
1788
  );
1746
- return recordAsyncExpect(test, promise);
1789
+ return recordAsyncExpect(
1790
+ test,
1791
+ promise,
1792
+ createAssertionMessage(utils, this, !!args.length),
1793
+ error
1794
+ );
1747
1795
  };
1748
1796
  }
1749
1797
  });
@@ -1777,6 +1825,7 @@ const JestChaiExpect = (chai, utils) => {
1777
1825
  return result instanceof chai.Assertion ? proxy : result;
1778
1826
  }
1779
1827
  return (...args) => {
1828
+ utils.flag(this, "_name", key);
1780
1829
  const promise = wrapper.then(
1781
1830
  (value) => {
1782
1831
  const _error = new AssertionError(
@@ -1800,7 +1849,12 @@ const JestChaiExpect = (chai, utils) => {
1800
1849
  return result.call(this, ...args);
1801
1850
  }
1802
1851
  );
1803
- return recordAsyncExpect(test, promise);
1852
+ return recordAsyncExpect(
1853
+ test,
1854
+ promise,
1855
+ createAssertionMessage(utils, this, !!args.length),
1856
+ error
1857
+ );
1804
1858
  };
1805
1859
  }
1806
1860
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/expect",
3
3
  "type": "module",
4
- "version": "2.1.3",
4
+ "version": "2.1.5",
5
5
  "description": "Jest's expect matchers as a Chai plugin",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -30,15 +30,15 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "chai": "^5.1.1",
33
+ "chai": "^5.1.2",
34
34
  "tinyrainbow": "^1.2.0",
35
- "@vitest/spy": "2.1.3",
36
- "@vitest/utils": "2.1.3"
35
+ "@vitest/utils": "2.1.5",
36
+ "@vitest/spy": "2.1.5"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/chai": "4.3.6",
40
40
  "rollup-plugin-copy": "^3.5.0",
41
- "@vitest/runner": "2.1.3"
41
+ "@vitest/runner": "2.1.5"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "rimraf dist && rollup -c",