@voidzero-dev/vite-plus-test 0.0.0-0bfcc90f.20260209-0731
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/LICENSE.md +691 -0
- package/browser/context.d.ts +4 -0
- package/browser/context.js +20 -0
- package/config.d.ts +3 -0
- package/coverage.d.ts +1 -0
- package/dist/@vitest/browser/client/.vite/manifest.json +24 -0
- package/dist/@vitest/browser/client/__vitest__/assets/index-BUCFJtth.js +57 -0
- package/dist/@vitest/browser/client/__vitest__/assets/index-DlhE0rqZ.css +1 -0
- package/dist/@vitest/browser/client/__vitest__/bg.png +0 -0
- package/dist/@vitest/browser/client/__vitest__/favicon.ico +0 -0
- package/dist/@vitest/browser/client/__vitest__/favicon.svg +5 -0
- package/dist/@vitest/browser/client/__vitest__/index.html +32 -0
- package/dist/@vitest/browser/client/__vitest_browser__/orchestrator-S_3e_uzt.js +345 -0
- package/dist/@vitest/browser/client/__vitest_browser__/tester-k74mgIRa.js +2171 -0
- package/dist/@vitest/browser/client/__vitest_browser__/utils-uxqdqUz8.js +223 -0
- package/dist/@vitest/browser/client/error-catcher.js +82 -0
- package/dist/@vitest/browser/client/esm-client-injector.js +67 -0
- package/dist/@vitest/browser/client/favicon.svg +5 -0
- package/dist/@vitest/browser/client/orchestrator.html +35 -0
- package/dist/@vitest/browser/client/tester/tester.html +13 -0
- package/dist/@vitest/browser/client.js +456 -0
- package/dist/@vitest/browser/context.d.ts +792 -0
- package/dist/@vitest/browser/context.js +541 -0
- package/dist/@vitest/browser/expect-element.js +32 -0
- package/dist/@vitest/browser/index-D6m36C6U.js +11 -0
- package/dist/@vitest/browser/index.d.ts +73 -0
- package/dist/@vitest/browser/index.js +3513 -0
- package/dist/@vitest/browser/jest-dom.d.ts +724 -0
- package/dist/@vitest/browser/locators.d.ts +354 -0
- package/dist/@vitest/browser/locators.js +1 -0
- package/dist/@vitest/browser/matchers.d.ts +29 -0
- package/dist/@vitest/browser/shared/screenshotMatcher/types.d.ts +22 -0
- package/dist/@vitest/browser/state.js +280 -0
- package/dist/@vitest/browser/types.d.ts +69 -0
- package/dist/@vitest/browser-playwright/context.d.ts +1 -0
- package/dist/@vitest/browser-playwright/index.d.ts +106 -0
- package/dist/@vitest/browser-playwright/index.js +1111 -0
- package/dist/@vitest/browser-playwright/locators.js +114 -0
- package/dist/@vitest/browser-preview/context.d.ts +1 -0
- package/dist/@vitest/browser-preview/index.d.ts +19 -0
- package/dist/@vitest/browser-preview/index.js +148 -0
- package/dist/@vitest/browser-preview/locators.js +79 -0
- package/dist/@vitest/browser-webdriverio/context.d.ts +1 -0
- package/dist/@vitest/browser-webdriverio/index.d.ts +63 -0
- package/dist/@vitest/browser-webdriverio/index.js +600 -0
- package/dist/@vitest/browser-webdriverio/locators.js +163 -0
- package/dist/@vitest/expect/index.d.ts +807 -0
- package/dist/@vitest/expect/index.js +1875 -0
- package/dist/@vitest/mocker/auto-register.d.ts +2 -0
- package/dist/@vitest/mocker/auto-register.js +9 -0
- package/dist/@vitest/mocker/automock.d.ts +12 -0
- package/dist/@vitest/mocker/automock.js +1 -0
- package/dist/@vitest/mocker/browser.d.ts +53 -0
- package/dist/@vitest/mocker/browser.js +91 -0
- package/dist/@vitest/mocker/chunk-automock.js +354 -0
- package/dist/@vitest/mocker/chunk-interceptor-native.js +15 -0
- package/dist/@vitest/mocker/chunk-mocker.js +521 -0
- package/dist/@vitest/mocker/chunk-pathe.M-eThtNZ.js +174 -0
- package/dist/@vitest/mocker/chunk-registry.js +185 -0
- package/dist/@vitest/mocker/chunk-utils.js +16 -0
- package/dist/@vitest/mocker/index.d-C-sLYZi-.d.ts +25 -0
- package/dist/@vitest/mocker/index.d.ts +2 -0
- package/dist/@vitest/mocker/index.js +185 -0
- package/dist/@vitest/mocker/mocker.d-TnKRhz7N.d.ts +81 -0
- package/dist/@vitest/mocker/node.d.ts +800 -0
- package/dist/@vitest/mocker/node.js +966 -0
- package/dist/@vitest/mocker/redirect.d.ts +3 -0
- package/dist/@vitest/mocker/redirect.js +79 -0
- package/dist/@vitest/mocker/register.d.ts +9 -0
- package/dist/@vitest/mocker/register.js +41 -0
- package/dist/@vitest/mocker/types.d-B8CCKmHt.d.ts +107 -0
- package/dist/@vitest/pretty-format/index.d.ts +124 -0
- package/dist/@vitest/pretty-format/index.js +1022 -0
- package/dist/@vitest/runner/chunk-tasks.js +340 -0
- package/dist/@vitest/runner/index.d.ts +180 -0
- package/dist/@vitest/runner/index.js +2114 -0
- package/dist/@vitest/runner/tasks.d-C7UxawJ9.d.ts +834 -0
- package/dist/@vitest/runner/types.d.ts +183 -0
- package/dist/@vitest/runner/types.js +1 -0
- package/dist/@vitest/runner/utils.d.ts +45 -0
- package/dist/@vitest/runner/utils.js +5 -0
- package/dist/@vitest/snapshot/environment.d-DHdQ1Csl.d.ts +22 -0
- package/dist/@vitest/snapshot/environment.d.ts +16 -0
- package/dist/@vitest/snapshot/environment.js +40 -0
- package/dist/@vitest/snapshot/index.d.ts +130 -0
- package/dist/@vitest/snapshot/index.js +1437 -0
- package/dist/@vitest/snapshot/manager.d.ts +18 -0
- package/dist/@vitest/snapshot/manager.js +73 -0
- package/dist/@vitest/snapshot/rawSnapshot.d-lFsMJFUd.d.ts +61 -0
- package/dist/@vitest/spy/index.d.ts +384 -0
- package/dist/@vitest/spy/index.js +433 -0
- package/dist/@vitest/utils/chunk-_commonjsHelpers.js +5 -0
- package/dist/@vitest/utils/chunk-pathe.M-eThtNZ.js +156 -0
- package/dist/@vitest/utils/constants.d.ts +21 -0
- package/dist/@vitest/utils/constants.js +49 -0
- package/dist/@vitest/utils/diff.d.ts +93 -0
- package/dist/@vitest/utils/diff.js +2199 -0
- package/dist/@vitest/utils/display.d.ts +29 -0
- package/dist/@vitest/utils/display.js +742 -0
- package/dist/@vitest/utils/error.d.ts +7 -0
- package/dist/@vitest/utils/error.js +42 -0
- package/dist/@vitest/utils/helpers.d.ts +73 -0
- package/dist/@vitest/utils/helpers.js +295 -0
- package/dist/@vitest/utils/highlight.d.ts +9 -0
- package/dist/@vitest/utils/highlight.js +538 -0
- package/dist/@vitest/utils/index.d.ts +5 -0
- package/dist/@vitest/utils/index.js +1 -0
- package/dist/@vitest/utils/offset.d.ts +5 -0
- package/dist/@vitest/utils/offset.js +32 -0
- package/dist/@vitest/utils/resolver.d.ts +7 -0
- package/dist/@vitest/utils/resolver.js +71 -0
- package/dist/@vitest/utils/serialize.d.ts +3 -0
- package/dist/@vitest/utils/serialize.js +118 -0
- package/dist/@vitest/utils/source-map.d.ts +55 -0
- package/dist/@vitest/utils/source-map.js +478 -0
- package/dist/@vitest/utils/timers.d.ts +33 -0
- package/dist/@vitest/utils/timers.js +49 -0
- package/dist/@vitest/utils/types.d-BCElaP-c.d.ts +53 -0
- package/dist/@vitest/utils/types.d.ts +34 -0
- package/dist/@vitest/utils/types.js +1 -0
- package/dist/browser-compat.js +3 -0
- package/dist/browser.d.ts +46 -0
- package/dist/browser.js +20 -0
- package/dist/chunks/_commonjsHelpers.D26ty3Ew.js +6 -0
- package/dist/chunks/base.CJ0Y4ePK.js +165 -0
- package/dist/chunks/benchmark.B3N2zMcH.js +40 -0
- package/dist/chunks/benchmark.d.DAaHLpsq.d.ts +24 -0
- package/dist/chunks/browser.d.ChKACdzH.d.ts +59 -0
- package/dist/chunks/cac.DVeoLl0M.js +1409 -0
- package/dist/chunks/cli-api.B7PN_QUv.js +13672 -0
- package/dist/chunks/config.d.Cy95HiCx.d.ts +210 -0
- package/dist/chunks/console.Cf-YriPC.js +146 -0
- package/dist/chunks/constants.D_Q9UYh-.js +36 -0
- package/dist/chunks/coverage.AVPTjMgw.js +3292 -0
- package/dist/chunks/coverage.D_JHT54q.js +25 -0
- package/dist/chunks/coverage.d.BZtK59WP.d.ts +37 -0
- package/dist/chunks/creator.DAmOKTvJ.js +673 -0
- package/dist/chunks/date.Bq6ZW5rf.js +73 -0
- package/dist/chunks/defaults.BOqNVLsY.js +74 -0
- package/dist/chunks/env.D4Lgay0q.js +8 -0
- package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
- package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
- package/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts +7 -0
- package/dist/chunks/git.Bm2pzPAa.js +71 -0
- package/dist/chunks/global.d.B15mdLcR.d.ts +99 -0
- package/dist/chunks/globals.DOayXfHP.js +30 -0
- package/dist/chunks/index.6Qv1eEA6.js +109 -0
- package/dist/chunks/index.C5r1PdPD.js +231 -0
- package/dist/chunks/index.Chj8NDwU.js +206 -0
- package/dist/chunks/index.CyBMJtT7.js +727 -0
- package/dist/chunks/index.D3XRDfWc.js +213 -0
- package/dist/chunks/index.D4KonVSU.js +6343 -0
- package/dist/chunks/index.M8mOzt4Y.js +3839 -0
- package/dist/chunks/index.Z5E_ObnR.js +37 -0
- package/dist/chunks/init-forks._y3TW739.js +41 -0
- package/dist/chunks/init-threads.DBO2kn-p.js +18 -0
- package/dist/chunks/init.B6MLFIaN.js +334 -0
- package/dist/chunks/inspector.CvyFGlXm.js +53 -0
- package/dist/chunks/modules.BJuCwlRJ.js +36 -0
- package/dist/chunks/node.Ce0vMQM7.js +14 -0
- package/dist/chunks/plugin.d.CtqpEehP.d.ts +38 -0
- package/dist/chunks/reporters.d.CWXNI2jG.d.ts +3271 -0
- package/dist/chunks/rpc.BoxB0q7B.js +76 -0
- package/dist/chunks/rpc.d.RH3apGEf.d.ts +64 -0
- package/dist/chunks/setup-common.Cm-kSBVi.js +60 -0
- package/dist/chunks/startModuleRunner.DEj0jb3e.js +861 -0
- package/dist/chunks/suite.d.BJWk38HB.d.ts +10 -0
- package/dist/chunks/test.B8ej_ZHS.js +254 -0
- package/dist/chunks/traces.CCmnQaNT.js +217 -0
- package/dist/chunks/traces.d.402V_yFI.d.ts +18 -0
- package/dist/chunks/utils.DvEY5TfP.js +52 -0
- package/dist/chunks/vi.2VT5v0um.js +3919 -0
- package/dist/chunks/vm.D3epNOPZ.js +744 -0
- package/dist/chunks/worker.d.Dyxm8DEL.d.ts +255 -0
- package/dist/cli.js +28 -0
- package/dist/client/.vite/manifest.json +24 -0
- package/dist/client/__vitest__/assets/index-BUCFJtth.js +57 -0
- package/dist/client/__vitest__/assets/index-DlhE0rqZ.css +1 -0
- package/dist/client/__vitest__/bg.png +0 -0
- package/dist/client/__vitest__/favicon.ico +0 -0
- package/dist/client/__vitest__/favicon.svg +5 -0
- package/dist/client/__vitest__/index.html +32 -0
- package/dist/client/__vitest_browser__/orchestrator-S_3e_uzt.js +345 -0
- package/dist/client/__vitest_browser__/tester-k74mgIRa.js +2171 -0
- package/dist/client/__vitest_browser__/utils-uxqdqUz8.js +223 -0
- package/dist/client/error-catcher.js +82 -0
- package/dist/client/esm-client-injector.js +67 -0
- package/dist/client/favicon.svg +5 -0
- package/dist/client/orchestrator.html +35 -0
- package/dist/client/tester/tester.html +13 -0
- package/dist/client.js +456 -0
- package/dist/config.cjs +94 -0
- package/dist/config.d.ts +104 -0
- package/dist/config.js +15 -0
- package/dist/context.js +541 -0
- package/dist/coverage.d.ts +118 -0
- package/dist/coverage.js +23 -0
- package/dist/dummy.js +2 -0
- package/dist/environments.d.ts +22 -0
- package/dist/environments.js +3 -0
- package/dist/expect-element.js +27 -0
- package/dist/index-D6m36C6U.js +6 -0
- package/dist/index-node.js +7 -0
- package/dist/index.d.ts +510 -0
- package/dist/index.js +19 -0
- package/dist/locators.d.ts +354 -0
- package/dist/locators.js +1 -0
- package/dist/mocker.d.ts +1 -0
- package/dist/mocker.js +1 -0
- package/dist/module-evaluator.d.ts +124 -0
- package/dist/module-evaluator.js +343 -0
- package/dist/module-runner-stub.js +44 -0
- package/dist/module-runner.js +17 -0
- package/dist/node.d.ts +251 -0
- package/dist/node.js +98 -0
- package/dist/path.js +7 -0
- package/dist/plugins/browser-client.mjs +2 -0
- package/dist/plugins/browser-context.mjs +2 -0
- package/dist/plugins/browser-locators.mjs +2 -0
- package/dist/plugins/browser-playwright.mjs +2 -0
- package/dist/plugins/browser-preview.mjs +2 -0
- package/dist/plugins/browser-webdriverio.mjs +2 -0
- package/dist/plugins/browser.mjs +2 -0
- package/dist/plugins/expect.mjs +2 -0
- package/dist/plugins/mocker-automock.mjs +2 -0
- package/dist/plugins/mocker-browser.mjs +2 -0
- package/dist/plugins/mocker-node.mjs +2 -0
- package/dist/plugins/mocker-redirect.mjs +2 -0
- package/dist/plugins/mocker-register.mjs +2 -0
- package/dist/plugins/mocker.mjs +2 -0
- package/dist/plugins/pretty-format.mjs +2 -0
- package/dist/plugins/runner-types.mjs +2 -0
- package/dist/plugins/runner-utils.mjs +2 -0
- package/dist/plugins/runner.mjs +2 -0
- package/dist/plugins/snapshot-environment.mjs +2 -0
- package/dist/plugins/snapshot-manager.mjs +2 -0
- package/dist/plugins/snapshot.mjs +2 -0
- package/dist/plugins/spy.mjs +2 -0
- package/dist/plugins/utils-constants.mjs +2 -0
- package/dist/plugins/utils-diff.mjs +2 -0
- package/dist/plugins/utils-display.mjs +2 -0
- package/dist/plugins/utils-error.mjs +2 -0
- package/dist/plugins/utils-helpers.mjs +2 -0
- package/dist/plugins/utils-highlight.mjs +2 -0
- package/dist/plugins/utils-offset.mjs +2 -0
- package/dist/plugins/utils-resolver.mjs +2 -0
- package/dist/plugins/utils-serialize.mjs +2 -0
- package/dist/plugins/utils-source-map.mjs +2 -0
- package/dist/plugins/utils-timers.mjs +2 -0
- package/dist/plugins/utils.mjs +2 -0
- package/dist/reporters.d.ts +27 -0
- package/dist/reporters.js +24 -0
- package/dist/runners.d.ts +50 -0
- package/dist/runners.js +19 -0
- package/dist/shared/screenshotMatcher/types.d.ts +22 -0
- package/dist/snapshot.d.ts +9 -0
- package/dist/snapshot.js +4 -0
- package/dist/spy.js +1 -0
- package/dist/state.js +280 -0
- package/dist/suite.d.ts +5 -0
- package/dist/suite.js +6 -0
- package/dist/types.d.ts +69 -0
- package/dist/vendor/chai.d.mts +1 -0
- package/dist/vendor/chai.mjs +3577 -0
- package/dist/vendor/es-module-lexer.d.mts +193 -0
- package/dist/vendor/es-module-lexer.mjs +79 -0
- package/dist/vendor/estree-walker.d.mts +583 -0
- package/dist/vendor/estree-walker.mjs +339 -0
- package/dist/vendor/expect-type.d.mts +1574 -0
- package/dist/vendor/expect-type.mjs +214 -0
- package/dist/vendor/magic-string.d.mts +261 -0
- package/dist/vendor/magic-string.mjs +1700 -0
- package/dist/vendor/obug.d.mts +56 -0
- package/dist/vendor/obug.mjs +276 -0
- package/dist/vendor/pathe.d.mts +46 -0
- package/dist/vendor/pathe.mjs +496 -0
- package/dist/vendor/picomatch.d.mts +1 -0
- package/dist/vendor/picomatch.mjs +1855 -0
- package/dist/vendor/shared-3g9mwCWP.mjs +31 -0
- package/dist/vendor/std-env.d.mts +88 -0
- package/dist/vendor/std-env.mjs +159 -0
- package/dist/vendor/tinybench.d.mts +317 -0
- package/dist/vendor/tinybench.mjs +504 -0
- package/dist/vendor/tinyexec.d.mts +72 -0
- package/dist/vendor/tinyexec.mjs +637 -0
- package/dist/vendor/tinyglobby.d.mts +157 -0
- package/dist/vendor/tinyglobby.mjs +832 -0
- package/dist/vendor/tinyrainbow.d.mts +60 -0
- package/dist/vendor/tinyrainbow.mjs +93 -0
- package/dist/vendor/vitest_browser.mjs +2 -0
- package/dist/vendor/vitest_internal_browser.mjs +2 -0
- package/dist/vendor/vitest_runner.mjs +2 -0
- package/dist/vendor/vitest_runners.mjs +2 -0
- package/dist/worker.d.ts +32 -0
- package/dist/worker.js +48 -0
- package/dist/workers/forks.js +54 -0
- package/dist/workers/runVmTests.js +95 -0
- package/dist/workers/threads.js +55 -0
- package/dist/workers/vmForks.js +36 -0
- package/dist/workers/vmThreads.js +37 -0
- package/environments.d.ts +1 -0
- package/globals.d.ts +20 -0
- package/import-meta.d.ts +5 -0
- package/importMeta.d.ts +4 -0
- package/index.cjs +5 -0
- package/index.d.cts +1 -0
- package/jsdom.d.ts +6 -0
- package/mocker.d.ts +1 -0
- package/node.d.ts +1 -0
- package/optional-types.d.ts +7 -0
- package/package.json +335 -0
- package/reporters.d.ts +1 -0
- package/runners.d.ts +1 -0
- package/snapshot.d.ts +1 -0
- package/suite.d.ts +1 -0
- package/suppress-warnings.cjs +21 -0
- package/vitest.mjs +2 -0
- package/worker.d.ts +1 -0
|
@@ -0,0 +1,1875 @@
|
|
|
1
|
+
import { printDiffOrStringify, diff } from '../utils/diff.js';
|
|
2
|
+
import { stringify } from '../utils/display.js';
|
|
3
|
+
import { getType, isObject, noop, assertTypes } from '../utils/helpers.js';
|
|
4
|
+
import c from '../../vendor/tinyrainbow.mjs';
|
|
5
|
+
import { isMockFunction } from '../spy/index.js';
|
|
6
|
+
import { processError } from '../utils/error.js';
|
|
7
|
+
import { use, util } from '../../vendor/chai.mjs';
|
|
8
|
+
import * as chai from '../../vendor/chai.mjs';
|
|
9
|
+
export { chai };
|
|
10
|
+
|
|
11
|
+
const MATCHERS_OBJECT = Symbol.for("matchers-object");
|
|
12
|
+
const JEST_MATCHERS_OBJECT = Symbol.for("$$jest-matchers-object");
|
|
13
|
+
const GLOBAL_EXPECT = Symbol.for("expect-global");
|
|
14
|
+
const ASYMMETRIC_MATCHERS_OBJECT = Symbol.for("asymmetric-matchers-object");
|
|
15
|
+
|
|
16
|
+
// selectively ported from https://github.com/jest-community/jest-extended
|
|
17
|
+
const customMatchers = {
|
|
18
|
+
toSatisfy(actual, expected, message) {
|
|
19
|
+
const { printReceived, printExpected, matcherHint } = this.utils;
|
|
20
|
+
const pass = expected(actual);
|
|
21
|
+
return {
|
|
22
|
+
pass,
|
|
23
|
+
message: () => pass ? `\
|
|
24
|
+
${matcherHint(".not.toSatisfy", "received", "")}
|
|
25
|
+
|
|
26
|
+
Expected value to not satisfy:
|
|
27
|
+
${message || printExpected(expected)}
|
|
28
|
+
Received:
|
|
29
|
+
${printReceived(actual)}` : `\
|
|
30
|
+
${matcherHint(".toSatisfy", "received", "")}
|
|
31
|
+
|
|
32
|
+
Expected value to satisfy:
|
|
33
|
+
${message || printExpected(expected)}
|
|
34
|
+
|
|
35
|
+
Received:
|
|
36
|
+
${printReceived(actual)}`
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
toBeOneOf(actual, expected) {
|
|
40
|
+
const { equals, customTesters } = this;
|
|
41
|
+
const { printReceived, printExpected, matcherHint } = this.utils;
|
|
42
|
+
let pass;
|
|
43
|
+
if (Array.isArray(expected)) {
|
|
44
|
+
pass = expected.length === 0 || expected.some((item) => equals(item, actual, customTesters));
|
|
45
|
+
} else if (expected instanceof Set) {
|
|
46
|
+
pass = expected.size === 0 || expected.has(actual) || [...expected].some((item) => equals(item, actual, customTesters));
|
|
47
|
+
} else {
|
|
48
|
+
throw new TypeError(`You must provide an array or set to ${matcherHint(".toBeOneOf")}, not '${typeof expected}'.`);
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
pass,
|
|
52
|
+
message: () => pass ? `\
|
|
53
|
+
${matcherHint(".not.toBeOneOf", "received", "")}
|
|
54
|
+
|
|
55
|
+
Expected value to not be one of:
|
|
56
|
+
${printExpected(expected)}
|
|
57
|
+
Received:
|
|
58
|
+
${printReceived(actual)}` : `\
|
|
59
|
+
${matcherHint(".toBeOneOf", "received", "")}
|
|
60
|
+
|
|
61
|
+
Expected value to be one of:
|
|
62
|
+
${printExpected(expected)}
|
|
63
|
+
|
|
64
|
+
Received:
|
|
65
|
+
${printReceived(actual)}`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const EXPECTED_COLOR = c.green;
|
|
71
|
+
const RECEIVED_COLOR = c.red;
|
|
72
|
+
const INVERTED_COLOR = c.inverse;
|
|
73
|
+
const BOLD_WEIGHT = c.bold;
|
|
74
|
+
const DIM_COLOR = c.dim;
|
|
75
|
+
function matcherHint(matcherName, received = "received", expected = "expected", options = {}) {
|
|
76
|
+
const { comment = "", isDirectExpectCall = false, isNot = false, promise = "", secondArgument = "", expectedColor = EXPECTED_COLOR, receivedColor = RECEIVED_COLOR, secondArgumentColor = EXPECTED_COLOR } = options;
|
|
77
|
+
let hint = "";
|
|
78
|
+
let dimString = "expect";
|
|
79
|
+
if (!isDirectExpectCall && received !== "") {
|
|
80
|
+
hint += DIM_COLOR(`${dimString}(`) + receivedColor(received);
|
|
81
|
+
dimString = ")";
|
|
82
|
+
}
|
|
83
|
+
if (promise !== "") {
|
|
84
|
+
hint += DIM_COLOR(`${dimString}.`) + promise;
|
|
85
|
+
dimString = "";
|
|
86
|
+
}
|
|
87
|
+
if (isNot) {
|
|
88
|
+
hint += `${DIM_COLOR(`${dimString}.`)}not`;
|
|
89
|
+
dimString = "";
|
|
90
|
+
}
|
|
91
|
+
if (matcherName.includes(".")) {
|
|
92
|
+
// Old format: for backward compatibility,
|
|
93
|
+
// especially without promise or isNot options
|
|
94
|
+
dimString += matcherName;
|
|
95
|
+
} else {
|
|
96
|
+
// New format: omit period from matcherName arg
|
|
97
|
+
hint += DIM_COLOR(`${dimString}.`) + matcherName;
|
|
98
|
+
dimString = "";
|
|
99
|
+
}
|
|
100
|
+
if (expected === "") {
|
|
101
|
+
dimString += "()";
|
|
102
|
+
} else {
|
|
103
|
+
hint += DIM_COLOR(`${dimString}(`) + expectedColor(expected);
|
|
104
|
+
if (secondArgument) {
|
|
105
|
+
hint += DIM_COLOR(", ") + secondArgumentColor(secondArgument);
|
|
106
|
+
}
|
|
107
|
+
dimString = ")";
|
|
108
|
+
}
|
|
109
|
+
if (comment !== "") {
|
|
110
|
+
dimString += ` // ${comment}`;
|
|
111
|
+
}
|
|
112
|
+
if (dimString !== "") {
|
|
113
|
+
hint += DIM_COLOR(dimString);
|
|
114
|
+
}
|
|
115
|
+
return hint;
|
|
116
|
+
}
|
|
117
|
+
const SPACE_SYMBOL = "·";
|
|
118
|
+
// Instead of inverse highlight which now implies a change,
|
|
119
|
+
// replace common spaces with middle dot at the end of any line.
|
|
120
|
+
function replaceTrailingSpaces(text) {
|
|
121
|
+
return text.replace(/\s+$/gm, (spaces) => SPACE_SYMBOL.repeat(spaces.length));
|
|
122
|
+
}
|
|
123
|
+
function printReceived(object) {
|
|
124
|
+
return RECEIVED_COLOR(replaceTrailingSpaces(stringify(object)));
|
|
125
|
+
}
|
|
126
|
+
function printExpected(value) {
|
|
127
|
+
return EXPECTED_COLOR(replaceTrailingSpaces(stringify(value)));
|
|
128
|
+
}
|
|
129
|
+
function getMatcherUtils() {
|
|
130
|
+
return {
|
|
131
|
+
EXPECTED_COLOR,
|
|
132
|
+
RECEIVED_COLOR,
|
|
133
|
+
INVERTED_COLOR,
|
|
134
|
+
BOLD_WEIGHT,
|
|
135
|
+
DIM_COLOR,
|
|
136
|
+
diff,
|
|
137
|
+
matcherHint,
|
|
138
|
+
printReceived,
|
|
139
|
+
printExpected,
|
|
140
|
+
printDiffOrStringify,
|
|
141
|
+
printWithType
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function printWithType(name, value, print) {
|
|
145
|
+
const type = getType(value);
|
|
146
|
+
const hasType = type !== "null" && type !== "undefined" ? `${name} has type: ${type}\n` : "";
|
|
147
|
+
const hasValue = `${name} has value: ${print(value)}`;
|
|
148
|
+
return hasType + hasValue;
|
|
149
|
+
}
|
|
150
|
+
function addCustomEqualityTesters(newTesters) {
|
|
151
|
+
if (!Array.isArray(newTesters)) {
|
|
152
|
+
throw new TypeError(`expect.customEqualityTesters: Must be set to an array of Testers. Was given "${getType(newTesters)}"`);
|
|
153
|
+
}
|
|
154
|
+
globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters.push(...newTesters);
|
|
155
|
+
}
|
|
156
|
+
function getCustomEqualityTesters() {
|
|
157
|
+
return globalThis[JEST_MATCHERS_OBJECT].customEqualityTesters;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Extracted out of jasmine 2.5.2
|
|
161
|
+
function equals(a, b, customTesters, strictCheck) {
|
|
162
|
+
customTesters = customTesters || [];
|
|
163
|
+
return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey);
|
|
164
|
+
}
|
|
165
|
+
const functionToString = Function.prototype.toString;
|
|
166
|
+
function isAsymmetric(obj) {
|
|
167
|
+
return !!obj && typeof obj === "object" && "asymmetricMatch" in obj && isA("Function", obj.asymmetricMatch);
|
|
168
|
+
}
|
|
169
|
+
function hasAsymmetric(obj, seen = new Set()) {
|
|
170
|
+
if (seen.has(obj)) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
seen.add(obj);
|
|
174
|
+
if (isAsymmetric(obj)) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(obj)) {
|
|
178
|
+
return obj.some((i) => hasAsymmetric(i, seen));
|
|
179
|
+
}
|
|
180
|
+
if (obj instanceof Set) {
|
|
181
|
+
return Array.from(obj).some((i) => hasAsymmetric(i, seen));
|
|
182
|
+
}
|
|
183
|
+
if (isObject(obj)) {
|
|
184
|
+
return Object.values(obj).some((v) => hasAsymmetric(v, seen));
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
function asymmetricMatch(a, b, customTesters) {
|
|
189
|
+
const asymmetricA = isAsymmetric(a);
|
|
190
|
+
const asymmetricB = isAsymmetric(b);
|
|
191
|
+
if (asymmetricA && asymmetricB) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
if (asymmetricA) {
|
|
195
|
+
return a.asymmetricMatch(b, customTesters);
|
|
196
|
+
}
|
|
197
|
+
if (asymmetricB) {
|
|
198
|
+
return b.asymmetricMatch(a, customTesters);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Equality function lovingly adapted from isEqual in
|
|
202
|
+
// [Underscore](http://underscorejs.org)
|
|
203
|
+
function eq(a, b, aStack, bStack, customTesters, hasKey) {
|
|
204
|
+
let result = true;
|
|
205
|
+
const asymmetricResult = asymmetricMatch(a, b, customTesters);
|
|
206
|
+
if (asymmetricResult !== undefined) {
|
|
207
|
+
return asymmetricResult;
|
|
208
|
+
}
|
|
209
|
+
const testerContext = { equals };
|
|
210
|
+
for (let i = 0; i < customTesters.length; i++) {
|
|
211
|
+
const customTesterResult = customTesters[i].call(testerContext, a, b, customTesters);
|
|
212
|
+
if (customTesterResult !== undefined) {
|
|
213
|
+
return customTesterResult;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (typeof URL === "function" && a instanceof URL && b instanceof URL) {
|
|
217
|
+
return a.href === b.href;
|
|
218
|
+
}
|
|
219
|
+
if (Object.is(a, b)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
// A strict comparison is necessary because `null == undefined`.
|
|
223
|
+
if (a === null || b === null) {
|
|
224
|
+
return a === b;
|
|
225
|
+
}
|
|
226
|
+
const className = Object.prototype.toString.call(a);
|
|
227
|
+
if (className !== Object.prototype.toString.call(b)) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
switch (className) {
|
|
231
|
+
case "[object Boolean]":
|
|
232
|
+
case "[object String]":
|
|
233
|
+
case "[object Number]": if (typeof a !== typeof b) {
|
|
234
|
+
// One is a primitive, one a `new Primitive()`
|
|
235
|
+
return false;
|
|
236
|
+
} else if (typeof a !== "object" && typeof b !== "object") {
|
|
237
|
+
// both are proper primitives
|
|
238
|
+
return Object.is(a, b);
|
|
239
|
+
} else {
|
|
240
|
+
// both are `new Primitive()`s
|
|
241
|
+
return Object.is(a.valueOf(), b.valueOf());
|
|
242
|
+
}
|
|
243
|
+
case "[object Date]": {
|
|
244
|
+
const numA = +a;
|
|
245
|
+
const numB = +b;
|
|
246
|
+
// Coerce dates to numeric primitive values. Dates are compared by their
|
|
247
|
+
// millisecond representations. Note that invalid dates with millisecond representations
|
|
248
|
+
// of `NaN` are equivalent.
|
|
249
|
+
return numA === numB || Number.isNaN(numA) && Number.isNaN(numB);
|
|
250
|
+
}
|
|
251
|
+
case "[object RegExp]": return a.source === b.source && a.flags === b.flags;
|
|
252
|
+
case "[object Temporal.Instant]":
|
|
253
|
+
case "[object Temporal.ZonedDateTime]":
|
|
254
|
+
case "[object Temporal.PlainDateTime]":
|
|
255
|
+
case "[object Temporal.PlainDate]":
|
|
256
|
+
case "[object Temporal.PlainTime]":
|
|
257
|
+
case "[object Temporal.PlainYearMonth]":
|
|
258
|
+
case "[object Temporal.PlainMonthDay]": return a.equals(b);
|
|
259
|
+
case "[object Temporal.Duration]": return a.toString() === b.toString();
|
|
260
|
+
}
|
|
261
|
+
if (typeof a !== "object" || typeof b !== "object") {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
// Use DOM3 method isEqualNode (IE>=9)
|
|
265
|
+
if (isDomNode(a) && isDomNode(b)) {
|
|
266
|
+
return a.isEqualNode(b);
|
|
267
|
+
}
|
|
268
|
+
// Used to detect circular references.
|
|
269
|
+
let length = aStack.length;
|
|
270
|
+
while (length--) {
|
|
271
|
+
// Linear search. Performance is inversely proportional to the number of
|
|
272
|
+
// unique nested structures.
|
|
273
|
+
// circular references at same depth are equal
|
|
274
|
+
// circular reference is not equal to non-circular one
|
|
275
|
+
if (aStack[length] === a) {
|
|
276
|
+
return bStack[length] === b;
|
|
277
|
+
} else if (bStack[length] === b) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Add the first object to the stack of traversed objects.
|
|
282
|
+
aStack.push(a);
|
|
283
|
+
bStack.push(b);
|
|
284
|
+
// Recursively compare objects and arrays.
|
|
285
|
+
// Compare array lengths to determine if a deep comparison is necessary.
|
|
286
|
+
if (className === "[object Array]" && a.length !== b.length) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
if (a instanceof Error && b instanceof Error) {
|
|
290
|
+
try {
|
|
291
|
+
return isErrorEqual(a, b, aStack, bStack, customTesters, hasKey);
|
|
292
|
+
} finally {
|
|
293
|
+
aStack.pop();
|
|
294
|
+
bStack.pop();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Deep compare objects.
|
|
298
|
+
const aKeys = keys(a, hasKey);
|
|
299
|
+
let key;
|
|
300
|
+
let size = aKeys.length;
|
|
301
|
+
// Ensure that both objects contain the same number of properties before comparing deep equality.
|
|
302
|
+
if (keys(b, hasKey).length !== size) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
while (size--) {
|
|
306
|
+
key = aKeys[size];
|
|
307
|
+
// Deep compare each member
|
|
308
|
+
result = hasKey(b, key) && eq(a[key], b[key], aStack, bStack, customTesters, hasKey);
|
|
309
|
+
if (!result) {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Remove the first object from the stack of traversed objects.
|
|
314
|
+
aStack.pop();
|
|
315
|
+
bStack.pop();
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
function isErrorEqual(a, b, aStack, bStack, customTesters, hasKey) {
|
|
319
|
+
// https://nodejs.org/docs/latest-v22.x/api/assert.html#comparison-details
|
|
320
|
+
// - [[Prototype]] of objects are compared using the === operator.
|
|
321
|
+
// - Only enumerable "own" properties are considered.
|
|
322
|
+
// - Error names, messages, causes, and errors are always compared, even if these are not enumerable properties. errors is also compared.
|
|
323
|
+
let result = Object.getPrototypeOf(a) === Object.getPrototypeOf(b) && a.name === b.name && a.message === b.message;
|
|
324
|
+
// check Error.cause asymmetrically
|
|
325
|
+
if (typeof b.cause !== "undefined") {
|
|
326
|
+
result && (result = eq(a.cause, b.cause, aStack, bStack, customTesters, hasKey));
|
|
327
|
+
}
|
|
328
|
+
// AggregateError.errors
|
|
329
|
+
if (a instanceof AggregateError && b instanceof AggregateError) {
|
|
330
|
+
result && (result = eq(a.errors, b.errors, aStack, bStack, customTesters, hasKey));
|
|
331
|
+
}
|
|
332
|
+
// spread to compare enumerable properties
|
|
333
|
+
result && (result = eq({ ...a }, { ...b }, aStack, bStack, customTesters, hasKey));
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
function keys(obj, hasKey) {
|
|
337
|
+
const keys = [];
|
|
338
|
+
for (const key in obj) {
|
|
339
|
+
if (hasKey(obj, key)) {
|
|
340
|
+
keys.push(key);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return keys.concat(Object.getOwnPropertySymbols(obj).filter((symbol) => Object.getOwnPropertyDescriptor(obj, symbol).enumerable));
|
|
344
|
+
}
|
|
345
|
+
function hasDefinedKey(obj, key) {
|
|
346
|
+
return hasKey(obj, key) && obj[key] !== undefined;
|
|
347
|
+
}
|
|
348
|
+
function hasKey(obj, key) {
|
|
349
|
+
return Object.hasOwn(obj, key);
|
|
350
|
+
}
|
|
351
|
+
function isA(typeName, value) {
|
|
352
|
+
return Object.prototype.toString.apply(value) === `[object ${typeName}]`;
|
|
353
|
+
}
|
|
354
|
+
function isDomNode(obj) {
|
|
355
|
+
return obj !== null && typeof obj === "object" && "nodeType" in obj && typeof obj.nodeType === "number" && "nodeName" in obj && typeof obj.nodeName === "string" && "isEqualNode" in obj && typeof obj.isEqualNode === "function";
|
|
356
|
+
}
|
|
357
|
+
function fnNameFor(func) {
|
|
358
|
+
if (func.name) {
|
|
359
|
+
return func.name;
|
|
360
|
+
}
|
|
361
|
+
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
|
|
362
|
+
return matches ? matches[1] : "<anonymous>";
|
|
363
|
+
}
|
|
364
|
+
function getPrototype(obj) {
|
|
365
|
+
if (Object.getPrototypeOf) {
|
|
366
|
+
return Object.getPrototypeOf(obj);
|
|
367
|
+
}
|
|
368
|
+
if (obj.constructor.prototype === obj) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
return obj.constructor.prototype;
|
|
372
|
+
}
|
|
373
|
+
function hasProperty(obj, property) {
|
|
374
|
+
if (!obj) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
if (Object.hasOwn(obj, property)) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
return hasProperty(getPrototype(obj), property);
|
|
381
|
+
}
|
|
382
|
+
// SENTINEL constants are from https://github.com/facebook/immutable-js
|
|
383
|
+
const IS_KEYED_SENTINEL = "@@__IMMUTABLE_KEYED__@@";
|
|
384
|
+
const IS_SET_SENTINEL = "@@__IMMUTABLE_SET__@@";
|
|
385
|
+
const IS_LIST_SENTINEL = "@@__IMMUTABLE_LIST__@@";
|
|
386
|
+
const IS_ORDERED_SENTINEL = "@@__IMMUTABLE_ORDERED__@@";
|
|
387
|
+
const IS_RECORD_SYMBOL = "@@__IMMUTABLE_RECORD__@@";
|
|
388
|
+
function isImmutableUnorderedKeyed(maybeKeyed) {
|
|
389
|
+
return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL] && !maybeKeyed[IS_ORDERED_SENTINEL]);
|
|
390
|
+
}
|
|
391
|
+
function isImmutableUnorderedSet(maybeSet) {
|
|
392
|
+
return !!(maybeSet && maybeSet[IS_SET_SENTINEL] && !maybeSet[IS_ORDERED_SENTINEL]);
|
|
393
|
+
}
|
|
394
|
+
function isObjectLiteral(source) {
|
|
395
|
+
return source != null && typeof source === "object" && !Array.isArray(source);
|
|
396
|
+
}
|
|
397
|
+
function isImmutableList(source) {
|
|
398
|
+
return Boolean(source && isObjectLiteral(source) && source[IS_LIST_SENTINEL]);
|
|
399
|
+
}
|
|
400
|
+
function isImmutableOrderedKeyed(source) {
|
|
401
|
+
return Boolean(source && isObjectLiteral(source) && source[IS_KEYED_SENTINEL] && source[IS_ORDERED_SENTINEL]);
|
|
402
|
+
}
|
|
403
|
+
function isImmutableOrderedSet(source) {
|
|
404
|
+
return Boolean(source && isObjectLiteral(source) && source[IS_SET_SENTINEL] && source[IS_ORDERED_SENTINEL]);
|
|
405
|
+
}
|
|
406
|
+
function isImmutableRecord(source) {
|
|
407
|
+
return Boolean(source && isObjectLiteral(source) && source[IS_RECORD_SYMBOL]);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
|
411
|
+
*
|
|
412
|
+
* This source code is licensed under the MIT license found in the
|
|
413
|
+
* LICENSE file in the root directory of this source tree.
|
|
414
|
+
*
|
|
415
|
+
*/
|
|
416
|
+
const IteratorSymbol = Symbol.iterator;
|
|
417
|
+
function hasIterator(object) {
|
|
418
|
+
return !!(object != null && object[IteratorSymbol]);
|
|
419
|
+
}
|
|
420
|
+
function iterableEquality(a, b, customTesters = [], aStack = [], bStack = []) {
|
|
421
|
+
if (typeof a !== "object" || typeof b !== "object" || Array.isArray(a) || Array.isArray(b) || !hasIterator(a) || !hasIterator(b)) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
if (a.constructor !== b.constructor) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
let length = aStack.length;
|
|
428
|
+
while (length--) {
|
|
429
|
+
// Linear search. Performance is inversely proportional to the number of
|
|
430
|
+
// unique nested structures.
|
|
431
|
+
// circular references at same depth are equal
|
|
432
|
+
// circular reference is not equal to non-circular one
|
|
433
|
+
if (aStack[length] === a) {
|
|
434
|
+
return bStack[length] === b;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
aStack.push(a);
|
|
438
|
+
bStack.push(b);
|
|
439
|
+
const filteredCustomTesters = [...customTesters.filter((t) => t !== iterableEquality), iterableEqualityWithStack];
|
|
440
|
+
function iterableEqualityWithStack(a, b) {
|
|
441
|
+
return iterableEquality(a, b, [...customTesters], [...aStack], [...bStack]);
|
|
442
|
+
}
|
|
443
|
+
if (a.size !== undefined) {
|
|
444
|
+
if (a.size !== b.size) {
|
|
445
|
+
return false;
|
|
446
|
+
} else if (isA("Set", a) || isImmutableUnorderedSet(a)) {
|
|
447
|
+
let allFound = true;
|
|
448
|
+
for (const aValue of a) {
|
|
449
|
+
if (!b.has(aValue)) {
|
|
450
|
+
let has = false;
|
|
451
|
+
for (const bValue of b) {
|
|
452
|
+
const isEqual = equals(aValue, bValue, filteredCustomTesters);
|
|
453
|
+
if (isEqual === true) {
|
|
454
|
+
has = true;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (has === false) {
|
|
458
|
+
allFound = false;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
// Remove the first value from the stack of traversed values.
|
|
464
|
+
aStack.pop();
|
|
465
|
+
bStack.pop();
|
|
466
|
+
return allFound;
|
|
467
|
+
} else if (isA("Map", a) || isImmutableUnorderedKeyed(a)) {
|
|
468
|
+
let allFound = true;
|
|
469
|
+
for (const aEntry of a) {
|
|
470
|
+
if (!b.has(aEntry[0]) || !equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)) {
|
|
471
|
+
let has = false;
|
|
472
|
+
for (const bEntry of b) {
|
|
473
|
+
const matchedKey = equals(aEntry[0], bEntry[0], filteredCustomTesters);
|
|
474
|
+
let matchedValue = false;
|
|
475
|
+
if (matchedKey === true) {
|
|
476
|
+
matchedValue = equals(aEntry[1], bEntry[1], filteredCustomTesters);
|
|
477
|
+
}
|
|
478
|
+
if (matchedValue === true) {
|
|
479
|
+
has = true;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (has === false) {
|
|
483
|
+
allFound = false;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Remove the first value from the stack of traversed values.
|
|
489
|
+
aStack.pop();
|
|
490
|
+
bStack.pop();
|
|
491
|
+
return allFound;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
const bIterator = b[IteratorSymbol]();
|
|
495
|
+
for (const aValue of a) {
|
|
496
|
+
const nextB = bIterator.next();
|
|
497
|
+
if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters)) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (!bIterator.next().done) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
if (!isImmutableList(a) && !isImmutableOrderedKeyed(a) && !isImmutableOrderedSet(a) && !isImmutableRecord(a)) {
|
|
505
|
+
const aEntries = Object.entries(a);
|
|
506
|
+
const bEntries = Object.entries(b);
|
|
507
|
+
if (!equals(aEntries, bEntries, filteredCustomTesters)) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
// Remove the first value from the stack of traversed values.
|
|
512
|
+
aStack.pop();
|
|
513
|
+
bStack.pop();
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Checks if `hasOwnProperty(object, key)` up the prototype chain, stopping at `Object.prototype`.
|
|
518
|
+
*/
|
|
519
|
+
function hasPropertyInObject(object, key) {
|
|
520
|
+
const shouldTerminate = !object || typeof object !== "object" || object === Object.prototype;
|
|
521
|
+
if (shouldTerminate) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
return Object.hasOwn(object, key) || hasPropertyInObject(Object.getPrototypeOf(object), key);
|
|
525
|
+
}
|
|
526
|
+
function isObjectWithKeys(a) {
|
|
527
|
+
return isObject(a) && !(a instanceof Error) && !Array.isArray(a) && !(a instanceof Date);
|
|
528
|
+
}
|
|
529
|
+
function subsetEquality(object, subset, customTesters = []) {
|
|
530
|
+
const filteredCustomTesters = customTesters.filter((t) => t !== subsetEquality);
|
|
531
|
+
// subsetEquality needs to keep track of the references
|
|
532
|
+
// it has already visited to avoid infinite loops in case
|
|
533
|
+
// there are circular references in the subset passed to it.
|
|
534
|
+
const subsetEqualityWithContext = (seenReferences = new WeakMap()) => (object, subset) => {
|
|
535
|
+
if (!isObjectWithKeys(subset)) {
|
|
536
|
+
return undefined;
|
|
537
|
+
}
|
|
538
|
+
return Object.keys(subset).every((key) => {
|
|
539
|
+
if (subset[key] != null && typeof subset[key] === "object") {
|
|
540
|
+
if (seenReferences.has(subset[key])) {
|
|
541
|
+
return equals(object[key], subset[key], filteredCustomTesters);
|
|
542
|
+
}
|
|
543
|
+
seenReferences.set(subset[key], true);
|
|
544
|
+
}
|
|
545
|
+
const result = object != null && hasPropertyInObject(object, key) && equals(object[key], subset[key], [...filteredCustomTesters, subsetEqualityWithContext(seenReferences)]);
|
|
546
|
+
// The main goal of using seenReference is to avoid circular node on tree.
|
|
547
|
+
// It will only happen within a parent and its child, not a node and nodes next to it (same level)
|
|
548
|
+
// We should keep the reference for a parent and its child only
|
|
549
|
+
// Thus we should delete the reference immediately so that it doesn't interfere
|
|
550
|
+
// other nodes within the same level on tree.
|
|
551
|
+
seenReferences.delete(subset[key]);
|
|
552
|
+
return result;
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
return subsetEqualityWithContext()(object, subset);
|
|
556
|
+
}
|
|
557
|
+
function typeEquality(a, b) {
|
|
558
|
+
if (a == null || b == null || a.constructor === b.constructor) {
|
|
559
|
+
return undefined;
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
function arrayBufferEquality(a, b) {
|
|
564
|
+
let dataViewA = a;
|
|
565
|
+
let dataViewB = b;
|
|
566
|
+
if (!(a instanceof DataView && b instanceof DataView)) {
|
|
567
|
+
if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
dataViewA = new DataView(a);
|
|
572
|
+
dataViewB = new DataView(b);
|
|
573
|
+
} catch {
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
// Buffers are not equal when they do not have the same byte length
|
|
578
|
+
if (dataViewA.byteLength !== dataViewB.byteLength) {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
// Check if every byte value is equal to each other
|
|
582
|
+
for (let i = 0; i < dataViewA.byteLength; i++) {
|
|
583
|
+
if (dataViewA.getUint8(i) !== dataViewB.getUint8(i)) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
function sparseArrayEquality(a, b, customTesters = []) {
|
|
590
|
+
if (!Array.isArray(a) || !Array.isArray(b)) {
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
593
|
+
// A sparse array [, , 1] will have keys ["2"] whereas [undefined, undefined, 1] will have keys ["0", "1", "2"]
|
|
594
|
+
const aKeys = Object.keys(a);
|
|
595
|
+
const bKeys = Object.keys(b);
|
|
596
|
+
const filteredCustomTesters = customTesters.filter((t) => t !== sparseArrayEquality);
|
|
597
|
+
return equals(a, b, filteredCustomTesters, true) && equals(aKeys, bKeys);
|
|
598
|
+
}
|
|
599
|
+
function generateToBeMessage(deepEqualityName, expected = "#{this}", actual = "#{exp}") {
|
|
600
|
+
const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality`;
|
|
601
|
+
if (["toStrictEqual", "toEqual"].includes(deepEqualityName)) {
|
|
602
|
+
return `${toBeMessage}\n\nIf it should pass with deep equality, replace "toBe" with "${deepEqualityName}"\n\nExpected: ${expected}\nReceived: serializes to the same string\n`;
|
|
603
|
+
}
|
|
604
|
+
return toBeMessage;
|
|
605
|
+
}
|
|
606
|
+
function pluralize(word, count) {
|
|
607
|
+
return `${count} ${word}${count === 1 ? "" : "s"}`;
|
|
608
|
+
}
|
|
609
|
+
function getObjectKeys(object) {
|
|
610
|
+
return [...Object.keys(object), ...Object.getOwnPropertySymbols(object).filter((s) => {
|
|
611
|
+
var _Object$getOwnPropert;
|
|
612
|
+
return (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(object, s)) === null || _Object$getOwnPropert === void 0 ? void 0 : _Object$getOwnPropert.enumerable;
|
|
613
|
+
})];
|
|
614
|
+
}
|
|
615
|
+
function getObjectSubset(object, subset, customTesters) {
|
|
616
|
+
let stripped = 0;
|
|
617
|
+
const getObjectSubsetWithContext = (seenReferences = new WeakMap()) => (object, subset) => {
|
|
618
|
+
if (Array.isArray(object)) {
|
|
619
|
+
if (Array.isArray(subset) && subset.length === object.length) {
|
|
620
|
+
// The map method returns correct subclass of subset.
|
|
621
|
+
return subset.map((sub, i) => getObjectSubsetWithContext(seenReferences)(object[i], sub));
|
|
622
|
+
}
|
|
623
|
+
} else if (object instanceof Date) {
|
|
624
|
+
return object;
|
|
625
|
+
} else if (isObject(object) && isObject(subset)) {
|
|
626
|
+
if (equals(object, subset, [
|
|
627
|
+
...customTesters,
|
|
628
|
+
iterableEquality,
|
|
629
|
+
subsetEquality
|
|
630
|
+
])) {
|
|
631
|
+
// return "expected" subset to avoid showing irrelevant toMatchObject diff
|
|
632
|
+
return subset;
|
|
633
|
+
}
|
|
634
|
+
const trimmed = {};
|
|
635
|
+
seenReferences.set(object, trimmed);
|
|
636
|
+
// preserve constructor for toMatchObject diff
|
|
637
|
+
if (typeof object.constructor === "function" && typeof object.constructor.name === "string") {
|
|
638
|
+
Object.defineProperty(trimmed, "constructor", {
|
|
639
|
+
enumerable: false,
|
|
640
|
+
value: object.constructor
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
for (const key of getObjectKeys(object)) {
|
|
644
|
+
if (hasPropertyInObject(subset, key)) {
|
|
645
|
+
trimmed[key] = seenReferences.has(object[key]) ? seenReferences.get(object[key]) : getObjectSubsetWithContext(seenReferences)(object[key], subset[key]);
|
|
646
|
+
} else {
|
|
647
|
+
if (!seenReferences.has(object[key])) {
|
|
648
|
+
stripped += 1;
|
|
649
|
+
if (isObject(object[key])) {
|
|
650
|
+
stripped += getObjectKeys(object[key]).length;
|
|
651
|
+
}
|
|
652
|
+
getObjectSubsetWithContext(seenReferences)(object[key], subset[key]);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (getObjectKeys(trimmed).length > 0) {
|
|
657
|
+
return trimmed;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return object;
|
|
661
|
+
};
|
|
662
|
+
return {
|
|
663
|
+
subset: getObjectSubsetWithContext()(object, subset),
|
|
664
|
+
stripped
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Detects if an object is a Standard Schema V1 compatible schema
|
|
669
|
+
*/
|
|
670
|
+
function isStandardSchema(obj) {
|
|
671
|
+
return !!obj && (typeof obj === "object" || typeof obj === "function") && obj["~standard"] && typeof obj["~standard"].validate === "function";
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (!Object.hasOwn(globalThis, MATCHERS_OBJECT)) {
|
|
675
|
+
const globalState = new WeakMap();
|
|
676
|
+
const matchers = Object.create(null);
|
|
677
|
+
const customEqualityTesters = [];
|
|
678
|
+
const asymmetricMatchers = Object.create(null);
|
|
679
|
+
Object.defineProperty(globalThis, MATCHERS_OBJECT, { get: () => globalState });
|
|
680
|
+
Object.defineProperty(globalThis, JEST_MATCHERS_OBJECT, {
|
|
681
|
+
configurable: true,
|
|
682
|
+
get: () => ({
|
|
683
|
+
state: globalState.get(globalThis[GLOBAL_EXPECT]),
|
|
684
|
+
matchers,
|
|
685
|
+
customEqualityTesters
|
|
686
|
+
})
|
|
687
|
+
});
|
|
688
|
+
Object.defineProperty(globalThis, ASYMMETRIC_MATCHERS_OBJECT, { get: () => asymmetricMatchers });
|
|
689
|
+
}
|
|
690
|
+
function getState(expect) {
|
|
691
|
+
return globalThis[MATCHERS_OBJECT].get(expect);
|
|
692
|
+
}
|
|
693
|
+
function setState(state, expect) {
|
|
694
|
+
const map = globalThis[MATCHERS_OBJECT];
|
|
695
|
+
const current = map.get(expect) || {};
|
|
696
|
+
// so it keeps getters from `testPath`
|
|
697
|
+
const results = Object.defineProperties(current, {
|
|
698
|
+
...Object.getOwnPropertyDescriptors(current),
|
|
699
|
+
...Object.getOwnPropertyDescriptors(state)
|
|
700
|
+
});
|
|
701
|
+
map.set(expect, results);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
class AsymmetricMatcher {
|
|
705
|
+
// should have "jest" to be compatible with its ecosystem
|
|
706
|
+
$$typeof = Symbol.for("jest.asymmetricMatcher");
|
|
707
|
+
constructor(sample, inverse = false) {
|
|
708
|
+
this.sample = sample;
|
|
709
|
+
this.inverse = inverse;
|
|
710
|
+
}
|
|
711
|
+
getMatcherContext(expect) {
|
|
712
|
+
return {
|
|
713
|
+
...getState(expect || globalThis[GLOBAL_EXPECT]),
|
|
714
|
+
equals,
|
|
715
|
+
isNot: this.inverse,
|
|
716
|
+
customTesters: getCustomEqualityTesters(),
|
|
717
|
+
utils: {
|
|
718
|
+
...getMatcherUtils(),
|
|
719
|
+
diff,
|
|
720
|
+
stringify,
|
|
721
|
+
iterableEquality,
|
|
722
|
+
subsetEquality
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// implement custom chai/loupe inspect for better AssertionError.message formatting
|
|
728
|
+
// https://github.com/chaijs/loupe/blob/9b8a6deabcd50adc056a64fb705896194710c5c6/src/index.ts#L29
|
|
729
|
+
// @ts-expect-error computed properties is not supported when isolatedDeclarations is enabled
|
|
730
|
+
// FIXME: https://github.com/microsoft/TypeScript/issues/61068
|
|
731
|
+
AsymmetricMatcher.prototype[Symbol.for("chai/inspect")] = function(options) {
|
|
732
|
+
// minimal pretty-format with simple manual truncation
|
|
733
|
+
const result = stringify(this, options.depth, { min: true });
|
|
734
|
+
if (result.length <= options.truncate) {
|
|
735
|
+
return result;
|
|
736
|
+
}
|
|
737
|
+
return `${this.toString()}{…}`;
|
|
738
|
+
};
|
|
739
|
+
class StringContaining extends AsymmetricMatcher {
|
|
740
|
+
constructor(sample, inverse = false) {
|
|
741
|
+
if (!isA("String", sample)) {
|
|
742
|
+
throw new Error("Expected is not a string");
|
|
743
|
+
}
|
|
744
|
+
super(sample, inverse);
|
|
745
|
+
}
|
|
746
|
+
asymmetricMatch(other) {
|
|
747
|
+
const result = isA("String", other) && other.includes(this.sample);
|
|
748
|
+
return this.inverse ? !result : result;
|
|
749
|
+
}
|
|
750
|
+
toString() {
|
|
751
|
+
return `String${this.inverse ? "Not" : ""}Containing`;
|
|
752
|
+
}
|
|
753
|
+
getExpectedType() {
|
|
754
|
+
return "string";
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
class Anything extends AsymmetricMatcher {
|
|
758
|
+
asymmetricMatch(other) {
|
|
759
|
+
return other != null;
|
|
760
|
+
}
|
|
761
|
+
toString() {
|
|
762
|
+
return "Anything";
|
|
763
|
+
}
|
|
764
|
+
toAsymmetricMatcher() {
|
|
765
|
+
return "Anything";
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
class ObjectContaining extends AsymmetricMatcher {
|
|
769
|
+
constructor(sample, inverse = false) {
|
|
770
|
+
super(sample, inverse);
|
|
771
|
+
}
|
|
772
|
+
getPrototype(obj) {
|
|
773
|
+
if (Object.getPrototypeOf) {
|
|
774
|
+
return Object.getPrototypeOf(obj);
|
|
775
|
+
}
|
|
776
|
+
if (obj.constructor.prototype === obj) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
return obj.constructor.prototype;
|
|
780
|
+
}
|
|
781
|
+
hasProperty(obj, property) {
|
|
782
|
+
if (!obj) {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
if (Object.hasOwn(obj, property)) {
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
return this.hasProperty(this.getPrototype(obj), property);
|
|
789
|
+
}
|
|
790
|
+
getProperties(obj) {
|
|
791
|
+
return [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj).filter((s) => {
|
|
792
|
+
var _Object$getOwnPropert;
|
|
793
|
+
return (_Object$getOwnPropert = Object.getOwnPropertyDescriptor(obj, s)) === null || _Object$getOwnPropert === void 0 ? void 0 : _Object$getOwnPropert.enumerable;
|
|
794
|
+
})];
|
|
795
|
+
}
|
|
796
|
+
asymmetricMatch(other, customTesters) {
|
|
797
|
+
if (typeof this.sample !== "object") {
|
|
798
|
+
throw new TypeError(`You must provide an object to ${this.toString()}, not '${typeof this.sample}'.`);
|
|
799
|
+
}
|
|
800
|
+
let result = true;
|
|
801
|
+
const properties = this.getProperties(this.sample);
|
|
802
|
+
for (const property of properties) {
|
|
803
|
+
var _Object$getOwnPropert2, _Object$getOwnPropert3;
|
|
804
|
+
if (!this.hasProperty(other, property)) {
|
|
805
|
+
result = false;
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
const value = ((_Object$getOwnPropert2 = Object.getOwnPropertyDescriptor(this.sample, property)) === null || _Object$getOwnPropert2 === void 0 ? void 0 : _Object$getOwnPropert2.value) ?? this.sample[property];
|
|
809
|
+
const otherValue = ((_Object$getOwnPropert3 = Object.getOwnPropertyDescriptor(other, property)) === null || _Object$getOwnPropert3 === void 0 ? void 0 : _Object$getOwnPropert3.value) ?? other[property];
|
|
810
|
+
if (!equals(value, otherValue, customTesters)) {
|
|
811
|
+
result = false;
|
|
812
|
+
break;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return this.inverse ? !result : result;
|
|
816
|
+
}
|
|
817
|
+
toString() {
|
|
818
|
+
return `Object${this.inverse ? "Not" : ""}Containing`;
|
|
819
|
+
}
|
|
820
|
+
getExpectedType() {
|
|
821
|
+
return "object";
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
class ArrayContaining extends AsymmetricMatcher {
|
|
825
|
+
constructor(sample, inverse = false) {
|
|
826
|
+
super(sample, inverse);
|
|
827
|
+
}
|
|
828
|
+
asymmetricMatch(other, customTesters) {
|
|
829
|
+
if (!Array.isArray(this.sample)) {
|
|
830
|
+
throw new TypeError(`You must provide an array to ${this.toString()}, not '${typeof this.sample}'.`);
|
|
831
|
+
}
|
|
832
|
+
const result = this.sample.length === 0 || Array.isArray(other) && this.sample.every((item) => other.some((another) => equals(item, another, customTesters)));
|
|
833
|
+
return this.inverse ? !result : result;
|
|
834
|
+
}
|
|
835
|
+
toString() {
|
|
836
|
+
return `Array${this.inverse ? "Not" : ""}Containing`;
|
|
837
|
+
}
|
|
838
|
+
getExpectedType() {
|
|
839
|
+
return "array";
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
class Any extends AsymmetricMatcher {
|
|
843
|
+
constructor(sample) {
|
|
844
|
+
if (typeof sample === "undefined") {
|
|
845
|
+
throw new TypeError("any() expects to be passed a constructor function. " + "Please pass one or use anything() to match any object.");
|
|
846
|
+
}
|
|
847
|
+
super(sample);
|
|
848
|
+
}
|
|
849
|
+
fnNameFor(func) {
|
|
850
|
+
if (func.name) {
|
|
851
|
+
return func.name;
|
|
852
|
+
}
|
|
853
|
+
const functionToString = Function.prototype.toString;
|
|
854
|
+
const matches = functionToString.call(func).match(/^(?:async)?\s*function\s*(?:\*\s*)?([\w$]+)\s*\(/);
|
|
855
|
+
return matches ? matches[1] : "<anonymous>";
|
|
856
|
+
}
|
|
857
|
+
asymmetricMatch(other) {
|
|
858
|
+
if (this.sample === String) {
|
|
859
|
+
return typeof other == "string" || other instanceof String;
|
|
860
|
+
}
|
|
861
|
+
if (this.sample === Number) {
|
|
862
|
+
return typeof other == "number" || other instanceof Number;
|
|
863
|
+
}
|
|
864
|
+
if (this.sample === Function) {
|
|
865
|
+
return typeof other == "function" || typeof other === "function";
|
|
866
|
+
}
|
|
867
|
+
if (this.sample === Boolean) {
|
|
868
|
+
return typeof other == "boolean" || other instanceof Boolean;
|
|
869
|
+
}
|
|
870
|
+
if (this.sample === BigInt) {
|
|
871
|
+
return typeof other == "bigint" || other instanceof BigInt;
|
|
872
|
+
}
|
|
873
|
+
if (this.sample === Symbol) {
|
|
874
|
+
return typeof other == "symbol" || other instanceof Symbol;
|
|
875
|
+
}
|
|
876
|
+
if (this.sample === Object) {
|
|
877
|
+
return typeof other == "object";
|
|
878
|
+
}
|
|
879
|
+
return other instanceof this.sample;
|
|
880
|
+
}
|
|
881
|
+
toString() {
|
|
882
|
+
return "Any";
|
|
883
|
+
}
|
|
884
|
+
getExpectedType() {
|
|
885
|
+
if (this.sample === String) {
|
|
886
|
+
return "string";
|
|
887
|
+
}
|
|
888
|
+
if (this.sample === Number) {
|
|
889
|
+
return "number";
|
|
890
|
+
}
|
|
891
|
+
if (this.sample === Function) {
|
|
892
|
+
return "function";
|
|
893
|
+
}
|
|
894
|
+
if (this.sample === Object) {
|
|
895
|
+
return "object";
|
|
896
|
+
}
|
|
897
|
+
if (this.sample === Boolean) {
|
|
898
|
+
return "boolean";
|
|
899
|
+
}
|
|
900
|
+
return this.fnNameFor(this.sample);
|
|
901
|
+
}
|
|
902
|
+
toAsymmetricMatcher() {
|
|
903
|
+
return `Any<${this.fnNameFor(this.sample)}>`;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
class StringMatching extends AsymmetricMatcher {
|
|
907
|
+
constructor(sample, inverse = false) {
|
|
908
|
+
if (!isA("String", sample) && !isA("RegExp", sample)) {
|
|
909
|
+
throw new Error("Expected is not a String or a RegExp");
|
|
910
|
+
}
|
|
911
|
+
super(new RegExp(sample), inverse);
|
|
912
|
+
}
|
|
913
|
+
asymmetricMatch(other) {
|
|
914
|
+
const result = isA("String", other) && this.sample.test(other);
|
|
915
|
+
return this.inverse ? !result : result;
|
|
916
|
+
}
|
|
917
|
+
toString() {
|
|
918
|
+
return `String${this.inverse ? "Not" : ""}Matching`;
|
|
919
|
+
}
|
|
920
|
+
getExpectedType() {
|
|
921
|
+
return "string";
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
class CloseTo extends AsymmetricMatcher {
|
|
925
|
+
precision;
|
|
926
|
+
constructor(sample, precision = 2, inverse = false) {
|
|
927
|
+
if (!isA("Number", sample)) {
|
|
928
|
+
throw new Error("Expected is not a Number");
|
|
929
|
+
}
|
|
930
|
+
if (!isA("Number", precision)) {
|
|
931
|
+
throw new Error("Precision is not a Number");
|
|
932
|
+
}
|
|
933
|
+
super(sample);
|
|
934
|
+
this.inverse = inverse;
|
|
935
|
+
this.precision = precision;
|
|
936
|
+
}
|
|
937
|
+
asymmetricMatch(other) {
|
|
938
|
+
if (!isA("Number", other)) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
let result = false;
|
|
942
|
+
if (other === Number.POSITIVE_INFINITY && this.sample === Number.POSITIVE_INFINITY) {
|
|
943
|
+
result = true;
|
|
944
|
+
} else if (other === Number.NEGATIVE_INFINITY && this.sample === Number.NEGATIVE_INFINITY) {
|
|
945
|
+
result = true;
|
|
946
|
+
} else {
|
|
947
|
+
result = Math.abs(this.sample - other) < 10 ** -this.precision / 2;
|
|
948
|
+
}
|
|
949
|
+
return this.inverse ? !result : result;
|
|
950
|
+
}
|
|
951
|
+
toString() {
|
|
952
|
+
return `Number${this.inverse ? "Not" : ""}CloseTo`;
|
|
953
|
+
}
|
|
954
|
+
getExpectedType() {
|
|
955
|
+
return "number";
|
|
956
|
+
}
|
|
957
|
+
toAsymmetricMatcher() {
|
|
958
|
+
return [
|
|
959
|
+
this.toString(),
|
|
960
|
+
this.sample,
|
|
961
|
+
`(${pluralize("digit", this.precision)})`
|
|
962
|
+
].join(" ");
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
class SchemaMatching extends AsymmetricMatcher {
|
|
966
|
+
result;
|
|
967
|
+
constructor(sample, inverse = false) {
|
|
968
|
+
if (!isStandardSchema(sample)) {
|
|
969
|
+
throw new TypeError("SchemaMatching expected to receive a Standard Schema.");
|
|
970
|
+
}
|
|
971
|
+
super(sample, inverse);
|
|
972
|
+
}
|
|
973
|
+
asymmetricMatch(other) {
|
|
974
|
+
const result = this.sample["~standard"].validate(other);
|
|
975
|
+
// Check if the result is a Promise (async validation)
|
|
976
|
+
if (result instanceof Promise) {
|
|
977
|
+
throw new TypeError("Async schema validation is not supported in asymmetric matchers.");
|
|
978
|
+
}
|
|
979
|
+
this.result = result;
|
|
980
|
+
const pass = !this.result.issues || this.result.issues.length === 0;
|
|
981
|
+
return this.inverse ? !pass : pass;
|
|
982
|
+
}
|
|
983
|
+
toString() {
|
|
984
|
+
return `Schema${this.inverse ? "Not" : ""}Matching`;
|
|
985
|
+
}
|
|
986
|
+
getExpectedType() {
|
|
987
|
+
return "object";
|
|
988
|
+
}
|
|
989
|
+
toAsymmetricMatcher() {
|
|
990
|
+
var _this$result;
|
|
991
|
+
const { utils } = this.getMatcherContext();
|
|
992
|
+
const issues = ((_this$result = this.result) === null || _this$result === void 0 ? void 0 : _this$result.issues) || [];
|
|
993
|
+
if (issues.length > 0) {
|
|
994
|
+
return `${this.toString()} ${utils.stringify(this.result, undefined, { printBasicPrototype: false })}`;
|
|
995
|
+
}
|
|
996
|
+
return this.toString();
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const JestAsymmetricMatchers = (chai, utils) => {
|
|
1000
|
+
utils.addMethod(chai.expect, "anything", () => new Anything());
|
|
1001
|
+
utils.addMethod(chai.expect, "any", (expected) => new Any(expected));
|
|
1002
|
+
utils.addMethod(chai.expect, "stringContaining", (expected) => new StringContaining(expected));
|
|
1003
|
+
utils.addMethod(chai.expect, "objectContaining", (expected) => new ObjectContaining(expected));
|
|
1004
|
+
utils.addMethod(chai.expect, "arrayContaining", (expected) => new ArrayContaining(expected));
|
|
1005
|
+
utils.addMethod(chai.expect, "stringMatching", (expected) => new StringMatching(expected));
|
|
1006
|
+
utils.addMethod(chai.expect, "closeTo", (expected, precision) => new CloseTo(expected, precision));
|
|
1007
|
+
utils.addMethod(chai.expect, "schemaMatching", (expected) => new SchemaMatching(expected));
|
|
1008
|
+
// defineProperty does not work
|
|
1009
|
+
chai.expect.not = {
|
|
1010
|
+
stringContaining: (expected) => new StringContaining(expected, true),
|
|
1011
|
+
objectContaining: (expected) => new ObjectContaining(expected, true),
|
|
1012
|
+
arrayContaining: (expected) => new ArrayContaining(expected, true),
|
|
1013
|
+
stringMatching: (expected) => new StringMatching(expected, true),
|
|
1014
|
+
closeTo: (expected, precision) => new CloseTo(expected, precision, true),
|
|
1015
|
+
schemaMatching: (expected) => new SchemaMatching(expected, true)
|
|
1016
|
+
};
|
|
1017
|
+
};
|
|
1018
|
+
|
|
1019
|
+
function createAssertionMessage(util, assertion, hasArgs) {
|
|
1020
|
+
const not = util.flag(assertion, "negate") ? "not." : "";
|
|
1021
|
+
const name = `${util.flag(assertion, "_name")}(${hasArgs ? "expected" : ""})`;
|
|
1022
|
+
const promiseName = util.flag(assertion, "promise");
|
|
1023
|
+
const promise = promiseName ? `.${promiseName}` : "";
|
|
1024
|
+
return `expect(actual)${promise}.${not}${name}`;
|
|
1025
|
+
}
|
|
1026
|
+
function recordAsyncExpect(_test, promise, assertion, error) {
|
|
1027
|
+
const test = _test;
|
|
1028
|
+
// record promise for test, that resolves before test ends
|
|
1029
|
+
if (test && promise instanceof Promise) {
|
|
1030
|
+
// if promise is explicitly awaited, remove it from the list
|
|
1031
|
+
promise = promise.finally(() => {
|
|
1032
|
+
if (!test.promises) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const index = test.promises.indexOf(promise);
|
|
1036
|
+
if (index !== -1) {
|
|
1037
|
+
test.promises.splice(index, 1);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
// record promise
|
|
1041
|
+
if (!test.promises) {
|
|
1042
|
+
test.promises = [];
|
|
1043
|
+
}
|
|
1044
|
+
test.promises.push(promise);
|
|
1045
|
+
let resolved = false;
|
|
1046
|
+
test.onFinished ?? (test.onFinished = []);
|
|
1047
|
+
test.onFinished.push(() => {
|
|
1048
|
+
if (!resolved) {
|
|
1049
|
+
var _vitest_worker__;
|
|
1050
|
+
const processor = ((_vitest_worker__ = globalThis.__vitest_worker__) === null || _vitest_worker__ === void 0 ? void 0 : _vitest_worker__.onFilterStackTrace) || ((s) => s || "");
|
|
1051
|
+
const stack = processor(error.stack);
|
|
1052
|
+
console.warn([
|
|
1053
|
+
`Promise returned by \`${assertion}\` was not awaited. `,
|
|
1054
|
+
"Vitest currently auto-awaits hanging assertions at the end of the test, but this will cause the test to fail in Vitest 3. ",
|
|
1055
|
+
"Please remember to await the assertion.\n",
|
|
1056
|
+
stack
|
|
1057
|
+
].join(""));
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
return {
|
|
1061
|
+
then(onFulfilled, onRejected) {
|
|
1062
|
+
resolved = true;
|
|
1063
|
+
return promise.then(onFulfilled, onRejected);
|
|
1064
|
+
},
|
|
1065
|
+
catch(onRejected) {
|
|
1066
|
+
return promise.catch(onRejected);
|
|
1067
|
+
},
|
|
1068
|
+
finally(onFinally) {
|
|
1069
|
+
return promise.finally(onFinally);
|
|
1070
|
+
},
|
|
1071
|
+
[Symbol.toStringTag]: "Promise"
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
return promise;
|
|
1075
|
+
}
|
|
1076
|
+
function handleTestError(test, err) {
|
|
1077
|
+
var _test$result;
|
|
1078
|
+
test.result || (test.result = { state: "fail" });
|
|
1079
|
+
test.result.state = "fail";
|
|
1080
|
+
(_test$result = test.result).errors || (_test$result.errors = []);
|
|
1081
|
+
test.result.errors.push(processError(err));
|
|
1082
|
+
}
|
|
1083
|
+
function wrapAssertion(utils, name, fn) {
|
|
1084
|
+
return function(...args) {
|
|
1085
|
+
// private
|
|
1086
|
+
if (name !== "withTest") {
|
|
1087
|
+
utils.flag(this, "_name", name);
|
|
1088
|
+
}
|
|
1089
|
+
if (!utils.flag(this, "soft")) {
|
|
1090
|
+
return fn.apply(this, args);
|
|
1091
|
+
}
|
|
1092
|
+
const test = utils.flag(this, "vitest-test");
|
|
1093
|
+
if (!test) {
|
|
1094
|
+
throw new Error("expect.soft() can only be used inside a test");
|
|
1095
|
+
}
|
|
1096
|
+
try {
|
|
1097
|
+
const result = fn.apply(this, args);
|
|
1098
|
+
if (result && typeof result === "object" && typeof result.then === "function") {
|
|
1099
|
+
return result.then(noop, (err) => {
|
|
1100
|
+
handleTestError(test, err);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
return result;
|
|
1104
|
+
} catch (err) {
|
|
1105
|
+
handleTestError(test, err);
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Jest Expect Compact
|
|
1111
|
+
const JestChaiExpect = (chai, utils) => {
|
|
1112
|
+
const { AssertionError } = chai;
|
|
1113
|
+
const customTesters = getCustomEqualityTesters();
|
|
1114
|
+
function def(name, fn) {
|
|
1115
|
+
const addMethod = (n) => {
|
|
1116
|
+
const softWrapper = wrapAssertion(utils, n, fn);
|
|
1117
|
+
utils.addMethod(chai.Assertion.prototype, n, softWrapper);
|
|
1118
|
+
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, n, softWrapper);
|
|
1119
|
+
};
|
|
1120
|
+
if (Array.isArray(name)) {
|
|
1121
|
+
name.forEach((n) => addMethod(n));
|
|
1122
|
+
} else {
|
|
1123
|
+
addMethod(name);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
[
|
|
1127
|
+
"throw",
|
|
1128
|
+
"throws",
|
|
1129
|
+
"Throw"
|
|
1130
|
+
].forEach((m) => {
|
|
1131
|
+
utils.overwriteMethod(chai.Assertion.prototype, m, (_super) => {
|
|
1132
|
+
return function(...args) {
|
|
1133
|
+
const promise = utils.flag(this, "promise");
|
|
1134
|
+
const object = utils.flag(this, "object");
|
|
1135
|
+
const isNot = utils.flag(this, "negate");
|
|
1136
|
+
if (promise === "rejects") {
|
|
1137
|
+
utils.flag(this, "object", () => {
|
|
1138
|
+
throw object;
|
|
1139
|
+
});
|
|
1140
|
+
} else if (promise === "resolves" && typeof object !== "function") {
|
|
1141
|
+
if (!isNot) {
|
|
1142
|
+
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
|
1143
|
+
const error = { showDiff: false };
|
|
1144
|
+
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
|
1145
|
+
} else {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
_super.apply(this, args);
|
|
1150
|
+
};
|
|
1151
|
+
});
|
|
1152
|
+
});
|
|
1153
|
+
// @ts-expect-error @internal
|
|
1154
|
+
def("withTest", function(test) {
|
|
1155
|
+
utils.flag(this, "vitest-test", test);
|
|
1156
|
+
return this;
|
|
1157
|
+
});
|
|
1158
|
+
def("toEqual", function(expected) {
|
|
1159
|
+
const actual = utils.flag(this, "object");
|
|
1160
|
+
const equal = equals(actual, expected, [...customTesters, iterableEquality]);
|
|
1161
|
+
return this.assert(equal, "expected #{this} to deeply equal #{exp}", "expected #{this} to not deeply equal #{exp}", expected, actual);
|
|
1162
|
+
});
|
|
1163
|
+
def("toStrictEqual", function(expected) {
|
|
1164
|
+
const obj = utils.flag(this, "object");
|
|
1165
|
+
const equal = equals(obj, expected, [
|
|
1166
|
+
...customTesters,
|
|
1167
|
+
iterableEquality,
|
|
1168
|
+
typeEquality,
|
|
1169
|
+
sparseArrayEquality,
|
|
1170
|
+
arrayBufferEquality
|
|
1171
|
+
], true);
|
|
1172
|
+
return this.assert(equal, "expected #{this} to strictly equal #{exp}", "expected #{this} to not strictly equal #{exp}", expected, obj);
|
|
1173
|
+
});
|
|
1174
|
+
def("toBe", function(expected) {
|
|
1175
|
+
const actual = this._obj;
|
|
1176
|
+
const pass = Object.is(actual, expected);
|
|
1177
|
+
let deepEqualityName = "";
|
|
1178
|
+
if (!pass) {
|
|
1179
|
+
const toStrictEqualPass = equals(actual, expected, [
|
|
1180
|
+
...customTesters,
|
|
1181
|
+
iterableEquality,
|
|
1182
|
+
typeEquality,
|
|
1183
|
+
sparseArrayEquality,
|
|
1184
|
+
arrayBufferEquality
|
|
1185
|
+
], true);
|
|
1186
|
+
if (toStrictEqualPass) {
|
|
1187
|
+
deepEqualityName = "toStrictEqual";
|
|
1188
|
+
} else {
|
|
1189
|
+
const toEqualPass = equals(actual, expected, [...customTesters, iterableEquality]);
|
|
1190
|
+
if (toEqualPass) {
|
|
1191
|
+
deepEqualityName = "toEqual";
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
return this.assert(pass, generateToBeMessage(deepEqualityName), "expected #{this} not to be #{exp} // Object.is equality", expected, actual);
|
|
1196
|
+
});
|
|
1197
|
+
def("toMatchObject", function(expected) {
|
|
1198
|
+
const actual = this._obj;
|
|
1199
|
+
const pass = equals(actual, expected, [
|
|
1200
|
+
...customTesters,
|
|
1201
|
+
iterableEquality,
|
|
1202
|
+
subsetEquality
|
|
1203
|
+
]);
|
|
1204
|
+
const isNot = utils.flag(this, "negate");
|
|
1205
|
+
const { subset: actualSubset, stripped } = getObjectSubset(actual, expected, customTesters);
|
|
1206
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1207
|
+
const msg = utils.getMessage(this, [
|
|
1208
|
+
pass,
|
|
1209
|
+
"expected #{this} to match object #{exp}",
|
|
1210
|
+
"expected #{this} to not match object #{exp}",
|
|
1211
|
+
expected,
|
|
1212
|
+
actualSubset,
|
|
1213
|
+
false
|
|
1214
|
+
]);
|
|
1215
|
+
const message = stripped === 0 ? msg : `${msg}\n(${stripped} matching ${stripped === 1 ? "property" : "properties"} omitted from actual)`;
|
|
1216
|
+
throw new AssertionError(message, {
|
|
1217
|
+
showDiff: true,
|
|
1218
|
+
expected,
|
|
1219
|
+
actual: actualSubset
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
def("toMatch", function(expected) {
|
|
1224
|
+
const actual = this._obj;
|
|
1225
|
+
if (typeof actual !== "string") {
|
|
1226
|
+
throw new TypeError(`.toMatch() expects to receive a string, but got ${typeof actual}`);
|
|
1227
|
+
}
|
|
1228
|
+
return this.assert(typeof expected === "string" ? actual.includes(expected) : actual.match(expected), `expected #{this} to match #{exp}`, `expected #{this} not to match #{exp}`, expected, actual);
|
|
1229
|
+
});
|
|
1230
|
+
def("toContain", function(item) {
|
|
1231
|
+
const actual = this._obj;
|
|
1232
|
+
if (typeof Node !== "undefined" && actual instanceof Node) {
|
|
1233
|
+
if (!(item instanceof Node)) {
|
|
1234
|
+
throw new TypeError(`toContain() expected a DOM node as the argument, but got ${typeof item}`);
|
|
1235
|
+
}
|
|
1236
|
+
return this.assert(actual.contains(item), "expected #{this} to contain element #{exp}", "expected #{this} not to contain element #{exp}", item, actual);
|
|
1237
|
+
}
|
|
1238
|
+
if (typeof DOMTokenList !== "undefined" && actual instanceof DOMTokenList) {
|
|
1239
|
+
assertTypes(item, "class name", ["string"]);
|
|
1240
|
+
const isNot = utils.flag(this, "negate");
|
|
1241
|
+
const expectedClassList = isNot ? actual.value.replace(item, "").trim() : `${actual.value} ${item}`;
|
|
1242
|
+
return this.assert(actual.contains(item), `expected "${actual.value}" to contain "${item}"`, `expected "${actual.value}" not to contain "${item}"`, expectedClassList, actual.value);
|
|
1243
|
+
}
|
|
1244
|
+
// handle simple case on our own using `this.assert` to include diff in error message
|
|
1245
|
+
if (typeof actual === "string" && typeof item === "string") {
|
|
1246
|
+
return this.assert(actual.includes(item), `expected #{this} to contain #{exp}`, `expected #{this} not to contain #{exp}`, item, actual);
|
|
1247
|
+
}
|
|
1248
|
+
// make "actual" indexable to have compatibility with jest
|
|
1249
|
+
if (actual != null && typeof actual !== "string") {
|
|
1250
|
+
utils.flag(this, "object", Array.from(actual));
|
|
1251
|
+
}
|
|
1252
|
+
return this.contain(item);
|
|
1253
|
+
});
|
|
1254
|
+
def("toContainEqual", function(expected) {
|
|
1255
|
+
const obj = utils.flag(this, "object");
|
|
1256
|
+
const index = Array.from(obj).findIndex((item) => {
|
|
1257
|
+
return equals(item, expected, customTesters);
|
|
1258
|
+
});
|
|
1259
|
+
this.assert(index !== -1, "expected #{this} to deep equally contain #{exp}", "expected #{this} to not deep equally contain #{exp}", expected);
|
|
1260
|
+
});
|
|
1261
|
+
def("toBeTruthy", function() {
|
|
1262
|
+
const obj = utils.flag(this, "object");
|
|
1263
|
+
this.assert(Boolean(obj), "expected #{this} to be truthy", "expected #{this} to not be truthy", true, obj);
|
|
1264
|
+
});
|
|
1265
|
+
def("toBeFalsy", function() {
|
|
1266
|
+
const obj = utils.flag(this, "object");
|
|
1267
|
+
this.assert(!obj, "expected #{this} to be falsy", "expected #{this} to not be falsy", false, obj);
|
|
1268
|
+
});
|
|
1269
|
+
def("toBeGreaterThan", function(expected) {
|
|
1270
|
+
const actual = this._obj;
|
|
1271
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
|
1272
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
|
1273
|
+
return this.assert(actual > expected, `expected ${actual} to be greater than ${expected}`, `expected ${actual} to be not greater than ${expected}`, expected, actual, false);
|
|
1274
|
+
});
|
|
1275
|
+
def("toBeGreaterThanOrEqual", function(expected) {
|
|
1276
|
+
const actual = this._obj;
|
|
1277
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
|
1278
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
|
1279
|
+
return this.assert(actual >= expected, `expected ${actual} to be greater than or equal to ${expected}`, `expected ${actual} to be not greater than or equal to ${expected}`, expected, actual, false);
|
|
1280
|
+
});
|
|
1281
|
+
def("toBeLessThan", function(expected) {
|
|
1282
|
+
const actual = this._obj;
|
|
1283
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
|
1284
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
|
1285
|
+
return this.assert(actual < expected, `expected ${actual} to be less than ${expected}`, `expected ${actual} to be not less than ${expected}`, expected, actual, false);
|
|
1286
|
+
});
|
|
1287
|
+
def("toBeLessThanOrEqual", function(expected) {
|
|
1288
|
+
const actual = this._obj;
|
|
1289
|
+
assertTypes(actual, "actual", ["number", "bigint"]);
|
|
1290
|
+
assertTypes(expected, "expected", ["number", "bigint"]);
|
|
1291
|
+
return this.assert(actual <= expected, `expected ${actual} to be less than or equal to ${expected}`, `expected ${actual} to be not less than or equal to ${expected}`, expected, actual, false);
|
|
1292
|
+
});
|
|
1293
|
+
def("toBeNaN", function() {
|
|
1294
|
+
const obj = utils.flag(this, "object");
|
|
1295
|
+
this.assert(Number.isNaN(obj), "expected #{this} to be NaN", "expected #{this} not to be NaN", Number.NaN, obj);
|
|
1296
|
+
});
|
|
1297
|
+
def("toBeUndefined", function() {
|
|
1298
|
+
const obj = utils.flag(this, "object");
|
|
1299
|
+
this.assert(undefined === obj, "expected #{this} to be undefined", "expected #{this} not to be undefined", undefined, obj);
|
|
1300
|
+
});
|
|
1301
|
+
def("toBeNull", function() {
|
|
1302
|
+
const obj = utils.flag(this, "object");
|
|
1303
|
+
this.assert(obj === null, "expected #{this} to be null", "expected #{this} not to be null", null, obj);
|
|
1304
|
+
});
|
|
1305
|
+
def("toBeNullable", function() {
|
|
1306
|
+
const obj = utils.flag(this, "object");
|
|
1307
|
+
this.assert(obj == null, "expected #{this} to be nullish", "expected #{this} not to be nullish", null, obj);
|
|
1308
|
+
});
|
|
1309
|
+
def("toBeDefined", function() {
|
|
1310
|
+
const obj = utils.flag(this, "object");
|
|
1311
|
+
this.assert(typeof obj !== "undefined", "expected #{this} to be defined", "expected #{this} to be undefined", obj);
|
|
1312
|
+
});
|
|
1313
|
+
def("toBeTypeOf", function(expected) {
|
|
1314
|
+
const actual = typeof this._obj;
|
|
1315
|
+
const equal = expected === actual;
|
|
1316
|
+
return this.assert(equal, "expected #{this} to be type of #{exp}", "expected #{this} not to be type of #{exp}", expected, actual);
|
|
1317
|
+
});
|
|
1318
|
+
def("toBeInstanceOf", function(obj) {
|
|
1319
|
+
return this.instanceOf(obj);
|
|
1320
|
+
});
|
|
1321
|
+
def("toHaveLength", function(length) {
|
|
1322
|
+
return this.have.length(length);
|
|
1323
|
+
});
|
|
1324
|
+
// destructuring, because it checks `arguments` inside, and value is passing as `undefined`
|
|
1325
|
+
def("toHaveProperty", function(...args) {
|
|
1326
|
+
if (Array.isArray(args[0])) {
|
|
1327
|
+
args[0] = args[0].map((key) => String(key).replace(/([.[\]])/g, "\\$1")).join(".");
|
|
1328
|
+
}
|
|
1329
|
+
const actual = this._obj;
|
|
1330
|
+
const [propertyName, expected] = args;
|
|
1331
|
+
const getValue = () => {
|
|
1332
|
+
const hasOwn = Object.hasOwn(actual, propertyName);
|
|
1333
|
+
if (hasOwn) {
|
|
1334
|
+
return {
|
|
1335
|
+
value: actual[propertyName],
|
|
1336
|
+
exists: true
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
return utils.getPathInfo(actual, propertyName);
|
|
1340
|
+
};
|
|
1341
|
+
const { value, exists } = getValue();
|
|
1342
|
+
const pass = exists && (args.length === 1 || equals(expected, value, customTesters));
|
|
1343
|
+
const valueString = args.length === 1 ? "" : ` with value ${utils.objDisplay(expected)}`;
|
|
1344
|
+
return this.assert(pass, `expected #{this} to have property "${propertyName}"${valueString}`, `expected #{this} to not have property "${propertyName}"${valueString}`, expected, exists ? value : undefined);
|
|
1345
|
+
});
|
|
1346
|
+
def("toBeCloseTo", function(received, precision = 2) {
|
|
1347
|
+
const expected = this._obj;
|
|
1348
|
+
let pass = false;
|
|
1349
|
+
let expectedDiff = 0;
|
|
1350
|
+
let receivedDiff = 0;
|
|
1351
|
+
if (received === Number.POSITIVE_INFINITY && expected === Number.POSITIVE_INFINITY) {
|
|
1352
|
+
pass = true;
|
|
1353
|
+
} else if (received === Number.NEGATIVE_INFINITY && expected === Number.NEGATIVE_INFINITY) {
|
|
1354
|
+
pass = true;
|
|
1355
|
+
} else {
|
|
1356
|
+
expectedDiff = 10 ** -precision / 2;
|
|
1357
|
+
receivedDiff = Math.abs(expected - received);
|
|
1358
|
+
pass = receivedDiff < expectedDiff;
|
|
1359
|
+
}
|
|
1360
|
+
return this.assert(pass, `expected #{this} to be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, `expected #{this} to not be close to #{exp}, received difference is ${receivedDiff}, but expected ${expectedDiff}`, received, expected, false);
|
|
1361
|
+
});
|
|
1362
|
+
function assertIsMock(assertion) {
|
|
1363
|
+
if (!isMockFunction(assertion._obj)) {
|
|
1364
|
+
throw new TypeError(`${utils.inspect(assertion._obj)} is not a spy or a call to a spy!`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function getSpy(assertion) {
|
|
1368
|
+
assertIsMock(assertion);
|
|
1369
|
+
return assertion._obj;
|
|
1370
|
+
}
|
|
1371
|
+
def(["toHaveBeenCalledTimes", "toBeCalledTimes"], function(number) {
|
|
1372
|
+
const spy = getSpy(this);
|
|
1373
|
+
const spyName = spy.getMockName();
|
|
1374
|
+
const callCount = spy.mock.calls.length;
|
|
1375
|
+
return this.assert(callCount === number, `expected "${spyName}" to be called #{exp} times, but got ${callCount} times`, `expected "${spyName}" to not be called #{exp} times`, number, callCount, false);
|
|
1376
|
+
});
|
|
1377
|
+
def("toHaveBeenCalledOnce", function() {
|
|
1378
|
+
const spy = getSpy(this);
|
|
1379
|
+
const spyName = spy.getMockName();
|
|
1380
|
+
const callCount = spy.mock.calls.length;
|
|
1381
|
+
return this.assert(callCount === 1, `expected "${spyName}" to be called once, but got ${callCount} times`, `expected "${spyName}" to not be called once`, 1, callCount, false);
|
|
1382
|
+
});
|
|
1383
|
+
def(["toHaveBeenCalled", "toBeCalled"], function() {
|
|
1384
|
+
const spy = getSpy(this);
|
|
1385
|
+
const spyName = spy.getMockName();
|
|
1386
|
+
const callCount = spy.mock.calls.length;
|
|
1387
|
+
const called = callCount > 0;
|
|
1388
|
+
const isNot = utils.flag(this, "negate");
|
|
1389
|
+
let msg = utils.getMessage(this, [
|
|
1390
|
+
called,
|
|
1391
|
+
`expected "${spyName}" to be called at least once`,
|
|
1392
|
+
`expected "${spyName}" to not be called at all, but actually been called ${callCount} times`,
|
|
1393
|
+
true,
|
|
1394
|
+
called
|
|
1395
|
+
]);
|
|
1396
|
+
if (called && isNot) {
|
|
1397
|
+
msg = formatCalls(spy, msg);
|
|
1398
|
+
}
|
|
1399
|
+
if (called && isNot || !called && !isNot) {
|
|
1400
|
+
throw new AssertionError(msg);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
// manually compare array elements since `jestEquals` cannot
|
|
1404
|
+
// apply asymmetric matcher to `undefined` array element.
|
|
1405
|
+
function equalsArgumentArray(a, b) {
|
|
1406
|
+
return a.length === b.length && a.every((aItem, i) => equals(aItem, b[i], [...customTesters, iterableEquality]));
|
|
1407
|
+
}
|
|
1408
|
+
def(["toHaveBeenCalledWith", "toBeCalledWith"], function(...args) {
|
|
1409
|
+
const spy = getSpy(this);
|
|
1410
|
+
const spyName = spy.getMockName();
|
|
1411
|
+
const pass = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args));
|
|
1412
|
+
const isNot = utils.flag(this, "negate");
|
|
1413
|
+
const msg = utils.getMessage(this, [
|
|
1414
|
+
pass,
|
|
1415
|
+
`expected "${spyName}" to be called with arguments: #{exp}`,
|
|
1416
|
+
`expected "${spyName}" to not be called with arguments: #{exp}`,
|
|
1417
|
+
args
|
|
1418
|
+
]);
|
|
1419
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1420
|
+
throw new AssertionError(formatCalls(spy, msg, args));
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
def("toHaveBeenCalledExactlyOnceWith", function(...args) {
|
|
1424
|
+
const spy = getSpy(this);
|
|
1425
|
+
const spyName = spy.getMockName();
|
|
1426
|
+
const callCount = spy.mock.calls.length;
|
|
1427
|
+
const hasCallWithArgs = spy.mock.calls.some((callArg) => equalsArgumentArray(callArg, args));
|
|
1428
|
+
const pass = hasCallWithArgs && callCount === 1;
|
|
1429
|
+
const isNot = utils.flag(this, "negate");
|
|
1430
|
+
const msg = utils.getMessage(this, [
|
|
1431
|
+
pass,
|
|
1432
|
+
`expected "${spyName}" to be called once with arguments: #{exp}`,
|
|
1433
|
+
`expected "${spyName}" to not be called once with arguments: #{exp}`,
|
|
1434
|
+
args
|
|
1435
|
+
]);
|
|
1436
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1437
|
+
throw new AssertionError(formatCalls(spy, msg, args));
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
def(["toHaveBeenNthCalledWith", "nthCalledWith"], function(times, ...args) {
|
|
1441
|
+
const spy = getSpy(this);
|
|
1442
|
+
const spyName = spy.getMockName();
|
|
1443
|
+
const nthCall = spy.mock.calls[times - 1];
|
|
1444
|
+
const callCount = spy.mock.calls.length;
|
|
1445
|
+
const isCalled = times <= callCount;
|
|
1446
|
+
this.assert(nthCall && equalsArgumentArray(nthCall, args), `expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}${isCalled ? `` : `, but called only ${callCount} times`}`, `expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`, args, nthCall, isCalled);
|
|
1447
|
+
});
|
|
1448
|
+
def(["toHaveBeenLastCalledWith", "lastCalledWith"], function(...args) {
|
|
1449
|
+
const spy = getSpy(this);
|
|
1450
|
+
const spyName = spy.getMockName();
|
|
1451
|
+
const lastCall = spy.mock.calls.at(-1);
|
|
1452
|
+
this.assert(lastCall && equalsArgumentArray(lastCall, args), `expected last "${spyName}" call to have been called with #{exp}`, `expected last "${spyName}" call to not have been called with #{exp}`, args, lastCall);
|
|
1453
|
+
});
|
|
1454
|
+
/**
|
|
1455
|
+
* Used for `toHaveBeenCalledBefore` and `toHaveBeenCalledAfter` to determine if the expected spy was called before the result spy.
|
|
1456
|
+
*/
|
|
1457
|
+
function isSpyCalledBeforeAnotherSpy(beforeSpy, afterSpy, failIfNoFirstInvocation) {
|
|
1458
|
+
const beforeInvocationCallOrder = beforeSpy.mock.invocationCallOrder;
|
|
1459
|
+
const afterInvocationCallOrder = afterSpy.mock.invocationCallOrder;
|
|
1460
|
+
if (beforeInvocationCallOrder.length === 0) {
|
|
1461
|
+
return !failIfNoFirstInvocation;
|
|
1462
|
+
}
|
|
1463
|
+
if (afterInvocationCallOrder.length === 0) {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
return beforeInvocationCallOrder[0] < afterInvocationCallOrder[0];
|
|
1467
|
+
}
|
|
1468
|
+
def(["toHaveBeenCalledBefore"], function(resultSpy, failIfNoFirstInvocation = true) {
|
|
1469
|
+
const expectSpy = getSpy(this);
|
|
1470
|
+
if (!isMockFunction(resultSpy)) {
|
|
1471
|
+
throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`);
|
|
1472
|
+
}
|
|
1473
|
+
this.assert(isSpyCalledBeforeAnotherSpy(expectSpy, resultSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called before "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called before "${resultSpy.getMockName()}"`, resultSpy, expectSpy);
|
|
1474
|
+
});
|
|
1475
|
+
def(["toHaveBeenCalledAfter"], function(resultSpy, failIfNoFirstInvocation = true) {
|
|
1476
|
+
const expectSpy = getSpy(this);
|
|
1477
|
+
if (!isMockFunction(resultSpy)) {
|
|
1478
|
+
throw new TypeError(`${utils.inspect(resultSpy)} is not a spy or a call to a spy`);
|
|
1479
|
+
}
|
|
1480
|
+
this.assert(isSpyCalledBeforeAnotherSpy(resultSpy, expectSpy, failIfNoFirstInvocation), `expected "${expectSpy.getMockName()}" to have been called after "${resultSpy.getMockName()}"`, `expected "${expectSpy.getMockName()}" to not have been called after "${resultSpy.getMockName()}"`, resultSpy, expectSpy);
|
|
1481
|
+
});
|
|
1482
|
+
def(["toThrow", "toThrowError"], function(expected) {
|
|
1483
|
+
if (typeof expected === "string" || typeof expected === "undefined" || expected instanceof RegExp) {
|
|
1484
|
+
// Fixes the issue related to `chai` <https://github.com/vitest-dev/vitest/issues/6618>
|
|
1485
|
+
return this.throws(expected === "" ? /^$/ : expected);
|
|
1486
|
+
}
|
|
1487
|
+
const obj = this._obj;
|
|
1488
|
+
const promise = utils.flag(this, "promise");
|
|
1489
|
+
const isNot = utils.flag(this, "negate");
|
|
1490
|
+
let thrown = null;
|
|
1491
|
+
if (promise === "rejects") {
|
|
1492
|
+
thrown = obj;
|
|
1493
|
+
} else if (promise === "resolves" && typeof obj !== "function") {
|
|
1494
|
+
if (!isNot) {
|
|
1495
|
+
const message = utils.flag(this, "message") || "expected promise to throw an error, but it didn't";
|
|
1496
|
+
const error = { showDiff: false };
|
|
1497
|
+
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
|
1498
|
+
} else {
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
} else {
|
|
1502
|
+
let isThrow = false;
|
|
1503
|
+
try {
|
|
1504
|
+
obj();
|
|
1505
|
+
} catch (err) {
|
|
1506
|
+
isThrow = true;
|
|
1507
|
+
thrown = err;
|
|
1508
|
+
}
|
|
1509
|
+
if (!isThrow && !isNot) {
|
|
1510
|
+
const message = utils.flag(this, "message") || "expected function to throw an error, but it didn't";
|
|
1511
|
+
const error = { showDiff: false };
|
|
1512
|
+
throw new AssertionError(message, error, utils.flag(this, "ssfi"));
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (typeof expected === "function") {
|
|
1516
|
+
const name = expected.name || expected.prototype.constructor.name;
|
|
1517
|
+
return this.assert(thrown && thrown instanceof expected, `expected error to be instance of ${name}`, `expected error not to be instance of ${name}`, expected, thrown);
|
|
1518
|
+
}
|
|
1519
|
+
if (expected instanceof Error) {
|
|
1520
|
+
const equal = equals(thrown, expected, [...customTesters, iterableEquality]);
|
|
1521
|
+
return this.assert(equal, "expected a thrown error to be #{exp}", "expected a thrown error not to be #{exp}", expected, thrown);
|
|
1522
|
+
}
|
|
1523
|
+
if (typeof expected === "object" && "asymmetricMatch" in expected && typeof expected.asymmetricMatch === "function") {
|
|
1524
|
+
const matcher = expected;
|
|
1525
|
+
return this.assert(thrown && matcher.asymmetricMatch(thrown), "expected error to match asymmetric matcher", "expected error not to match asymmetric matcher", matcher, thrown);
|
|
1526
|
+
}
|
|
1527
|
+
throw new Error(`"toThrow" expects string, RegExp, function, Error instance or asymmetric matcher, got "${typeof expected}"`);
|
|
1528
|
+
});
|
|
1529
|
+
[{
|
|
1530
|
+
name: "toHaveResolved",
|
|
1531
|
+
condition: (spy) => spy.mock.settledResults.length > 0 && spy.mock.settledResults.some(({ type }) => type === "fulfilled"),
|
|
1532
|
+
action: "resolved"
|
|
1533
|
+
}, {
|
|
1534
|
+
name: ["toHaveReturned", "toReturn"],
|
|
1535
|
+
condition: (spy) => spy.mock.calls.length > 0 && spy.mock.results.some(({ type }) => type !== "throw"),
|
|
1536
|
+
action: "called"
|
|
1537
|
+
}].forEach(({ name, condition, action }) => {
|
|
1538
|
+
def(name, function() {
|
|
1539
|
+
const spy = getSpy(this);
|
|
1540
|
+
const spyName = spy.getMockName();
|
|
1541
|
+
const pass = condition(spy);
|
|
1542
|
+
this.assert(pass, `expected "${spyName}" to be successfully ${action} at least once`, `expected "${spyName}" to not be successfully ${action}`, pass, !pass, false);
|
|
1543
|
+
});
|
|
1544
|
+
});
|
|
1545
|
+
[{
|
|
1546
|
+
name: "toHaveResolvedTimes",
|
|
1547
|
+
condition: (spy, times) => spy.mock.settledResults.reduce((s, { type }) => type === "fulfilled" ? ++s : s, 0) === times,
|
|
1548
|
+
action: "resolved"
|
|
1549
|
+
}, {
|
|
1550
|
+
name: ["toHaveReturnedTimes", "toReturnTimes"],
|
|
1551
|
+
condition: (spy, times) => spy.mock.results.reduce((s, { type }) => type === "throw" ? s : ++s, 0) === times,
|
|
1552
|
+
action: "called"
|
|
1553
|
+
}].forEach(({ name, condition, action }) => {
|
|
1554
|
+
def(name, function(times) {
|
|
1555
|
+
const spy = getSpy(this);
|
|
1556
|
+
const spyName = spy.getMockName();
|
|
1557
|
+
const pass = condition(spy, times);
|
|
1558
|
+
this.assert(pass, `expected "${spyName}" to be successfully ${action} ${times} times`, `expected "${spyName}" to not be successfully ${action} ${times} times`, `expected resolved times: ${times}`, `received resolved times: ${pass}`, false);
|
|
1559
|
+
});
|
|
1560
|
+
});
|
|
1561
|
+
[{
|
|
1562
|
+
name: "toHaveResolvedWith",
|
|
1563
|
+
condition: (spy, value) => spy.mock.settledResults.some(({ type, value: result }) => type === "fulfilled" && equals(value, result)),
|
|
1564
|
+
action: "resolve"
|
|
1565
|
+
}, {
|
|
1566
|
+
name: ["toHaveReturnedWith", "toReturnWith"],
|
|
1567
|
+
condition: (spy, value) => spy.mock.results.some(({ type, value: result }) => type === "return" && equals(value, result)),
|
|
1568
|
+
action: "return"
|
|
1569
|
+
}].forEach(({ name, condition, action }) => {
|
|
1570
|
+
def(name, function(value) {
|
|
1571
|
+
const spy = getSpy(this);
|
|
1572
|
+
const pass = condition(spy, value);
|
|
1573
|
+
const isNot = utils.flag(this, "negate");
|
|
1574
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1575
|
+
const spyName = spy.getMockName();
|
|
1576
|
+
const msg = utils.getMessage(this, [
|
|
1577
|
+
pass,
|
|
1578
|
+
`expected "${spyName}" to ${action} with: #{exp} at least once`,
|
|
1579
|
+
`expected "${spyName}" to not ${action} with: #{exp}`,
|
|
1580
|
+
value
|
|
1581
|
+
]);
|
|
1582
|
+
const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
|
|
1583
|
+
throw new AssertionError(formatReturns(spy, results, msg, value));
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
});
|
|
1587
|
+
[{
|
|
1588
|
+
name: "toHaveLastResolvedWith",
|
|
1589
|
+
condition: (spy, value) => {
|
|
1590
|
+
const result = spy.mock.settledResults.at(-1);
|
|
1591
|
+
return Boolean(result && result.type === "fulfilled" && equals(result.value, value));
|
|
1592
|
+
},
|
|
1593
|
+
action: "resolve"
|
|
1594
|
+
}, {
|
|
1595
|
+
name: ["toHaveLastReturnedWith", "lastReturnedWith"],
|
|
1596
|
+
condition: (spy, value) => {
|
|
1597
|
+
const result = spy.mock.results.at(-1);
|
|
1598
|
+
return Boolean(result && result.type === "return" && equals(result.value, value));
|
|
1599
|
+
},
|
|
1600
|
+
action: "return"
|
|
1601
|
+
}].forEach(({ name, condition, action }) => {
|
|
1602
|
+
def(name, function(value) {
|
|
1603
|
+
const spy = getSpy(this);
|
|
1604
|
+
const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
|
|
1605
|
+
const result = results.at(-1);
|
|
1606
|
+
const spyName = spy.getMockName();
|
|
1607
|
+
this.assert(condition(spy, value), `expected last "${spyName}" call to ${action} #{exp}`, `expected last "${spyName}" call to not ${action} #{exp}`, value, result === null || result === void 0 ? void 0 : result.value);
|
|
1608
|
+
});
|
|
1609
|
+
});
|
|
1610
|
+
[{
|
|
1611
|
+
name: "toHaveNthResolvedWith",
|
|
1612
|
+
condition: (spy, index, value) => {
|
|
1613
|
+
const result = spy.mock.settledResults[index - 1];
|
|
1614
|
+
return result && result.type === "fulfilled" && equals(result.value, value);
|
|
1615
|
+
},
|
|
1616
|
+
action: "resolve"
|
|
1617
|
+
}, {
|
|
1618
|
+
name: ["toHaveNthReturnedWith", "nthReturnedWith"],
|
|
1619
|
+
condition: (spy, index, value) => {
|
|
1620
|
+
const result = spy.mock.results[index - 1];
|
|
1621
|
+
return result && result.type === "return" && equals(result.value, value);
|
|
1622
|
+
},
|
|
1623
|
+
action: "return"
|
|
1624
|
+
}].forEach(({ name, condition, action }) => {
|
|
1625
|
+
def(name, function(nthCall, value) {
|
|
1626
|
+
const spy = getSpy(this);
|
|
1627
|
+
const spyName = spy.getMockName();
|
|
1628
|
+
const results = action === "return" ? spy.mock.results : spy.mock.settledResults;
|
|
1629
|
+
const result = results[nthCall - 1];
|
|
1630
|
+
const ordinalCall = `${ordinalOf(nthCall)} call`;
|
|
1631
|
+
this.assert(condition(spy, nthCall, value), `expected ${ordinalCall} "${spyName}" call to ${action} #{exp}`, `expected ${ordinalCall} "${spyName}" call to not ${action} #{exp}`, value, result === null || result === void 0 ? void 0 : result.value);
|
|
1632
|
+
});
|
|
1633
|
+
});
|
|
1634
|
+
// @ts-expect-error @internal
|
|
1635
|
+
def("withContext", function(context) {
|
|
1636
|
+
for (const key in context) {
|
|
1637
|
+
utils.flag(this, key, context[key]);
|
|
1638
|
+
}
|
|
1639
|
+
return this;
|
|
1640
|
+
});
|
|
1641
|
+
utils.addProperty(chai.Assertion.prototype, "resolves", function __VITEST_RESOLVES__() {
|
|
1642
|
+
const error = new Error("resolves");
|
|
1643
|
+
utils.flag(this, "promise", "resolves");
|
|
1644
|
+
utils.flag(this, "error", error);
|
|
1645
|
+
const test = utils.flag(this, "vitest-test");
|
|
1646
|
+
const obj = utils.flag(this, "object");
|
|
1647
|
+
if (utils.flag(this, "poll")) {
|
|
1648
|
+
throw new SyntaxError(`expect.poll() is not supported in combination with .resolves`);
|
|
1649
|
+
}
|
|
1650
|
+
if (typeof (obj === null || obj === void 0 ? void 0 : obj.then) !== "function") {
|
|
1651
|
+
throw new TypeError(`You must provide a Promise to expect() when using .resolves, not '${typeof obj}'.`);
|
|
1652
|
+
}
|
|
1653
|
+
const proxy = new Proxy(this, { get: (target, key, receiver) => {
|
|
1654
|
+
const result = Reflect.get(target, key, receiver);
|
|
1655
|
+
if (typeof result !== "function") {
|
|
1656
|
+
return result instanceof chai.Assertion ? proxy : result;
|
|
1657
|
+
}
|
|
1658
|
+
return (...args) => {
|
|
1659
|
+
utils.flag(this, "_name", key);
|
|
1660
|
+
const promise = obj.then((value) => {
|
|
1661
|
+
utils.flag(this, "object", value);
|
|
1662
|
+
return result.call(this, ...args);
|
|
1663
|
+
}, (err) => {
|
|
1664
|
+
const _error = new AssertionError(`promise rejected "${utils.inspect(err)}" instead of resolving`, { showDiff: false });
|
|
1665
|
+
_error.cause = err;
|
|
1666
|
+
_error.stack = error.stack.replace(error.message, _error.message);
|
|
1667
|
+
throw _error;
|
|
1668
|
+
});
|
|
1669
|
+
return recordAsyncExpect(test, promise, createAssertionMessage(utils, this, !!args.length), error);
|
|
1670
|
+
};
|
|
1671
|
+
} });
|
|
1672
|
+
return proxy;
|
|
1673
|
+
});
|
|
1674
|
+
utils.addProperty(chai.Assertion.prototype, "rejects", function __VITEST_REJECTS__() {
|
|
1675
|
+
const error = new Error("rejects");
|
|
1676
|
+
utils.flag(this, "promise", "rejects");
|
|
1677
|
+
utils.flag(this, "error", error);
|
|
1678
|
+
const test = utils.flag(this, "vitest-test");
|
|
1679
|
+
const obj = utils.flag(this, "object");
|
|
1680
|
+
const wrapper = typeof obj === "function" ? obj() : obj;
|
|
1681
|
+
if (utils.flag(this, "poll")) {
|
|
1682
|
+
throw new SyntaxError(`expect.poll() is not supported in combination with .rejects`);
|
|
1683
|
+
}
|
|
1684
|
+
if (typeof (wrapper === null || wrapper === void 0 ? void 0 : wrapper.then) !== "function") {
|
|
1685
|
+
throw new TypeError(`You must provide a Promise to expect() when using .rejects, not '${typeof wrapper}'.`);
|
|
1686
|
+
}
|
|
1687
|
+
const proxy = new Proxy(this, { get: (target, key, receiver) => {
|
|
1688
|
+
const result = Reflect.get(target, key, receiver);
|
|
1689
|
+
if (typeof result !== "function") {
|
|
1690
|
+
return result instanceof chai.Assertion ? proxy : result;
|
|
1691
|
+
}
|
|
1692
|
+
return (...args) => {
|
|
1693
|
+
utils.flag(this, "_name", key);
|
|
1694
|
+
const promise = wrapper.then((value) => {
|
|
1695
|
+
const _error = new AssertionError(`promise resolved "${utils.inspect(value)}" instead of rejecting`, {
|
|
1696
|
+
showDiff: true,
|
|
1697
|
+
expected: new Error("rejected promise"),
|
|
1698
|
+
actual: value
|
|
1699
|
+
});
|
|
1700
|
+
_error.stack = error.stack.replace(error.message, _error.message);
|
|
1701
|
+
throw _error;
|
|
1702
|
+
}, (err) => {
|
|
1703
|
+
utils.flag(this, "object", err);
|
|
1704
|
+
return result.call(this, ...args);
|
|
1705
|
+
});
|
|
1706
|
+
return recordAsyncExpect(test, promise, createAssertionMessage(utils, this, !!args.length), error);
|
|
1707
|
+
};
|
|
1708
|
+
} });
|
|
1709
|
+
return proxy;
|
|
1710
|
+
});
|
|
1711
|
+
};
|
|
1712
|
+
function ordinalOf(i) {
|
|
1713
|
+
const j = i % 10;
|
|
1714
|
+
const k = i % 100;
|
|
1715
|
+
if (j === 1 && k !== 11) {
|
|
1716
|
+
return `${i}st`;
|
|
1717
|
+
}
|
|
1718
|
+
if (j === 2 && k !== 12) {
|
|
1719
|
+
return `${i}nd`;
|
|
1720
|
+
}
|
|
1721
|
+
if (j === 3 && k !== 13) {
|
|
1722
|
+
return `${i}rd`;
|
|
1723
|
+
}
|
|
1724
|
+
return `${i}th`;
|
|
1725
|
+
}
|
|
1726
|
+
function formatCalls(spy, msg, showActualCall) {
|
|
1727
|
+
if (spy.mock.calls.length) {
|
|
1728
|
+
msg += c.gray(`\n\nReceived: \n\n${spy.mock.calls.map((callArg, i) => {
|
|
1729
|
+
let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call:\n\n`);
|
|
1730
|
+
if (showActualCall) {
|
|
1731
|
+
methodCall += diff(showActualCall, callArg, { omitAnnotationLines: true });
|
|
1732
|
+
} else {
|
|
1733
|
+
methodCall += stringify(callArg).split("\n").map((line) => ` ${line}`).join("\n");
|
|
1734
|
+
}
|
|
1735
|
+
methodCall += "\n";
|
|
1736
|
+
return methodCall;
|
|
1737
|
+
}).join("\n")}`);
|
|
1738
|
+
}
|
|
1739
|
+
msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`);
|
|
1740
|
+
return msg;
|
|
1741
|
+
}
|
|
1742
|
+
function formatReturns(spy, results, msg, showActualReturn) {
|
|
1743
|
+
if (results.length) {
|
|
1744
|
+
msg += c.gray(`\n\nReceived: \n\n${results.map((callReturn, i) => {
|
|
1745
|
+
let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:\n\n`);
|
|
1746
|
+
if (showActualReturn) {
|
|
1747
|
+
methodCall += diff(showActualReturn, callReturn.value, { omitAnnotationLines: true });
|
|
1748
|
+
} else {
|
|
1749
|
+
methodCall += stringify(callReturn).split("\n").map((line) => ` ${line}`).join("\n");
|
|
1750
|
+
}
|
|
1751
|
+
methodCall += "\n";
|
|
1752
|
+
return methodCall;
|
|
1753
|
+
}).join("\n")}`);
|
|
1754
|
+
}
|
|
1755
|
+
msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`);
|
|
1756
|
+
return msg;
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
function getMatcherState(assertion, expect) {
|
|
1760
|
+
const obj = assertion._obj;
|
|
1761
|
+
const isNot = util.flag(assertion, "negate");
|
|
1762
|
+
const promise = util.flag(assertion, "promise") || "";
|
|
1763
|
+
const customMessage = util.flag(assertion, "message");
|
|
1764
|
+
const jestUtils = {
|
|
1765
|
+
...getMatcherUtils(),
|
|
1766
|
+
diff,
|
|
1767
|
+
stringify,
|
|
1768
|
+
iterableEquality,
|
|
1769
|
+
subsetEquality
|
|
1770
|
+
};
|
|
1771
|
+
let task = util.flag(assertion, "vitest-test");
|
|
1772
|
+
const currentTestName = (task === null || task === void 0 ? void 0 : task.fullTestName) ?? "";
|
|
1773
|
+
if ((task === null || task === void 0 ? void 0 : task.type) !== "test") {
|
|
1774
|
+
task = undefined;
|
|
1775
|
+
}
|
|
1776
|
+
const matcherState = {
|
|
1777
|
+
...getState(expect),
|
|
1778
|
+
task,
|
|
1779
|
+
currentTestName,
|
|
1780
|
+
customTesters: getCustomEqualityTesters(),
|
|
1781
|
+
isNot,
|
|
1782
|
+
utils: jestUtils,
|
|
1783
|
+
promise,
|
|
1784
|
+
equals,
|
|
1785
|
+
suppressedErrors: [],
|
|
1786
|
+
soft: util.flag(assertion, "soft"),
|
|
1787
|
+
poll: util.flag(assertion, "poll")
|
|
1788
|
+
};
|
|
1789
|
+
return {
|
|
1790
|
+
state: matcherState,
|
|
1791
|
+
isNot,
|
|
1792
|
+
obj,
|
|
1793
|
+
customMessage
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
class JestExtendError extends Error {
|
|
1797
|
+
constructor(message, actual, expected) {
|
|
1798
|
+
super(message);
|
|
1799
|
+
this.actual = actual;
|
|
1800
|
+
this.expected = expected;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
function JestExtendPlugin(c, expect, matchers) {
|
|
1804
|
+
return (_, utils) => {
|
|
1805
|
+
Object.entries(matchers).forEach(([expectAssertionName, expectAssertion]) => {
|
|
1806
|
+
function expectWrapper(...args) {
|
|
1807
|
+
const { state, isNot, obj, customMessage } = getMatcherState(this, expect);
|
|
1808
|
+
const result = expectAssertion.call(state, obj, ...args);
|
|
1809
|
+
if (result && typeof result === "object" && typeof result.then === "function") {
|
|
1810
|
+
const thenable = result;
|
|
1811
|
+
return thenable.then(({ pass, message, actual, expected }) => {
|
|
1812
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1813
|
+
const errorMessage = customMessage != null ? customMessage : message();
|
|
1814
|
+
throw new JestExtendError(errorMessage, actual, expected);
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
const { pass, message, actual, expected } = result;
|
|
1819
|
+
if (pass && isNot || !pass && !isNot) {
|
|
1820
|
+
const errorMessage = customMessage != null ? customMessage : message();
|
|
1821
|
+
throw new JestExtendError(errorMessage, actual, expected);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
const softWrapper = wrapAssertion(utils, expectAssertionName, expectWrapper);
|
|
1825
|
+
utils.addMethod(globalThis[JEST_MATCHERS_OBJECT].matchers, expectAssertionName, softWrapper);
|
|
1826
|
+
utils.addMethod(c.Assertion.prototype, expectAssertionName, softWrapper);
|
|
1827
|
+
class CustomMatcher extends AsymmetricMatcher {
|
|
1828
|
+
constructor(inverse = false, ...sample) {
|
|
1829
|
+
super(sample, inverse);
|
|
1830
|
+
}
|
|
1831
|
+
asymmetricMatch(other) {
|
|
1832
|
+
const { pass } = expectAssertion.call(this.getMatcherContext(expect), other, ...this.sample);
|
|
1833
|
+
return this.inverse ? !pass : pass;
|
|
1834
|
+
}
|
|
1835
|
+
toString() {
|
|
1836
|
+
return `${this.inverse ? "not." : ""}${expectAssertionName}`;
|
|
1837
|
+
}
|
|
1838
|
+
getExpectedType() {
|
|
1839
|
+
return "any";
|
|
1840
|
+
}
|
|
1841
|
+
toAsymmetricMatcher() {
|
|
1842
|
+
return `${this.toString()}<${this.sample.map((item) => stringify(item)).join(", ")}>`;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
const customMatcher = (...sample) => new CustomMatcher(false, ...sample);
|
|
1846
|
+
Object.defineProperty(expect, expectAssertionName, {
|
|
1847
|
+
configurable: true,
|
|
1848
|
+
enumerable: true,
|
|
1849
|
+
value: customMatcher,
|
|
1850
|
+
writable: true
|
|
1851
|
+
});
|
|
1852
|
+
Object.defineProperty(expect.not, expectAssertionName, {
|
|
1853
|
+
configurable: true,
|
|
1854
|
+
enumerable: true,
|
|
1855
|
+
value: (...sample) => new CustomMatcher(true, ...sample),
|
|
1856
|
+
writable: true
|
|
1857
|
+
});
|
|
1858
|
+
// keep track of asymmetric matchers on global so that it can be copied over to local context's `expect`.
|
|
1859
|
+
// note that the negated variant is automatically shared since it's assigned on the single `expect.not` object.
|
|
1860
|
+
Object.defineProperty(globalThis[ASYMMETRIC_MATCHERS_OBJECT], expectAssertionName, {
|
|
1861
|
+
configurable: true,
|
|
1862
|
+
enumerable: true,
|
|
1863
|
+
value: customMatcher,
|
|
1864
|
+
writable: true
|
|
1865
|
+
});
|
|
1866
|
+
});
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
const JestExtend = (chai, utils) => {
|
|
1870
|
+
utils.addMethod(chai.expect, "extend", (expect, expects) => {
|
|
1871
|
+
use(JestExtendPlugin(chai, expect, expects));
|
|
1872
|
+
});
|
|
1873
|
+
};
|
|
1874
|
+
|
|
1875
|
+
export { ASYMMETRIC_MATCHERS_OBJECT, Any, Anything, ArrayContaining, AsymmetricMatcher, GLOBAL_EXPECT, JEST_MATCHERS_OBJECT, JestAsymmetricMatchers, JestChaiExpect, JestExtend, MATCHERS_OBJECT, ObjectContaining, SchemaMatching, StringContaining, StringMatching, addCustomEqualityTesters, arrayBufferEquality, customMatchers, equals, fnNameFor, generateToBeMessage, getObjectKeys, getObjectSubset, getState, hasAsymmetric, hasProperty, isA, isAsymmetric, isImmutableUnorderedKeyed, isImmutableUnorderedSet, isStandardSchema, iterableEquality, pluralize, setState, sparseArrayEquality, subsetEquality, typeEquality };
|