@unrift/core 0.3.0 → 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 (51) hide show
  1. package/bin/unrift.mjs +2 -1
  2. package/dist/esm/cli.js +5 -0
  3. package/dist/esm/expect.d.ts +12 -0
  4. package/dist/esm/expect.d.ts.map +1 -1
  5. package/dist/esm/index.d.ts +2 -0
  6. package/dist/esm/index.d.ts.map +1 -1
  7. package/dist/esm/index.js +2 -0
  8. package/dist/esm/matchers.d.ts.map +1 -1
  9. package/dist/esm/matchers.js +161 -1
  10. package/dist/esm/mock/auto.d.ts +27 -0
  11. package/dist/esm/mock/auto.d.ts.map +1 -0
  12. package/dist/esm/mock/auto.js +108 -0
  13. package/dist/esm/mock/fetch.d.ts +47 -0
  14. package/dist/esm/mock/fetch.d.ts.map +1 -0
  15. package/dist/esm/mock/fetch.js +149 -0
  16. package/dist/esm/mock/fs.d.ts +54 -0
  17. package/dist/esm/mock/fs.d.ts.map +1 -0
  18. package/dist/esm/mock/fs.js +125 -0
  19. package/dist/esm/mock/index.d.ts +70 -0
  20. package/dist/esm/mock/index.d.ts.map +1 -0
  21. package/dist/esm/mock/index.js +70 -0
  22. package/dist/esm/mock/module-loader.d.ts +66 -0
  23. package/dist/esm/mock/module-loader.d.ts.map +1 -0
  24. package/dist/esm/mock/module-loader.js +123 -0
  25. package/dist/esm/mock/module.d.ts +99 -0
  26. package/dist/esm/mock/module.d.ts.map +1 -0
  27. package/dist/esm/mock/module.js +198 -0
  28. package/dist/esm/mock/register-loader.d.ts +17 -0
  29. package/dist/esm/mock/register-loader.d.ts.map +1 -0
  30. package/dist/esm/mock/register-loader.js +45 -0
  31. package/dist/esm/mock/spy.d.ts +73 -0
  32. package/dist/esm/mock/spy.d.ts.map +1 -0
  33. package/dist/esm/mock/spy.js +307 -0
  34. package/dist/esm/mock/stub.d.ts +26 -0
  35. package/dist/esm/mock/stub.d.ts.map +1 -0
  36. package/dist/esm/mock/stub.js +82 -0
  37. package/dist/esm/mock/timers.d.ts +51 -0
  38. package/dist/esm/mock/timers.d.ts.map +1 -0
  39. package/dist/esm/mock/timers.js +581 -0
  40. package/dist/esm/runner.d.ts.map +1 -1
  41. package/dist/esm/runner.js +13 -8
  42. package/dist/esm/test.d.ts.map +1 -1
  43. package/dist/esm/test.js +12 -5
  44. package/dist/esm/utils/compareEngine.js +3 -3
  45. package/dist/esm/utils/discoverTestFiles.js +1 -1
  46. package/dist/esm/utils/helpers.d.ts +10 -0
  47. package/dist/esm/utils/helpers.d.ts.map +1 -1
  48. package/dist/esm/utils/helpers.js +13 -0
  49. package/dist/esm/utils/toThrow.d.ts.map +1 -1
  50. package/dist/esm/utils/toThrow.js +2 -1
  51. package/package.json +4 -3
package/dist/esm/test.js CHANGED
@@ -1,5 +1,10 @@
1
1
  import { TaskMode, TaskStatus } from "./types.js";
2
2
  import { toError } from "./utils/toError.js";
3
+ /**
4
+ * Captured at module load so the runner's own timing measurements are
5
+ * immune to user mocks (e.g. mock.useFakeTimers patches performance.now).
6
+ */
7
+ const realPerformanceNow = performance.now.bind(performance);
3
8
  export class Test {
4
9
  description;
5
10
  durationMs = 0;
@@ -26,7 +31,7 @@ export class Test {
26
31
  this.durationMs = 0;
27
32
  return;
28
33
  }
29
- const start = performance.now();
34
+ const start = realPerformanceNow();
30
35
  this.status = TaskStatus.Running;
31
36
  const exec = Promise.resolve().then(() => this.fn());
32
37
  try {
@@ -37,9 +42,11 @@ export class Test {
37
42
  reject(new Error(`Test "${this.description}" timed out after ${timeoutMs} ms`));
38
43
  }, timeoutMs);
39
44
  });
40
- // NOTE: Promise.race does not cancel the losing promise. A timed-out test
41
- // function continues running in the background. True cancellation would
42
- // require AbortController integration, which is a larger future change.
45
+ /**
46
+ * NOTE: Promise.race does not cancel the losing promise. A timed-out test
47
+ * function continues running in the background. True cancellation would
48
+ * require AbortController integration, which is a larger future change.
49
+ */
43
50
  try {
44
51
  await Promise.race([exec, timeoutFn]);
45
52
  }
@@ -58,7 +65,7 @@ export class Test {
58
65
  this.status = TaskStatus.Fail;
59
66
  }
60
67
  finally {
61
- this.durationMs = performance.now() - start;
68
+ this.durationMs = realPerformanceNow() - start;
62
69
  }
63
70
  }
64
71
  }
