@unrift/core 0.5.0 → 0.5.1

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.
@@ -43,7 +43,7 @@ export interface Matchers<T> {
43
43
  toHaveFetched(urlOrMatcher: string | RegExp | ((req: Request) => boolean)): void | Promise<void>;
44
44
  toHaveFetchedTimes(expected: number): void | Promise<void>;
45
45
  readonly not: Matchers<T>;
46
- readonly resolves: Matchers<Awaited<T>>;
46
+ readonly resolves: Matchers<Settled<T>>;
47
47
  readonly rejects: Matchers<unknown>;
48
48
  }
49
49
  export interface Matchers<T> {
@@ -53,5 +53,8 @@ export interface ExpectInterface {
53
53
  extend<M extends MatcherMap>(m: M): void;
54
54
  readonly matchers: Readonly<MatcherMap>;
55
55
  }
56
+ /** What `.resolves` sees: a function's returned promise, or the promise itself. */
57
+ type Settled<T> = Awaited<T extends (...args: never[]) => infer R ? R : T>;
56
58
  export declare const expect: ExpectInterface;
59
+ export {};
57
60
  //# sourceMappingURL=expect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"expect.d.ts","sourceRoot":"","sources":["../../src/expect.ts"],"names":[],"mappings":"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"}
@@ -1,14 +1,24 @@
1
1
  import { formatDiff } from "./utils/diff.js";
2
+ import { isErrorLike, safeString } from "./utils/helpers.js";
2
3
  const matcherRegistry = Object.create(null);
3
4
  export function extendMatchers(newMatchers) {
4
5
  for (const key in newMatchers)
5
6
  matcherRegistry[key] = newMatchers[key];
6
7
  }
8
+ function describeError(value) {
9
+ return isErrorLike(value) ? value.message : safeString(value);
10
+ }
11
+ function isThenable(value) {
12
+ return (value != null &&
13
+ (typeof value === "object" || typeof value === "function") &&
14
+ typeof value.then === "function");
15
+ }
7
16
  export const expect = (() => {
8
17
  function expectFn(received) {
9
18
  function makeExpectation(isNot) {
10
19
  const ctx = {
11
20
  isNot,
21
+ promise: "",
12
22
  diff(a, b) {
13
23
  return formatDiff(a, b);
14
24
  },
@@ -18,9 +28,10 @@ export const expect = (() => {
18
28
  const fn = matcherRegistry[name];
19
29
  matcherFns[name] = (...args) => fn.call(ctx, received, ...args);
20
30
  }
21
- function makeAsyncProxy(getActual, asyncIsNot) {
31
+ function makeAsyncProxy(getActual, asyncIsNot, promise) {
22
32
  const asyncCtx = {
23
33
  isNot: asyncIsNot,
34
+ promise,
24
35
  diff(a, b) {
25
36
  return formatDiff(a, b);
26
37
  },
@@ -28,15 +39,14 @@ export const expect = (() => {
28
39
  const asyncFns = {};
29
40
  for (const name in matcherRegistry) {
30
41
  const fn = matcherRegistry[name];
31
- asyncFns[name] = async (...args) => {
32
- const actual = await getActual();
33
- return fn.call(asyncCtx, actual, ...args);
34
- };
42
+ // Not async: getActual() runs now, so a bad received value (a sync
43
+ // throw, a non-promise) throws immediately like Jest, even unawaited.
44
+ asyncFns[name] = (...args) => getActual().then((actual) => fn.call(asyncCtx, actual, ...args));
35
45
  }
36
46
  return new Proxy(asyncFns, {
37
47
  get(target, prop) {
38
48
  if (prop === "not") {
39
- return makeAsyncProxy(getActual, !asyncIsNot);
49
+ return makeAsyncProxy(getActual, !asyncIsNot, promise);
40
50
  }
41
51
  if (typeof prop === "string" && !(prop in target)) {
42
52
  throw new Error(`Unknown matcher: ${prop}`);
@@ -49,30 +59,51 @@ export const expect = (() => {
49
59
  get(target, prop) {
50
60
  if (prop === "not")
51
61
  return makeExpectation(!isNot);
62
+ // Same as Jest: a function is called and must return a promise. A
63
+ // sync throw from it isn't a rejection, it fails the assertion.
64
+ const toPromise = (modifier) => {
65
+ const value = typeof received === "function"
66
+ ? received()
67
+ : received;
68
+ if (!isThenable(value)) {
69
+ throw new TypeError(`.${modifier}: received value must be a promise or a function returning a promise, got ${describeError(value)}`);
70
+ }
71
+ return value;
72
+ };
52
73
  if (prop === "resolves") {
53
- return makeAsyncProxy(async () => {
54
- try {
55
- return await received;
56
- }
57
- catch (err) {
58
- throw new Error(`Expected promise to resolve, but it rejected with: ${err instanceof Error ? err.message : String(err)}`);
59
- }
60
- }, isNot);
74
+ return makeAsyncProxy(() => {
75
+ const pending = toPromise("resolves");
76
+ return (async () => {
77
+ try {
78
+ return await pending;
79
+ }
80
+ catch (err) {
81
+ throw new Error(`Expected promise to resolve, but it rejected with: ${describeError(err)}`);
82
+ }
83
+ })();
84
+ }, isNot, "resolves");
61
85
  }
62
86
  if (prop === "rejects") {
63
- return makeAsyncProxy(async () => {
64
- try {
65
- await received;
66
- throw new Error("Expected promise to reject, but it resolved");
67
- }
68
- catch (err) {
69
- if (err instanceof Error &&
70
- err.message === "Expected promise to reject, but it resolved") {
71
- throw err;
87
+ return makeAsyncProxy(() => {
88
+ const pending = toPromise("rejects");
89
+ return (async () => {
90
+ // Tracked with a flag, not by error message, so a promise that
91
+ // rejects with that same text is still treated as a rejection.
92
+ let resolved = false;
93
+ let reason;
94
+ try {
95
+ await pending;
96
+ resolved = true;
97
+ }
98
+ catch (err) {
99
+ reason = err;
100
+ }
101
+ if (resolved) {
102
+ throw new Error("Expected promise to reject, but it resolved");
72
103
  }
73
- return err;
74
- }
75
- }, isNot);
104
+ return reason;
105
+ })();
106
+ }, isNot, "rejects");
76
107
  }
77
108
  if (typeof prop === "string" && !(prop in target)) {
78
109
  throw new Error(`Unknown matcher: ${prop}`);
@@ -1 +1 @@
1
- {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/matchers.ts"],"names":[],"mappings":"AAyGA,wBAAgB,sBAAsB,SAMrC;AAED,wBAAgB,aAAa,SAE5B"}
1
+ {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/matchers.ts"],"names":[],"mappings":"AA6GA,wBAAgB,sBAAsB,SAMrC;AAED,wBAAgB,aAAa,SAE5B"}
@@ -4,7 +4,7 @@ import { isFetchMock } from "./mock/fetch.js";
4
4
  import { deepEqual } from "./utils/deepEqual.js";
5
5
  import { looseEqual } from "./utils/looseEqual.js";
6
6
  import { regexTest } from "./utils/helpers.js";
7
- import { toThrow } from "./utils/toThrow.js";
7
+ import { toThrow, toThrowFromPromise, } from "./utils/toThrow.js";
8
8
  let matchersRegistered = false;
9
9
  function ensureSpy(received, matcherName) {
10
10
  if (!isSpy(received)) {
@@ -223,7 +223,10 @@ function registerCoreMatchers() {
223
223
  }
224
224
  },
225
225
  toThrow(received, expected) {
226
- const { pass, message } = toThrow(received, expected, this.isNot);
226
+ // Under .resolves/.rejects, `received` is the settled value itself.
227
+ const { pass, message } = this.promise
228
+ ? toThrowFromPromise(received, expected, this.isNot, this.promise)
229
+ : toThrow(received, expected, this.isNot);
227
230
  if (!pass)
228
231
  throw new Error(message());
229
232
  },
@@ -16,6 +16,8 @@ export declare enum TaskMode {
16
16
  }
17
17
  export interface MatcherContext {
18
18
  isNot: boolean;
19
+ /** "resolves" or "rejects" under those modifiers, otherwise "" (same as Jest). */
20
+ promise: "" | "resolves" | "rejects";
19
21
  diff(a: unknown, b: unknown): string;
20
22
  }
21
23
  export type MatcherFn = (this: MatcherContext, received: unknown, ...args: readonly unknown[]) => void | Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE3C,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC;AAElD,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,IAAI,SAAS;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,CACtB,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,OAAO,EACjB,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,KACxB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAE3C,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,CAAC;AAElD,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,IAAI,SAAS;CACd;AAED,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,IAAI,SAAS;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,kFAAkF;IAClF,OAAO,EAAE,EAAE,GAAG,UAAU,GAAG,SAAS,CAAC;IACrC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,MAAM,SAAS,GAAG,CACtB,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,OAAO,EACjB,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,KACxB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC"}
@@ -32,5 +32,12 @@ export declare function regexTest(re: RegExp, value: string): boolean;
32
32
  export declare function sameValue(a: unknown, b: unknown): boolean;
33
33
  /** Throws unless `retries` is undefined or a non-negative integer. */
34
34
  export declare function assertValidRetries(retries: unknown, where: string): void;
35
+ /** String() throws for e.g. null-prototype objects; fall back to the tag. */
36
+ export declare function safeString(value: unknown): string;
37
+ /**
38
+ * Error check that also works across realms (vm, workers, jsdom), where
39
+ * `instanceof Error` is false. Same test Jest uses.
40
+ */
41
+ export declare function isErrorLike(value: unknown): value is Error;
35
42
  export {};
36
43
  //# sourceMappingURL=helpers.d.ts.map
@@ -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;;;;;;;;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;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CASxE"}
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;AAED,sEAAsE;AACtE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CASxE;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWjD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAW1D"}
@@ -131,3 +131,33 @@ export function assertValidRetries(retries, where) {
131
131
  throw new TypeError(`${where}: retries must be a non-negative integer, got ${typeof retries === "string" ? JSON.stringify(retries) : String(retries)}`);
132
132
  }
133
133
  }
134
+ /** String() throws for e.g. null-prototype objects; fall back to the tag. */
135
+ export function safeString(value) {
136
+ try {
137
+ return String(value);
138
+ }
139
+ catch {
140
+ // The tag lookup can throw too (a throwing Symbol.toStringTag getter).
141
+ try {
142
+ return Object.prototype.toString.call(value);
143
+ }
144
+ catch {
145
+ return "[unprintable value]";
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * Error check that also works across realms (vm, workers, jsdom), where
151
+ * `instanceof Error` is false. Same test Jest uses.
152
+ */
153
+ export function isErrorLike(value) {
154
+ // Both checks can throw (Proxy traps, a throwing Symbol.toStringTag getter).
155
+ // A value that can't be inspected isn't treated as an Error.
156
+ try {
157
+ return (value instanceof Error ||
158
+ Object.prototype.toString.call(value) === "[object Error]");
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
@@ -7,5 +7,10 @@ interface MatchResult {
7
7
  message: () => string;
8
8
  }
9
9
  export declare function toThrow(received: unknown, expected?: ToThrowExpected, isNot?: boolean): MatchResult;
10
+ /**
11
+ * toThrow under `.resolves` / `.rejects`. `received` is the settled value, not
12
+ * a function. Same as Jest: it only counts as thrown if it's an Error.
13
+ */
14
+ export declare function toThrowFromPromise(received: unknown, expected: ToThrowExpected, isNot: boolean, promise: "resolves" | "rejects"): MatchResult;
10
15
  export {};
11
16
  //# sourceMappingURL=toThrow.d.ts.map
@@ -1 +1 @@
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
+ {"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;AA4ED,wBAAgB,OAAO,CACrB,QAAQ,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,eAAe,EAC1B,KAAK,UAAQ,GACZ,WAAW,CAwDb;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,OAAO,EACjB,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,UAAU,GAAG,SAAS,GAC9B,WAAW,CAyBb"}
@@ -1,9 +1,9 @@
1
- import { regexTest } from "./helpers.js";
1
+ import { isErrorLike, regexTest, safeString } from "./helpers.js";
2
2
  function isErrorConstructor(err) {
3
3
  return typeof err === "function";
4
4
  }
5
5
  function formatThrown(thrown) {
6
- if (thrown instanceof Error) {
6
+ if (isErrorLike(thrown)) {
7
7
  const name = thrown.name || "Error";
8
8
  const msg = thrown.message ?? "";
9
9
  return msg ? `${name}: ${msg}` : name;
@@ -12,13 +12,22 @@ function formatThrown(thrown) {
12
12
  return typeof thrown === "string" ? thrown : JSON.stringify(thrown);
13
13
  }
14
14
  catch {
15
- return String(thrown);
15
+ return safeString(thrown);
16
16
  }
17
17
  }
18
+ function formatExpected(expected) {
19
+ return typeof expected === "string"
20
+ ? `"${expected}"`
21
+ : expected instanceof RegExp
22
+ ? expected.toString()
23
+ : isErrorConstructor(expected)
24
+ ? expected.name || "[AnonymousErrorConstructor]"
25
+ : JSON.stringify(expected, (_key, value) => value instanceof RegExp ? value.toString() : value);
26
+ }
18
27
  function matchThrown(thrown, expected) {
19
28
  if (expected === undefined)
20
29
  return true;
21
- const msg = thrown instanceof Error ? thrown.message : String(thrown);
30
+ const msg = isErrorLike(thrown) ? thrown.message : safeString(thrown);
22
31
  // toThrow("substring")
23
32
  if (typeof expected === "string") {
24
33
  return msg.includes(expected);
@@ -34,7 +43,7 @@ function matchThrown(thrown, expected) {
34
43
  // toThrow({ name?, message? })
35
44
  if (expected && typeof expected === "object") {
36
45
  if (expected.name) {
37
- const thrownName = thrown instanceof Error ? thrown.name : undefined;
46
+ const thrownName = isErrorLike(thrown) ? thrown.name : undefined;
38
47
  if (thrownName !== expected.name)
39
48
  return false;
40
49
  }
@@ -44,7 +53,7 @@ function matchThrown(thrown, expected) {
44
53
  return false;
45
54
  }
46
55
  else {
47
- if (!expected.message.test(msg))
56
+ if (!regexTest(expected.message, msg))
48
57
  return false;
49
58
  }
50
59
  }
@@ -73,7 +82,8 @@ export function toThrow(received, expected, isNot = false) {
73
82
  result != null &&
74
83
  typeof result.then === "function") {
75
84
  // Suppress the unhandled rejection from the returned promise
76
- result.catch(() => { });
85
+ // Promise.resolve so plain thenables without .catch work too
86
+ Promise.resolve(result).catch(() => { });
77
87
  return {
78
88
  pass: false,
79
89
  message: () => "toThrow() received an async function. Use expect(fn).rejects.toThrow() for async errors.",
@@ -92,14 +102,33 @@ export function toThrow(received, expected, isNot = false) {
92
102
  if (expected === undefined) {
93
103
  return `Expected function ${expectation}, but it threw: ${thrownStr}`;
94
104
  }
95
- const expectedStr = typeof expected === "string"
96
- ? `"${expected}"`
97
- : expected instanceof RegExp
98
- ? expected.toString()
99
- : isErrorConstructor(expected)
100
- ? expected.name || "[AnonymousErrorConstructor]"
101
- : JSON.stringify(expected);
102
- return `Expected function ${expectation} ${expectedStr}, but it threw: ${thrownStr}`;
105
+ return `Expected function ${expectation} ${formatExpected(expected)}, but it threw: ${thrownStr}`;
106
+ },
107
+ };
108
+ }
109
+ /**
110
+ * toThrow under `.resolves` / `.rejects`. `received` is the settled value, not
111
+ * a function. Same as Jest: it only counts as thrown if it's an Error.
112
+ */
113
+ export function toThrowFromPromise(received, expected, isNot, promise) {
114
+ // Same as Jest: a non-Error function is called and treated like a sync
115
+ // toThrow, so `resolves.toThrow()` works on a promise of a function.
116
+ if (typeof received === "function" && !isErrorLike(received)) {
117
+ return toThrow(received, expected, isNot);
118
+ }
119
+ const isError = isErrorLike(received);
120
+ const matched = isError && matchThrown(received, expected);
121
+ const pass = isNot ? !matched : matched;
122
+ return {
123
+ pass,
124
+ message: () => {
125
+ const verb = promise === "rejects" ? "reject" : "resolve";
126
+ const past = promise === "rejects" ? "rejected" : "resolved";
127
+ const target = expected === undefined ? "an Error" : formatExpected(expected);
128
+ const got = isError
129
+ ? formatThrown(received)
130
+ : `a non-Error value: ${formatThrown(received)}`;
131
+ return `Expected promise ${isNot ? "not " : ""}to ${verb} with ${target}, but it ${past} with ${got}`;
103
132
  },
104
133
  };
105
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unrift/core",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Core engine for the Unrift TypeScript test framework - runner, matchers, CLI, and programmatic API",
5
5
  "private": false,
6
6
  "sideEffects": false,