@unrift/core 0.2.1 → 0.4.0

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.
Files changed (68) hide show
  1. package/bin/unrift.mjs +2 -1
  2. package/dist/esm/cli.js +19 -13
  3. package/dist/esm/context.d.ts +1 -2
  4. package/dist/esm/context.d.ts.map +1 -1
  5. package/dist/esm/expect.d.ts +21 -1
  6. package/dist/esm/expect.d.ts.map +1 -1
  7. package/dist/esm/index.d.ts +3 -0
  8. package/dist/esm/index.d.ts.map +1 -1
  9. package/dist/esm/index.js +3 -0
  10. package/dist/esm/interface.d.ts +3 -3
  11. package/dist/esm/interface.d.ts.map +1 -1
  12. package/dist/esm/interface.js +11 -6
  13. package/dist/esm/matchers.d.ts.map +1 -1
  14. package/dist/esm/matchers.js +261 -1
  15. package/dist/esm/mock/auto.d.ts +27 -0
  16. package/dist/esm/mock/auto.d.ts.map +1 -0
  17. package/dist/esm/mock/auto.js +108 -0
  18. package/dist/esm/mock/fetch.d.ts +47 -0
  19. package/dist/esm/mock/fetch.d.ts.map +1 -0
  20. package/dist/esm/mock/fetch.js +149 -0
  21. package/dist/esm/mock/fs.d.ts +54 -0
  22. package/dist/esm/mock/fs.d.ts.map +1 -0
  23. package/dist/esm/mock/fs.js +125 -0
  24. package/dist/esm/mock/index.d.ts +70 -0
  25. package/dist/esm/mock/index.d.ts.map +1 -0
  26. package/dist/esm/mock/index.js +70 -0
  27. package/dist/esm/mock/module-loader.d.ts +66 -0
  28. package/dist/esm/mock/module-loader.d.ts.map +1 -0
  29. package/dist/esm/mock/module-loader.js +123 -0
  30. package/dist/esm/mock/module.d.ts +99 -0
  31. package/dist/esm/mock/module.d.ts.map +1 -0
  32. package/dist/esm/mock/module.js +198 -0
  33. package/dist/esm/mock/register-loader.d.ts +17 -0
  34. package/dist/esm/mock/register-loader.d.ts.map +1 -0
  35. package/dist/esm/mock/register-loader.js +45 -0
  36. package/dist/esm/mock/spy.d.ts +73 -0
  37. package/dist/esm/mock/spy.d.ts.map +1 -0
  38. package/dist/esm/mock/spy.js +307 -0
  39. package/dist/esm/mock/stub.d.ts +26 -0
  40. package/dist/esm/mock/stub.d.ts.map +1 -0
  41. package/dist/esm/mock/stub.js +82 -0
  42. package/dist/esm/mock/timers.d.ts +51 -0
  43. package/dist/esm/mock/timers.d.ts.map +1 -0
  44. package/dist/esm/mock/timers.js +581 -0
  45. package/dist/esm/runner.d.ts +1 -0
  46. package/dist/esm/runner.d.ts.map +1 -1
  47. package/dist/esm/runner.js +138 -28
  48. package/dist/esm/test.d.ts +3 -2
  49. package/dist/esm/test.d.ts.map +1 -1
  50. package/dist/esm/test.js +17 -7
  51. package/dist/esm/types.d.ts +0 -1
  52. package/dist/esm/types.d.ts.map +1 -1
  53. package/dist/esm/types.js +0 -1
  54. package/dist/esm/utils/compareEngine.d.ts.map +1 -1
  55. package/dist/esm/utils/compareEngine.js +32 -1
  56. package/dist/esm/utils/defineConfig.d.ts +0 -1
  57. package/dist/esm/utils/defineConfig.d.ts.map +1 -1
  58. package/dist/esm/utils/diff.d.ts.map +1 -1
  59. package/dist/esm/utils/diff.js +31 -0
  60. package/dist/esm/utils/discoverTestFiles.js +1 -1
  61. package/dist/esm/utils/helpers.d.ts +21 -0
  62. package/dist/esm/utils/helpers.d.ts.map +1 -1
  63. package/dist/esm/utils/helpers.js +97 -0
  64. package/dist/esm/utils/loadConfig.d.ts.map +1 -1
  65. package/dist/esm/utils/loadConfig.js +2 -2
  66. package/dist/esm/utils/toThrow.d.ts.map +1 -1
  67. package/dist/esm/utils/toThrow.js +2 -1
  68. package/package.json +8 -3