@@ -142,7 +142,7 @@ export function compareInternal(left, right, options, leftToRightSeen, rightToLe
142
142
  const rightSet = currentRight;
143
143
  if (leftSet.size !== rightSet.size)
144
144
  return false;
145
- // Fast path: primitive-only sets can use has() directly — O(n) vs O(n²)
145
+ // Fast path: primitive-only sets can use has() directly - O(n) vs O(n²)
146
146
  let allPrimitive = true;
147
147
  for (const v of leftSet) {
148
148
  if (!isPrimitive(v)) {
@@ -179,7 +179,7 @@ export function compareInternal(left, right, options, leftToRightSeen, rightToLe
179
179
  const rightMap = currentRight;
180
180
  if (leftMap.size !== rightMap.size)
181
181
  return false;
182
- // Fast path: primitive-only keys can use get() directly — O(n) vs O(n²)
182
+ // Fast path: primitive-only keys can use get() directly - O(n) vs O(n²)
183
183
  let allKeysPrimitive = true;
184
184
  for (const k of leftMap.keys()) {
185
185
  if (!isPrimitive(k)) {
@@ -230,7 +230,7 @@ export function compareInternal(left, right, options, leftToRightSeen, rightToLe
230
230
  }
231
231
  }
232
232
  else {
233
- // Prototype-insensitive (looseEqual) — union-of-keys, missing ≈ undefined
233
+ // Prototype-insensitive (looseEqual) - union-of-keys, missing ≈ undefined
234
234
  const leftKeys = Object.keys(currentLeft);
235
235
  const rightKeys = Object.keys(currentRight);
236
236
  const allKeys = new Set([...leftKeys, ...rightKeys]);
@@ -21,7 +21,7 @@ export function discoverTestFiles(dir) {
21
21
  }
22
22
  }
23
23
  catch {
24
- // Broken symlink — skip silently
24
+ // Broken symlink - skip silently
25
25
  }
26
26
  continue;
27
27
  }
@@ -14,6 +14,16 @@ export declare function isGlobPattern(pattern: string): boolean;
14
14
  */
15
15
  export declare function globToRegex(glob: string): RegExp;
16
16
  export declare function safeRegExp(source: string): RegExp;
17
+ /**
18
+ * Stateless RegExp match for user-supplied patterns.
19
+ *
20
+ * `RegExp.prototype.test` advances `lastIndex` for `/g` and `/y` regexes, so
21
+ * reusing the same regex object across calls (a matcher tested against several
22
+ * requests, or the same assertion run twice) can start mid-string and miss.
23
+ * Resetting `lastIndex` first makes every test independent - "does this pattern
24
+ * match the string" regardless of prior calls.
25
+ */
26
+ export declare function regexTest(re: RegExp, value: string): boolean;
17
27
  /**
18
28
  * SameValueZero-ish semantics:
19
29
  * - NaN equals NaN
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA,KAAK,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhF,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,SAAS,GACT,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,cAAc,CAAC;AAEnB,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAY9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyDhD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CASzD"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA,KAAK,SAAS,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhF,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,SAAS,GACT,WAAW,GACX,UAAU,GACV,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,cAAc,CAAC;AAEnB,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAY9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyDhD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG5D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CASzD"}
@@ -98,6 +98,19 @@ export function safeRegExp(source) {
98
98
  throw new Error(`Invalid regex "${source}": ${msg}`);
99
99
  }
100
100
  }
101
+ /**
102
+ * Stateless RegExp match for user-supplied patterns.
103
+ *
104
+ * `RegExp.prototype.test` advances `lastIndex` for `/g` and `/y` regexes, so
105
+ * reusing the same regex object across calls (a matcher tested against several
106
+ * requests, or the same assertion run twice) can start mid-string and miss.
107
+ * Resetting `lastIndex` first makes every test independent - "does this pattern
108
+ * match the string" regardless of prior calls.
109
+ */
110
+ export function regexTest(re, value) {
111
+ re.lastIndex = 0;
112
+ return re.test(value);
113
+ }
101
114
  /**
102
115
  * SameValueZero-ish semantics:
103
116
  * - NaN equals NaN
@@ -1 +1 @@
1
- {"version":3,"file":"toThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/toThrow.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,MAAM,GACN,MAAM,GACN,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,GACnC;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,UAAU,WAAW;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AAgED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,KAAK,UAAQ,GACZ,WAAW,CAgEb"}
1
+ {"version":3,"file":"toThrow.d.ts","sourceRoot":"","sources":["../../../src/utils/toThrow.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,MAAM,GACN,MAAM,GACN,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,GACnC;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,UAAU,WAAW;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AAgED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,KAAK,UAAQ,GACZ,WAAW,CAgEb"}
@@ -1,3 +1,4 @@
1
+ import { regexTest } from "./helpers.js";
1
2
  function isErrorConstructor(err) {
2
3
  return typeof err === "function";
3
4
  }
@@ -24,7 +25,7 @@ function matchThrown(thrown, expected) {
24
25
  }
25
26
  // toThrow(/regex/)
26
27
  if (expected instanceof RegExp) {
27
- return expected.test(msg);
28
+ return regexTest(expected, msg);
28
29
  }
29
30
  // toThrow(ErrorClass)
30
31
  if (isErrorConstructor(expected)) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unrift/core",
3
- "version": "0.3.0",
4
- "description": "Core engine for the Unrift TypeScript test framework — runner, matchers, CLI, and programmatic API",
3
+ "version": "0.4.0",
4
+ "description": "Core engine for the Unrift TypeScript test framework - runner, matchers, CLI, and programmatic API",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "main": "./dist/cjs/index.cjs",
@@ -58,7 +58,8 @@
58
58
  "access": "public"
59
59
  },
60
60
  "dependencies": {
61
- "esbuild": "^0.27.2"
61
+ "esbuild": "^0.27.2",
62
+ "memfs": "^4.57.7"
62
63
  },
63
64
  "devDependencies": {
64
65
  "ts-node": "^10.9.2",