package/bin/unrift.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
- // Run the real CLI
4
+ // Run the real CLI. The CLI installs the module-mock loader itself
5
+ // (see src/mock/register-loader.ts), so this bin stays a thin entry point.
5
6
  await import("../dist/esm/cli.js");
package/dist/esm/cli.js CHANGED
@@ -2,7 +2,13 @@
2
2
  import { readFileSync } from "node:fs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { runTestsCLI } from "./runner.js";
5
+ import { registerModuleLoader } from "./mock/register-loader.js";
6
+ import { runTestsCLI, watchTestsCLI } from "./runner.js";
7
+ import { safeRegExp } from "./utils/helpers.js";
8
+ // Install the module-mock loader before any test file is imported. Both the
9
+ // core bin and the unrift wrapper bin run this CLI, so this is the single
10
+ // place module mocking is bootstrapped.
11
+ registerModuleLoader();
6
12
  const __cliDir = dirname(fileURLToPath(import.meta.url));
7
13
  function readVersion() {
8
14
  // Walk up from current file to find package.json (works from both src/ and dist/esm/)
@@ -21,15 +27,6 @@ function readVersion() {
21
27
  return "0.0.0";
22
28
  }
23
29
  const version = readVersion();
24
- function safeRegExp(source) {
25
- try {
26
- return new RegExp(source);
27
- }
28
- catch (err) {
29
- const msg = err instanceof Error ? err.message : String(err);
30
- throw new Error(`Invalid pattern regex "${source}": ${msg}`);
31
- }
32
- }
33
30
  function printHelp() {
34
31
  console.log(`
35
32
  Unrift Test Runner v${version}
@@ -41,6 +38,7 @@ Options:
41
38
  -c, --config <path> Path to config file
42
39
  -b, --bail Stop on first test failure
43
40
  -t, --timeout <ms> Default test timeout in milliseconds
41
+ -w, --watch Re-run tests on file changes
44
42
  -d, --debug Enable debug logging
45
43
  -l, --list List discovered test files
46
44
  -j, --json Output test results as JSON
@@ -53,6 +51,7 @@ Examples:
53
51
  unrift math
54
52
  unrift -c test/config.ts --bail
55
53
  unrift --timeout 10000
54
+ unrift --watch
56
55
  `);
57
56
  }
58
57
  function parseArgs(argv) {
@@ -66,6 +65,7 @@ function parseArgs(argv) {
66
65
  let list = false;
67
66
  let json = false;
68
67
  let cacheClean = false;
68
+ let watch = false;
69
69
  const rest = [];
70
70
  for (let i = 0; i < argv.length; i++) {
71
71
  const a = argv[i];
@@ -111,6 +111,10 @@ function parseArgs(argv) {
111
111
  i++;
112
112
  continue;
113
113
  }
114
+ if (a === "--watch" || a === "-w") {
115
+ watch = true;
116
+ continue;
117
+ }
114
118
  if (a === "--cache-clean" || a === "--clear-cache") {
115
119
  cacheClean = true;
116
120
  continue;
@@ -129,11 +133,12 @@ function parseArgs(argv) {
129
133
  list,
130
134
  json,
131
135
  cacheClean,
136
+ watch,
132
137
  help,
133
138
  showVersion,
134
139
  };
135
140
  }
136
- const { configPath, pattern, timeoutMs, bail, debug, list, json, cacheClean, help, showVersion, } = parseArgs(process.argv.slice(2));
141
+ const { configPath, pattern, timeoutMs, bail, debug, list, json, cacheClean, watch, help, showVersion, } = parseArgs(process.argv.slice(2));
137
142
  if (showVersion) {
138
143
  console.log(version);
139
144
  process.exit(0);
@@ -142,7 +147,7 @@ if (help) {
142
147
  printHelp();
143
148
  process.exit(0);
144
149
  }
145
- runTestsCLI({
150
+ const runOpts = {
146
151
  pattern,
147
152
  configPath,
148
153
  timeoutMs,
@@ -151,7 +156,8 @@ runTestsCLI({
151
156
  list,
152
157
  json,
153
158
  cacheClean,
154
- }).catch((err) => {
159
+ };
160
+ (watch ? watchTestsCLI(runOpts) : runTestsCLI(runOpts)).catch((err) => {
155
161
  console.error(err);
156
162
  process.exit(1);
157
163
  });
@@ -4,10 +4,9 @@ export declare enum RunState {
4
4
  Run = "run",
5
5
  Idle = "idle"
6
6
  }
7
- type UnriftState = RunState;
8
7
  interface UnriftGlobalContext {
9
8
  currentSuite: Suite | null;
10
- state: UnriftState;
9
+ state: RunState;
11
10
  }
12
11
  export declare const unriftGlobalContext: UnriftGlobalContext;
13
12
  export declare function assertRegisterState(apiName: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,oBAAY,QAAQ;IAClB,QAAQ,aAAa;IACrB,GAAG,QAAQ;IACX,IAAI,SAAS;CACd;AAED,KAAK,WAAW,GAAG,QAAQ,CAAC;AAE5B,UAAU,mBAAmB;IAC3B,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,eAAO,MAAM,mBAAmB,EAAE,mBAGjC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,QAOlD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,oBAAY,QAAQ;IAClB,QAAQ,aAAa;IACrB,GAAG,QAAQ;IACX,IAAI,SAAS;CACd;AAED,UAAU,mBAAmB;IAC3B,YAAY,EAAE,KAAK,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,eAAO,MAAM,mBAAmB,EAAE,mBAGjC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,QAOlD"}
@@ -13,13 +13,33 @@ export interface Matchers<T> {
13
13
  toBeUndefined(): void | Promise<void>;
14
14
  toBeGreaterThan(expected: number): void | Promise<void>;
15
15
  toBeLessThan(expected: number): void | Promise<void>;
16
- toBeInstanceOf(expected: new (...args: unknown[]) => unknown): void | Promise<void>;
16
+ toBeInstanceOf(expected: {
17
+ prototype: unknown;
18
+ }): void | Promise<void>;
17
19
  toContain(expected: unknown): void | Promise<void>;
18
20
  toEqual(expected: unknown): void | Promise<void>;
19
21
  toHaveLength(expected: number): void | Promise<void>;
20
22
  toMatch(expected: RegExp | string): void | Promise<void>;
21
23
  toStrictEqual(expected: unknown): void | Promise<void>;
22
24
  toThrow(expected?: unknown): void | Promise<void>;
25
+ toBeGreaterThanOrEqual(expected: number): void | Promise<void>;
26
+ toBeLessThanOrEqual(expected: number): void | Promise<void>;
27
+ toBeNaN(): void | Promise<void>;
28
+ toBeFinite(): void | Promise<void>;
29
+ toMatchObject(expected: Record<string, unknown>): void | Promise<void>;
30
+ toHaveProperty(path: string | readonly string[], value?: unknown): void | Promise<void>;
31
+ toHaveBeenCalled(): void | Promise<void>;
32
+ toHaveBeenCalledTimes(expected: number): void | Promise<void>;
33
+ toHaveBeenCalledWith(...args: unknown[]): void | Promise<void>;
34
+ toHaveBeenLastCalledWith(...args: unknown[]): void | Promise<void>;
35
+ toHaveBeenNthCalledWith(n: number, ...args: unknown[]): void | Promise<void>;
36
+ toHaveReturned(): void | Promise<void>;
37
+ toHaveReturnedTimes(expected: number): void | Promise<void>;
38
+ toHaveReturnedWith(expected: unknown): void | Promise<void>;
39
+ toHaveLastReturnedWith(expected: unknown): void | Promise<void>;
40
+ toHaveNthReturnedWith(n: number, expected: unknown): void | Promise<void>;
41
+ toHaveFetched(urlOrMatcher: string | RegExp | ((req: Request) => boolean)): void | Promise<void>;
42
+ toHaveFetchedTimes(expected: number): void | Promise<void>;
23
43
  readonly not: Matchers<T>;
24
44
  readonly resolves: Matchers<Awaited<T>>;
25
45
  readonly rejects: Matchers<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAC;AAK1D,wBAAgB,cAAc,CAAC,WAAW,EAAE,UAAU,QAErD;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,WAAW,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,cAAc,CACZ,QAAQ,EAAE,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAC5C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;CACrC;AAID,MAAM,WAAW,QAAQ,CAAC,CAAC;CAAI;AAE/B,MAAM,WAAW,eAAe;IAC9B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,eAAO,MAAM,MAAM,EAAE,eAsGjB,CAAC"}
1
+ {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,UAAU,EAAE,MAAM,SAAS,CAAC;AAK1D,wBAAgB,cAAc,CAAC,WAAW,EAAE,UAAU,QAErD;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,WAAW,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,QAAQ,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,cAAc,CAAC,QAAQ,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,SAAS,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,OAAO,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,UAAU,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,cAAc,CACZ,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAChC,KAAK,CAAC,EAAE,OAAO,GACd,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGxB,gBAAgB,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,oBAAoB,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,wBAAwB,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,kBAAkB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAG1E,aAAa,CACX,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,GAC1D,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;CACrC;AAID,MAAM,WAAW,QAAQ,CAAC,CAAC;CAAI;AAE/B,MAAM,WAAW,eAAe;IAC9B,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,eAAO,MAAM,MAAM,EAAE,eAsGjB,CAAC"}
@@ -4,5 +4,8 @@ export { describe, it, test } from "./interface";
4
4
  export { expect, extendMatchers } from "./expect";
5
5
  export type { Matchers, ExpectInterface } from "./expect";
6
6
  export type { MatcherContext, MatcherFn, MatcherMap, Promisable, } from "./types";
7
+ export { runEngine } from "./run";
7
8
  export type { RunEngineResult } from "./run";
9
+ export { mock, spy, spyOn, isSpy, mockObject, mockClass, stub, mockGlobal, mockFetch, mockFetchOnce, useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime, doMock, unmock, listMockedSpecs, fromModule, mockFs, clearAllMocks, resetAllMocks, restoreAllMocks, } from "./mock/index";
10
+ export type { Spy, MockState, MockResult, MockFetch, FetchMatcher, MockResponseInit, MockFetchResponse, UseFakeTimersOptions, FakeableApi, LoaderMessage, FakeFsHandle, FakeFsState, } from "./mock/index";
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAElD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,cAAc,EACd,SAAS,EACT,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAElD,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,cAAc,EACd,SAAS,EACT,UAAU,EACV,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,YAAY,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAG7C,OAAO,EACL,IAAI,EACJ,GAAG,EACH,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,IAAI,EACJ,UAAU,EACV,SAAS,EACT,aAAa,EACb,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACZ,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,eAAe,EACf,UAAU,EACV,MAAM,EACN,aAAa,EACb,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,GAAG,EACH,SAAS,EACT,UAAU,EACV,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,cAAc,CAAC"}
package/dist/esm/index.js CHANGED
@@ -2,3 +2,6 @@ export { beforeEach, afterEach, beforeAll, afterAll } from "./hooks.js";
2
2
  export { defineConfig } from "./utils/defineConfig.js";
3
3
  export { describe, it, test } from "./interface.js";
4
4
  export { expect, extendMatchers } from "./expect.js";
5
+ export { runEngine } from "./run.js";
6
+ // Mocking suite
7
+ export { mock, spy, spyOn, isSpy, mockObject, mockClass, stub, mockGlobal, mockFetch, mockFetchOnce, useFakeTimers, useRealTimers, advanceTimersByTime, advanceTimersByTimeAsync, runAllTimers, runAllTimersAsync, runOnlyPendingTimers, runOnlyPendingTimersAsync, getTimerCount, setSystemTime, getRealSystemTime, doMock, unmock, listMockedSpecs, fromModule, mockFs, clearAllMocks, resetAllMocks, restoreAllMocks, } from "./mock/index.js";
@@ -9,9 +9,9 @@ type DescribeFn = {
9
9
  todo: (description: string) => void;
10
10
  };
11
11
  type ItFn = {
12
- (description: string, fn: PromisableFn<void>): void;
13
- only: (description: string, fn: PromisableFn<void>) => void;
14
- skip: (description: string, fn: PromisableFn<void>) => void;
12
+ (description: string, fn: PromisableFn<void>, timeoutMs?: number): void;
13
+ only: (description: string, fn: PromisableFn<void>, timeoutMs?: number) => void;
14
+ skip: (description: string, fn: PromisableFn<void>, timeoutMs?: number) => void;
15
15
  todo: (description: string) => void;
16
16
  };
17
17
  declare const describe: DescribeFn;
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/interface.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,YAAY,EAAY,MAAM,SAAS,CAAC;AAEtD;;GAEG;AAEH,KAAK,UAAU,GAAG;IAChB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC5C,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,KAAK,IAAI,GAAG;IACV,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC5D,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC5D,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAmBF,QAAA,MAAM,QAAQ,EAAE,UAC8B,CAAC;AAc/C,QAAA,MAAM,EAAE,EAAE,IAAkE,CAAC;AAK7E,QAAA,MAAM,IAAI,EAAE,IAAS,CAAC;AAEtB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/interface.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,YAAY,EAAY,MAAM,SAAS,CAAC;AAEtD;;GAEG;AAEH,KAAK,UAAU,GAAG;IAChB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC5C,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IACpD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,KAAK,IAAI,GAAG;IACV,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxE,IAAI,EAAE,CACJ,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,EACtB,SAAS,CAAC,EAAE,MAAM,KACf,IAAI,CAAC;IACV,IAAI,EAAE,CACJ,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,EACtB,SAAS,CAAC,EAAE,MAAM,KACf,IAAI,CAAC;IACV,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AA6BF,QAAA,MAAM,QAAQ,EAAE,UAC8B,CAAC;AAmB/C,QAAA,MAAM,EAAE,EAAE,IACyC,CAAC;AAOpD,QAAA,MAAM,IAAI,EAAE,IAAS,CAAC;AAEtB,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC"}
@@ -9,7 +9,12 @@ function _describe(description, fn, mode) {
9
9
  parent.addSuite(suite);
10
10
  unriftGlobalContext.currentSuite = suite;
11
11
  try {
12
- fn();
12
+ const result = fn();
13
+ if (result != null &&
14
+ typeof result.then === "function") {
15
+ throw new Error(`describe() callbacks must be synchronous. ` +
16
+ `Move async setup into beforeAll() or beforeEach().`);
17
+ }
13
18
  }
14
19
  finally {
15
20
  unriftGlobalContext.currentSuite = parent;
@@ -19,14 +24,14 @@ const describe = (description, fn) => _describe(description, fn, TaskMode.Defaul
19
24
  describe.only = (description, fn) => _describe(description, fn, TaskMode.Only);
20
25
  describe.skip = (description, fn) => _describe(description, fn, TaskMode.Skip);
21
26
  describe.todo = (description) => _describe(description, () => { }, TaskMode.Todo);
22
- function _it(description, fn, mode) {
27
+ function _it(description, fn, mode, timeoutMs) {
23
28
  assertRegisterState("it");
24
- const test = new Test(description, fn, mode);
29
+ const test = new Test(description, fn, mode, timeoutMs);
25
30
  getCurrentSuite().addTest(test);
26
31
  }
27
- const it = (description, fn) => _it(description, fn, TaskMode.Default);
28
- it.only = (description, fn) => _it(description, fn, TaskMode.Only);
29
- it.skip = (description, fn) => _it(description, fn, TaskMode.Skip);
32
+ const it = (description, fn, timeoutMs) => _it(description, fn, TaskMode.Default, timeoutMs);
33
+ it.only = (description, fn, timeoutMs) => _it(description, fn, TaskMode.Only, timeoutMs);
34
+ it.skip = (description, fn, timeoutMs) => _it(description, fn, TaskMode.Skip, timeoutMs);
30
35
  it.todo = (description) => _it(description, () => { }, TaskMode.Todo);
31
36
  const test = it;
32
37
  export { describe, it, test };
@@ -1 +1 @@
1
- {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/matchers.ts"],"names":[],"mappings":"AAUA,wBAAgB,sBAAsB,SAMrC;AAED,wBAAgB,aAAa,SAE5B"}
1
+ {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/matchers.ts"],"names":[],"mappings":"AAyGA,wBAAgB,sBAAsB,SAMrC;AAED,wBAAgB,aAAa,SAE5B"}
@@ -1,8 +1,81 @@
1
1
  import { expect } from "./expect.js";
2
+ import { isSpy } from "./mock/spy.js";
3
+ import { isFetchMock } from "./mock/fetch.js";
2
4
  import { deepEqual } from "./utils/deepEqual.js";
3
5
  import { looseEqual } from "./utils/looseEqual.js";
6
+ import { regexTest } from "./utils/helpers.js";
4
7
  import { toThrow } from "./utils/toThrow.js";
5
8
  let matchersRegistered = false;
9
+ function ensureSpy(received, matcherName) {
10
+ if (!isSpy(received)) {
11
+ throw new Error(`${matcherName}() requires a spy or mock function (created via spy() or spyOn()), ` +
12
+ `but received ${typeof received === "object" ? Object.prototype.toString.call(received) : typeof received}`);
13
+ }
14
+ }
15
+ function ensureFetchMock(received, matcherName) {
16
+ if (!isFetchMock(received)) {
17
+ throw new Error(`${matcherName}() requires \`mock.fetch\` as the received value, ` +
18
+ `but received ${typeof received === "object" ? Object.prototype.toString.call(received) : typeof received}`);
19
+ }
20
+ }
21
+ function matchesFetchTarget(req, target) {
22
+ if (typeof target === "string")
23
+ return req.url === target;
24
+ if (target instanceof RegExp)
25
+ return regexTest(target, req.url);
26
+ if (typeof target === "function")
27
+ return Boolean(target(req));
28
+ return false;
29
+ }
30
+ function argsEqual(a, b) {
31
+ if (a.length !== b.length)
32
+ return false;
33
+ for (let i = 0; i < a.length; i++) {
34
+ if (!looseEqual(a[i], b[i]))
35
+ return false;
36
+ }
37
+ return true;
38
+ }
39
+ function partialMatch(received, expected) {
40
+ if (expected === null || typeof expected !== "object") {
41
+ return looseEqual(received, expected);
42
+ }
43
+ if (Array.isArray(expected)) {
44
+ if (!Array.isArray(received))
45
+ return false;
46
+ if (received.length !== expected.length)
47
+ return false;
48
+ return expected.every((e, i) => partialMatch(received[i], e));
49
+ }
50
+ if (received === null ||
51
+ typeof received !== "object" ||
52
+ Array.isArray(received)) {
53
+ return false;
54
+ }
55
+ const recv = received;
56
+ const exp = expected;
57
+ for (const key of Object.keys(exp)) {
58
+ if (!Object.hasOwn(recv, key))
59
+ return false;
60
+ if (!partialMatch(recv[key], exp[key]))
61
+ return false;
62
+ }
63
+ return true;
64
+ }
65
+ function getNestedValue(obj, path) {
66
+ const keys = Array.isArray(path) ? path : path.split(".");
67
+ let current = obj;
68
+ for (const key of keys) {
69
+ if (current === null || typeof current !== "object") {
70
+ return { found: false, value: undefined };
71
+ }
72
+ const record = current;
73
+ if (!Object.hasOwn(record, key))
74
+ return { found: false, value: undefined };
75
+ current = record[key];
76
+ }
77
+ return { found: true, value: current };
78
+ }
6
79
  export function ensureInternalMatchers() {
7
80
  if (matchersRegistered)
8
81
  return;
@@ -123,7 +196,7 @@ function registerCoreMatchers() {
123
196
  }
124
197
  const pass = typeof expected === "string"
125
198
  ? received.includes(expected)
126
- : expected.test(received);
199
+ : regexTest(expected, received);
127
200
  if (this.isNot ? pass : !pass)
128
201
  throw new Error(this.diff(received, expected));
129
202
  },
@@ -138,5 +211,192 @@ function registerCoreMatchers() {
138
211
  if (!pass)
139
212
  throw new Error(message());
140
213
  },
214
+ toBeGreaterThanOrEqual(received, expected) {
215
+ if (typeof received !== "number") {
216
+ throw new Error(this.diff(received, "expected a number"));
217
+ }
218
+ const pass = received >= expected;
219
+ if (this.isNot ? pass : !pass) {
220
+ throw new Error(this.diff(received, `>= ${expected}`));
221
+ }
222
+ },
223
+ toBeLessThanOrEqual(received, expected) {
224
+ if (typeof received !== "number") {
225
+ throw new Error(this.diff(received, "expected a number"));
226
+ }
227
+ const pass = received <= expected;
228
+ if (this.isNot ? pass : !pass) {
229
+ throw new Error(this.diff(received, `<= ${expected}`));
230
+ }
231
+ },
232
+ toBeNaN(received) {
233
+ const pass = Number.isNaN(received);
234
+ if (this.isNot ? pass : !pass) {
235
+ throw new Error(this.diff(received, "NaN"));
236
+ }
237
+ },
238
+ toBeFinite(received) {
239
+ const pass = Number.isFinite(received);
240
+ if (this.isNot ? pass : !pass) {
241
+ throw new Error(this.diff(received, "finite number"));
242
+ }
243
+ },
244
+ toMatchObject(received, expected) {
245
+ if (received === null ||
246
+ typeof received !== "object" ||
247
+ Array.isArray(received)) {
248
+ throw new Error(this.diff(received, "expected a non-array object for toMatchObject()"));
249
+ }
250
+ const pass = partialMatch(received, expected);
251
+ if (this.isNot ? pass : !pass) {
252
+ throw new Error(this.diff(received, expected));
253
+ }
254
+ },
255
+ toHaveBeenCalled(received) {
256
+ ensureSpy(received, "toHaveBeenCalled");
257
+ const pass = received.mock.calls.length > 0;
258
+ if (this.isNot ? pass : !pass) {
259
+ throw new Error(this.diff(`${received.getMockName()} called ${received.mock.calls.length} time(s)`, "to have been called"));
260
+ }
261
+ },
262
+ toHaveBeenCalledTimes(received, expected) {
263
+ ensureSpy(received, "toHaveBeenCalledTimes");
264
+ if (typeof expected !== "number" || !Number.isFinite(expected)) {
265
+ throw new Error(this.diff(expected, "toHaveBeenCalledTimes() requires a finite number"));
266
+ }
267
+ const actual = received.mock.calls.length;
268
+ const pass = actual === expected;
269
+ if (this.isNot ? pass : !pass) {
270
+ throw new Error(this.diff(actual, expected));
271
+ }
272
+ },
273
+ toHaveBeenCalledWith(received, ...expectedArgs) {
274
+ ensureSpy(received, "toHaveBeenCalledWith");
275
+ const pass = received.mock.calls.some((call) => argsEqual(call, expectedArgs));
276
+ if (this.isNot ? pass : !pass) {
277
+ throw new Error(this.diff(received.mock.calls, expectedArgs));
278
+ }
279
+ },
280
+ toHaveBeenLastCalledWith(received, ...expectedArgs) {
281
+ ensureSpy(received, "toHaveBeenLastCalledWith");
282
+ const last = received.mock.lastCall;
283
+ const pass = last !== undefined && argsEqual(last, expectedArgs);
284
+ if (this.isNot ? pass : !pass) {
285
+ throw new Error(this.diff(last ?? "no calls", expectedArgs));
286
+ }
287
+ },
288
+ toHaveBeenNthCalledWith(received, n, ...expectedArgs) {
289
+ ensureSpy(received, "toHaveBeenNthCalledWith");
290
+ if (typeof n !== "number" || !Number.isInteger(n) || n < 1) {
291
+ throw new Error(this.diff(n, "toHaveBeenNthCalledWith() requires a 1-indexed integer"));
292
+ }
293
+ const call = received.mock.calls[n - 1];
294
+ const pass = call !== undefined && argsEqual(call, expectedArgs);
295
+ if (this.isNot ? pass : !pass) {
296
+ throw new Error(this.diff(call ?? `call #${n} not found`, expectedArgs));
297
+ }
298
+ },
299
+ toHaveReturned(received) {
300
+ ensureSpy(received, "toHaveReturned");
301
+ const pass = received.mock.results.some((r) => r.type === "return");
302
+ if (this.isNot ? pass : !pass) {
303
+ throw new Error(this.diff(`${received.mock.results.length} result(s)`, "at least one successful return"));
304
+ }
305
+ },
306
+ toHaveReturnedTimes(received, expected) {
307
+ ensureSpy(received, "toHaveReturnedTimes");
308
+ if (typeof expected !== "number" || !Number.isFinite(expected)) {
309
+ throw new Error(this.diff(expected, "toHaveReturnedTimes() requires a finite number"));
310
+ }
311
+ const actual = received.mock.results.filter((r) => r.type === "return").length;
312
+ const pass = actual === expected;
313
+ if (this.isNot ? pass : !pass) {
314
+ throw new Error(this.diff(actual, expected));
315
+ }
316
+ },
317
+ toHaveReturnedWith(received, expected) {
318
+ ensureSpy(received, "toHaveReturnedWith");
319
+ const pass = received.mock.results.some((r) => r.type === "return" && looseEqual(r.value, expected));
320
+ if (this.isNot ? pass : !pass) {
321
+ const returnValues = received.mock.results
322
+ .filter((r) => r.type === "return")
323
+ .map((r) => r.value);
324
+ throw new Error(this.diff(returnValues, expected));
325
+ }
326
+ },
327
+ toHaveLastReturnedWith(received, expected) {
328
+ ensureSpy(received, "toHaveLastReturnedWith");
329
+ const results = received.mock.results;
330
+ const last = results[results.length - 1];
331
+ const pass = last !== undefined &&
332
+ last.type === "return" &&
333
+ looseEqual(last.value, expected);
334
+ if (this.isNot ? pass : !pass) {
335
+ if (last === undefined) {
336
+ throw new Error(this.diff("no calls", expected));
337
+ }
338
+ if (last.type === "throw") {
339
+ throw new Error(this.diff("last call threw", expected));
340
+ }
341
+ throw new Error(this.diff(last.value, expected));
342
+ }
343
+ },
344
+ toHaveNthReturnedWith(received, n, expected) {
345
+ ensureSpy(received, "toHaveNthReturnedWith");
346
+ if (typeof n !== "number" || !Number.isInteger(n) || n < 1) {
347
+ throw new Error(this.diff(n, "toHaveNthReturnedWith() requires a 1-indexed integer"));
348
+ }
349
+ const result = received.mock.results[n - 1];
350
+ const pass = result !== undefined &&
351
+ result.type === "return" &&
352
+ looseEqual(result.value, expected);
353
+ if (this.isNot ? pass : !pass) {
354
+ if (result === undefined) {
355
+ throw new Error(this.diff(`call #${n} not found`, expected));
356
+ }
357
+ if (result.type === "throw") {
358
+ throw new Error(this.diff(`call #${n} threw`, expected));
359
+ }
360
+ throw new Error(this.diff(result.value, expected));
361
+ }
362
+ },
363
+ toHaveFetched(received, target) {
364
+ ensureFetchMock(received, "toHaveFetched");
365
+ const pass = received.calls.some((req) => matchesFetchTarget(req, target));
366
+ if (this.isNot ? pass : !pass) {
367
+ const urls = received.calls.map((r) => `${r.method} ${r.url}`);
368
+ throw new Error(this.diff(urls, target));
369
+ }
370
+ },
371
+ toHaveFetchedTimes(received, expected) {
372
+ ensureFetchMock(received, "toHaveFetchedTimes");
373
+ if (typeof expected !== "number" || !Number.isFinite(expected)) {
374
+ throw new Error(this.diff(expected, "toHaveFetchedTimes() requires a finite number"));
375
+ }
376
+ const actual = received.calls.length;
377
+ const pass = actual === expected;
378
+ if (this.isNot ? pass : !pass) {
379
+ throw new Error(this.diff(actual, expected));
380
+ }
381
+ },
382
+ toHaveProperty(received, path, ...rest) {
383
+ if (received === null || typeof received !== "object") {
384
+ throw new Error(this.diff(received, "expected an object for toHaveProperty()"));
385
+ }
386
+ const normalizedPath = path;
387
+ const { found, value } = getNestedValue(received, normalizedPath);
388
+ const hasValue = rest.length > 0;
389
+ const expectedValue = rest[0];
390
+ const pass = hasValue ? found && looseEqual(value, expectedValue) : found;
391
+ if (this.isNot ? pass : !pass) {
392
+ const pathStr = Array.isArray(normalizedPath)
393
+ ? normalizedPath.join(".")
394
+ : normalizedPath;
395
+ if (!found) {
396
+ throw new Error(this.diff(Object.keys(received), `property "${pathStr}"`));
397
+ }
398
+ throw new Error(this.diff(value, expectedValue));
399
+ }
400
+ },
141
401
  });
142
402
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Auto-mocking helpers: `mock.object` and `mock.class`.
3
+ *
4
+ * Both walk own + prototype methods and replace each function with a spy.
5
+ * Restoration uses the same registry as `spyOn`, so `mock.restoreAll()`
6
+ * cleans up auto mocks too.
7
+ */
8
+ /**
9
+ * Replace every method on `obj` (own + inherited, excluding Object.prototype)
10
+ * with a spy. Returns the same reference, mutated in place.
11
+ *
12
+ * Cleanup happens via `mock.restoreAll()` - auto-mock spies share the same
13
+ * restorer registry as `spyOn`.
14
+ */
15
+ export declare function mockObject<T extends object>(obj: T): T;
16
+ /**
17
+ * Wrap a constructor so every instance is auto-mocked, and so static
18
+ * methods on the constructor itself are spied too. Returns a Proxy that
19
+ * is `instanceof`-compatible with the original.
20
+ *
21
+ * Instance methods are spied as OWN properties on each instance (shadowing
22
+ * the prototype), NOT by mutating the shared prototype. This keeps call
23
+ * state per-instance (a.run and b.run are separate spies) and leaves the
24
+ * class prototype untouched - nothing to restore, nothing to leak.
25
+ */
26
+ export declare function mockClass<T extends new (...args: any[]) => any>(Ctor: T): T;
27
+ //# sourceMappingURL=auto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../../../src/mock/auto.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+CH;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAatD;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAEvB,CAAC,SAAS,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EACrC,IAAI,EAAE,CAAC,GAAG,CAAC,CA8BZ"}