bupkis 0.2.0 → 0.3.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.
- package/CHANGELOG.md +15 -0
- package/README.md +9 -9
- package/dist/commonjs/assertion/assertion-async.d.ts +2 -1
- package/dist/commonjs/assertion/assertion-async.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion-async.js +84 -2
- package/dist/commonjs/assertion/assertion-async.js.map +1 -1
- package/dist/commonjs/assertion/assertion-sync.d.ts +1 -1
- package/dist/commonjs/assertion/assertion-sync.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion-sync.js +5 -1
- package/dist/commonjs/assertion/assertion-sync.js.map +1 -1
- package/dist/commonjs/assertion/assertion-types.d.ts +6 -2
- package/dist/commonjs/assertion/assertion-types.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion.d.ts +1 -1
- package/dist/commonjs/assertion/assertion.d.ts.map +1 -1
- package/dist/commonjs/assertion/assertion.js +1 -14
- package/dist/commonjs/assertion/assertion.js.map +1 -1
- package/dist/commonjs/assertion/impl/async.d.ts +122 -21
- package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/async.js +112 -88
- package/dist/commonjs/assertion/impl/async.js.map +1 -1
- package/dist/commonjs/assertion/impl/callback.d.ts +104 -0
- package/dist/commonjs/assertion/impl/callback.d.ts.map +1 -0
- package/dist/commonjs/assertion/impl/callback.js +694 -0
- package/dist/commonjs/assertion/impl/callback.js.map +1 -0
- package/dist/commonjs/assertion/impl/index.d.ts +1 -1
- package/dist/commonjs/assertion/impl/index.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/index.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-esoteric.js +1 -1
- package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts +21 -18
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.js +31 -46
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync.d.ts +66 -19
- package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync.js +4 -1
- package/dist/commonjs/assertion/impl/sync.js.map +1 -1
- package/dist/commonjs/bootstrap.d.ts +145 -41
- package/dist/commonjs/bootstrap.d.ts.map +1 -1
- package/dist/commonjs/bootstrap.js +2 -3
- package/dist/commonjs/bootstrap.js.map +1 -1
- package/dist/commonjs/constant.js +7 -1
- package/dist/commonjs/constant.js.map +1 -1
- package/dist/commonjs/error.d.ts +22 -2
- package/dist/commonjs/error.d.ts.map +1 -1
- package/dist/commonjs/error.js +44 -4
- package/dist/commonjs/error.js.map +1 -1
- package/dist/commonjs/expect.d.ts.map +1 -1
- package/dist/commonjs/expect.js +1 -1
- package/dist/commonjs/expect.js.map +1 -1
- package/dist/commonjs/guards.d.ts +23 -5
- package/dist/commonjs/guards.d.ts.map +1 -1
- package/dist/commonjs/guards.js +26 -24
- package/dist/commonjs/guards.js.map +1 -1
- package/dist/commonjs/index.d.ts +144 -40
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/schema.d.ts +18 -8
- package/dist/commonjs/schema.d.ts.map +1 -1
- package/dist/commonjs/schema.js +21 -11
- package/dist/commonjs/schema.js.map +1 -1
- package/dist/commonjs/types.d.ts +39 -8
- package/dist/commonjs/types.d.ts.map +1 -1
- package/dist/commonjs/util.d.ts +66 -50
- package/dist/commonjs/util.d.ts.map +1 -1
- package/dist/commonjs/util.js +169 -156
- package/dist/commonjs/util.js.map +1 -1
- package/dist/commonjs/value-to-schema.d.ts +122 -0
- package/dist/commonjs/value-to-schema.d.ts.map +1 -0
- package/dist/commonjs/value-to-schema.js +309 -0
- package/dist/commonjs/value-to-schema.js.map +1 -0
- package/dist/esm/assertion/assertion-async.d.ts +2 -1
- package/dist/esm/assertion/assertion-async.d.ts.map +1 -1
- package/dist/esm/assertion/assertion-async.js +85 -3
- package/dist/esm/assertion/assertion-async.js.map +1 -1
- package/dist/esm/assertion/assertion-sync.d.ts +1 -1
- package/dist/esm/assertion/assertion-sync.d.ts.map +1 -1
- package/dist/esm/assertion/assertion-sync.js +6 -2
- package/dist/esm/assertion/assertion-sync.js.map +1 -1
- package/dist/esm/assertion/assertion-types.d.ts +6 -2
- package/dist/esm/assertion/assertion-types.d.ts.map +1 -1
- package/dist/esm/assertion/assertion.d.ts +1 -1
- package/dist/esm/assertion/assertion.d.ts.map +1 -1
- package/dist/esm/assertion/assertion.js +1 -14
- package/dist/esm/assertion/assertion.js.map +1 -1
- package/dist/esm/assertion/impl/async.d.ts +122 -21
- package/dist/esm/assertion/impl/async.d.ts.map +1 -1
- package/dist/esm/assertion/impl/async.js +111 -87
- package/dist/esm/assertion/impl/async.js.map +1 -1
- package/dist/esm/assertion/impl/callback.d.ts +104 -0
- package/dist/esm/assertion/impl/callback.d.ts.map +1 -0
- package/dist/esm/assertion/impl/callback.js +691 -0
- package/dist/esm/assertion/impl/callback.js.map +1 -0
- package/dist/esm/assertion/impl/index.d.ts +1 -1
- package/dist/esm/assertion/impl/index.d.ts.map +1 -1
- package/dist/esm/assertion/impl/index.js +1 -1
- package/dist/esm/assertion/impl/index.js.map +1 -1
- package/dist/esm/assertion/impl/sync-esoteric.js +2 -2
- package/dist/esm/assertion/impl/sync-esoteric.js.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.d.ts +21 -18
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.js +31 -46
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/esm/assertion/impl/sync.d.ts +66 -19
- package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync.js +3 -1
- package/dist/esm/assertion/impl/sync.js.map +1 -1
- package/dist/esm/bootstrap.d.ts +145 -41
- package/dist/esm/bootstrap.d.ts.map +1 -1
- package/dist/esm/bootstrap.js +1 -2
- package/dist/esm/bootstrap.js.map +1 -1
- package/dist/esm/constant.js +6 -0
- package/dist/esm/constant.js.map +1 -1
- package/dist/esm/error.d.ts +22 -2
- package/dist/esm/error.d.ts.map +1 -1
- package/dist/esm/error.js +43 -4
- package/dist/esm/error.js.map +1 -1
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/expect.js +2 -2
- package/dist/esm/expect.js.map +1 -1
- package/dist/esm/guards.d.ts +23 -5
- package/dist/esm/guards.d.ts.map +1 -1
- package/dist/esm/guards.js +22 -21
- package/dist/esm/guards.js.map +1 -1
- package/dist/esm/index.d.ts +144 -40
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/schema.d.ts +18 -8
- package/dist/esm/schema.d.ts.map +1 -1
- package/dist/esm/schema.js +21 -11
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/types.d.ts +39 -8
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/util.d.ts +66 -50
- package/dist/esm/util.d.ts.map +1 -1
- package/dist/esm/util.js +153 -154
- package/dist/esm/util.js.map +1 -1
- package/dist/esm/value-to-schema.d.ts +122 -0
- package/dist/esm/value-to-schema.d.ts.map +1 -0
- package/dist/esm/value-to-schema.js +305 -0
- package/dist/esm/value-to-schema.js.map +1 -0
- package/package.json +16 -13
- package/src/assertion/assertion-async.ts +113 -3
- package/src/assertion/assertion-sync.ts +5 -2
- package/src/assertion/assertion-types.ts +14 -4
- package/src/assertion/assertion.ts +2 -17
- package/src/assertion/impl/async.ts +130 -90
- package/src/assertion/impl/callback.ts +882 -0
- package/src/assertion/impl/index.ts +1 -1
- package/src/assertion/impl/sync-esoteric.ts +2 -2
- package/src/assertion/impl/sync-parametric.ts +40 -48
- package/src/assertion/impl/sync.ts +3 -0
- package/src/bootstrap.ts +1 -2
- package/src/constant.ts +8 -0
- package/src/error.ts +57 -3
- package/src/expect.ts +6 -2
- package/src/guards.ts +45 -18
- package/src/index.ts +1 -0
- package/src/schema.ts +22 -11
- package/src/types.ts +40 -8
- package/src/util.ts +168 -223
- package/src/value-to-schema.ts +464 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { AsyncAssertions } from './async.js';
|
|
2
|
-
export { SyncAssertions } from './sync.js';
|
|
2
|
+
export { SyncAssertions as SyncAssertions } from './sync.js';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { DictionarySchema, PropertyKeySchema } from '../../schema.js';
|
|
4
4
|
import { createAssertion } from '../create.js';
|
|
5
5
|
|
|
6
6
|
export const EsotericAssertions = [
|
|
7
|
-
createAssertion(['to have a null prototype'],
|
|
7
|
+
createAssertion(['to have a null prototype'], DictionarySchema),
|
|
8
8
|
createAssertion(
|
|
9
9
|
[PropertyKeySchema, 'to be an enumerable property of', z.looseObject({})],
|
|
10
10
|
(subject, obj) =>
|
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
StrongSetSchema,
|
|
12
12
|
WrappedPromiseLikeSchema,
|
|
13
13
|
} from '../../schema.js';
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
valueToSchema,
|
|
16
|
+
valueToSchemaOptionsForDeepEqual,
|
|
17
|
+
valueToSchemaOptionsForSatisfies,
|
|
18
|
+
} from '../../value-to-schema.js';
|
|
15
19
|
import { createAssertion } from '../create.js';
|
|
16
20
|
|
|
17
21
|
const trapError = (fn: () => unknown): unknown => {
|
|
@@ -304,19 +308,19 @@ export const ParametricAssertions = [
|
|
|
304
308
|
}
|
|
305
309
|
},
|
|
306
310
|
),
|
|
307
|
-
// @ts-expect-error fix later
|
|
308
311
|
createAssertion(
|
|
309
312
|
[
|
|
310
313
|
z.looseObject({}),
|
|
311
314
|
['to deep equal', 'to deeply equal'],
|
|
312
315
|
z.looseObject({}),
|
|
313
316
|
],
|
|
314
|
-
(_, expected) => valueToSchema(expected,
|
|
317
|
+
(_, expected) => valueToSchema(expected, valueToSchemaOptionsForDeepEqual),
|
|
315
318
|
),
|
|
316
|
-
// @ts-expect-error fix later
|
|
317
319
|
createAssertion(
|
|
318
320
|
[ArrayLikeSchema, ['to deep equal', 'to deeply equal'], ArrayLikeSchema],
|
|
319
|
-
(_, expected) =>
|
|
321
|
+
(_, expected) => {
|
|
322
|
+
return valueToSchema(expected, valueToSchemaOptionsForDeepEqual);
|
|
323
|
+
},
|
|
320
324
|
),
|
|
321
325
|
createAssertion([FunctionSchema, 'to throw'], (subject) => {
|
|
322
326
|
const error = trapError(subject);
|
|
@@ -376,10 +380,7 @@ export const ParametricAssertions = [
|
|
|
376
380
|
.or(z.coerce.string().regex(param))
|
|
377
381
|
.safeParse(error).success;
|
|
378
382
|
} else if (isNonNullObject(param)) {
|
|
379
|
-
const schema = valueToSchema(param,
|
|
380
|
-
literalPrimitives: true,
|
|
381
|
-
strict: true,
|
|
382
|
-
});
|
|
383
|
+
const schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
|
|
383
384
|
return schema.safeParse(error).success;
|
|
384
385
|
} else {
|
|
385
386
|
throw new TypeError(`Invalid parameter schema: ${inspect(param)}`);
|
|
@@ -396,6 +397,7 @@ export const ParametricAssertions = [
|
|
|
396
397
|
],
|
|
397
398
|
(subject, ctor, param) => {
|
|
398
399
|
const error = trapError(subject);
|
|
400
|
+
|
|
399
401
|
if (!isA(error, ctor)) {
|
|
400
402
|
return {
|
|
401
403
|
actual: error,
|
|
@@ -405,47 +407,32 @@ export const ParametricAssertions = [
|
|
|
405
407
|
: `Expected function to throw an instance of ${ctor.name}, but it threw a non-object value: ${error as unknown}`,
|
|
406
408
|
};
|
|
407
409
|
}
|
|
408
|
-
|
|
410
|
+
let schema: undefined | z.ZodType;
|
|
411
|
+
// TODO: can valueToSchema handle the first two conditional branches?
|
|
409
412
|
if (isString(param)) {
|
|
410
|
-
|
|
413
|
+
schema = z
|
|
411
414
|
.looseObject({
|
|
412
|
-
message: z.coerce.string().
|
|
415
|
+
message: z.coerce.string().pipe(z.literal(param)),
|
|
413
416
|
})
|
|
414
|
-
.or(z.coerce.string().
|
|
415
|
-
.safeParse(error);
|
|
416
|
-
if (!result.success) {
|
|
417
|
-
return {
|
|
418
|
-
actual: isError(error) ? error.message : String(error),
|
|
419
|
-
expected: `error with message containing "${param}"`,
|
|
420
|
-
message: `Expected error message to contain "${param}", but got: ${isError(error) ? error.message : String(error)}`,
|
|
421
|
-
};
|
|
422
|
-
}
|
|
417
|
+
.or(z.coerce.string().pipe(z.literal(param)));
|
|
423
418
|
} else if (isA(param, RegExp)) {
|
|
424
|
-
|
|
419
|
+
schema = z
|
|
425
420
|
.looseObject({
|
|
426
421
|
message: z.coerce.string().regex(param),
|
|
427
422
|
})
|
|
428
|
-
.or(z.coerce.string().regex(param))
|
|
429
|
-
.safeParse(error);
|
|
430
|
-
if (!result.success) {
|
|
431
|
-
return {
|
|
432
|
-
actual: isError(error) ? error.message : String(error),
|
|
433
|
-
expected: `error with message matching ${param}`,
|
|
434
|
-
message: `Expected error message to match ${param}, but got: ${isError(error) ? error.message : String(error)}`,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
423
|
+
.or(z.coerce.string().regex(param));
|
|
437
424
|
} else if (isNonNullObject(param)) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
425
|
+
schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
|
|
426
|
+
}
|
|
427
|
+
if (!schema) {
|
|
428
|
+
throw new TypeError(
|
|
429
|
+
`Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const result = schema.safeParse(error);
|
|
434
|
+
if (!result.success) {
|
|
435
|
+
return result.error;
|
|
449
436
|
}
|
|
450
437
|
},
|
|
451
438
|
),
|
|
@@ -455,7 +442,15 @@ export const ParametricAssertions = [
|
|
|
455
442
|
['includes', 'contains', 'to include', 'to contain'],
|
|
456
443
|
z.string(),
|
|
457
444
|
],
|
|
458
|
-
(subject, expected) =>
|
|
445
|
+
(subject, expected) => {
|
|
446
|
+
if (!subject.includes(expected)) {
|
|
447
|
+
return {
|
|
448
|
+
actual: subject,
|
|
449
|
+
expected: `string including "${expected}"`,
|
|
450
|
+
message: `Expected "${subject}" to include "${expected}"`,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
},
|
|
459
454
|
),
|
|
460
455
|
|
|
461
456
|
createAssertion([z.string(), 'to match', RegExpSchema], (subject, regex) =>
|
|
@@ -467,14 +462,11 @@ export const ParametricAssertions = [
|
|
|
467
462
|
['to satisfy', 'to be like'],
|
|
468
463
|
z.looseObject({}),
|
|
469
464
|
],
|
|
470
|
-
(
|
|
471
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
472
|
-
valueToSchema(shape) as unknown as z.ZodType<{}, z.core.$loose>,
|
|
465
|
+
(_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
|
|
473
466
|
),
|
|
474
467
|
createAssertion(
|
|
475
468
|
[ArrayLikeSchema, ['to satisfy', 'to be like'], ArrayLikeSchema],
|
|
476
|
-
(
|
|
477
|
-
valueToSchema(shape) as unknown as typeof ArrayLikeSchema,
|
|
469
|
+
(_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
|
|
478
470
|
),
|
|
479
471
|
createAssertion(
|
|
480
472
|
[FunctionSchema, 'to have arity', z.number().int().nonnegative()],
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @packageDocumentation
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { CallbackSyncAssertions } from './callback.js';
|
|
12
13
|
import { BasicAssertions } from './sync-basic.js';
|
|
13
14
|
import { CollectionAssertions } from './sync-collection.js';
|
|
14
15
|
import { EsotericAssertions } from './sync-esoteric.js';
|
|
@@ -19,10 +20,12 @@ export const SyncAssertions = [
|
|
|
19
20
|
...BasicAssertions,
|
|
20
21
|
...EsotericAssertions,
|
|
21
22
|
...ParametricAssertions,
|
|
23
|
+
...CallbackSyncAssertions,
|
|
22
24
|
] as const;
|
|
23
25
|
|
|
24
26
|
export {
|
|
25
27
|
BasicAssertions,
|
|
28
|
+
CallbackSyncAssertions,
|
|
26
29
|
CollectionAssertions,
|
|
27
30
|
EsotericAssertions,
|
|
28
31
|
ParametricAssertions,
|
package/src/bootstrap.ts
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* @packageDocumentation
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { AsyncAssertions } from './assertion/
|
|
11
|
-
import { SyncAssertions } from './assertion/impl/sync.js';
|
|
10
|
+
import { AsyncAssertions, SyncAssertions } from './assertion/index.js';
|
|
12
11
|
import { type Expect, type ExpectAsync } from './types.js';
|
|
13
12
|
import { createUse } from './use.js';
|
|
14
13
|
|
package/src/constant.ts
CHANGED
|
@@ -26,6 +26,14 @@ export const kStringLiteral: unique symbol = Symbol('bupkis:string-literal');
|
|
|
26
26
|
|
|
27
27
|
export const kBupkisAssertionError: unique symbol = Symbol('bupkis-error');
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Symbol used to flag a `FailAssertionError`
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export const kBupkisFailAssertionError: unique symbol =
|
|
35
|
+
Symbol('bupkis-fail-error');
|
|
36
|
+
|
|
29
37
|
/**
|
|
30
38
|
* Symbol used to flag a `NegatedAssertionError`
|
|
31
39
|
*
|
package/src/error.ts
CHANGED
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { AssertionError as NodeAssertionError } from 'node:assert';
|
|
10
|
+
import { z } from 'zod/v4';
|
|
10
11
|
|
|
11
12
|
import {
|
|
12
13
|
kBupkisAssertionError,
|
|
14
|
+
kBupkisFailAssertionError,
|
|
13
15
|
kBupkisNegatedAssertionError,
|
|
14
16
|
} from './constant.js';
|
|
15
17
|
import { isA } from './guards.js';
|
|
18
|
+
import { type AssertionParts, type ParsedValues } from './types.js';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* _BUPKIS_' s custom `AssertionError` class, which is just a thin wrapper
|
|
@@ -27,10 +30,39 @@ export class AssertionError extends NodeAssertionError {
|
|
|
27
30
|
override name = 'AssertionError';
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
|
-
* @
|
|
33
|
+
* Translates a {@link z.ZodError} into an {@link AssertionError} with a
|
|
34
|
+
* human-friendly message.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* This does not handle parameterized assertions with more than one parameter
|
|
38
|
+
* too cleanly; it's unclear how a test runner would display the expected
|
|
39
|
+
* values. This will probably need a fix in the future.
|
|
40
|
+
* @param stackStartFn The function to start the stack trace from
|
|
41
|
+
* @param zodError The original `ZodError`
|
|
42
|
+
* @param values Values which caused the error
|
|
43
|
+
* @returns New `AssertionError`
|
|
31
44
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
static fromZodError<Parts extends AssertionParts>(
|
|
46
|
+
zodError: z.ZodError,
|
|
47
|
+
stackStartFn: (...args: any[]) => any,
|
|
48
|
+
values: ParsedValues<Parts>,
|
|
49
|
+
): AssertionError {
|
|
50
|
+
const flat = z.flattenError(zodError);
|
|
51
|
+
|
|
52
|
+
let pretty = flat.formErrors.join('; ');
|
|
53
|
+
for (const [keypath, errors] of Object.entries(flat.fieldErrors)) {
|
|
54
|
+
pretty += `; ${keypath}: ${(errors as unknown[]).join('; ')}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const [actual, ...expected] = values as unknown as [unknown, ...unknown[]];
|
|
58
|
+
|
|
59
|
+
return new AssertionError({
|
|
60
|
+
actual,
|
|
61
|
+
expected: expected.length === 1 ? expected[0] : expected,
|
|
62
|
+
message: `Assertion ${this} failed: ${pretty}`,
|
|
63
|
+
operator: `${this}`,
|
|
64
|
+
stackStartFn,
|
|
65
|
+
});
|
|
34
66
|
}
|
|
35
67
|
|
|
36
68
|
/**
|
|
@@ -47,6 +79,26 @@ export class AssertionError extends NodeAssertionError {
|
|
|
47
79
|
}
|
|
48
80
|
}
|
|
49
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Variant of an {@link AssertionError} that is thrown when
|
|
84
|
+
* {@link bupkis!expect.fail} is called.
|
|
85
|
+
*/
|
|
86
|
+
export class FailAssertionError extends AssertionError {
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
[kBupkisFailAssertionError] = true;
|
|
91
|
+
|
|
92
|
+
override name = 'FailAssertionError';
|
|
93
|
+
|
|
94
|
+
static isFailAssertionError(err: unknown): err is FailAssertionError {
|
|
95
|
+
return (
|
|
96
|
+
isA(err, FailAssertionError) &&
|
|
97
|
+
Object.hasOwn(err, kBupkisFailAssertionError)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
50
102
|
/**
|
|
51
103
|
* Error type used internally to catch failed negated assertions.
|
|
52
104
|
*
|
|
@@ -55,6 +107,8 @@ export class AssertionError extends NodeAssertionError {
|
|
|
55
107
|
export class NegatedAssertionError extends AssertionError {
|
|
56
108
|
[kBupkisNegatedAssertionError] = true;
|
|
57
109
|
|
|
110
|
+
override name = 'NegatedAssertionError';
|
|
111
|
+
|
|
58
112
|
static isNegatedAssertionError(err: unknown): err is NegatedAssertionError {
|
|
59
113
|
return (
|
|
60
114
|
isA(err, AssertionError) &&
|
package/src/expect.ts
CHANGED
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
type ParsedValues,
|
|
17
17
|
} from './assertion/assertion-types.js';
|
|
18
18
|
import { createAssertion, createAsyncAssertion } from './assertion/create.js';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AssertionError,
|
|
21
|
+
FailAssertionError,
|
|
22
|
+
NegatedAssertionError,
|
|
23
|
+
} from './error.js';
|
|
20
24
|
import { isAssertionFailure, isString } from './guards.js';
|
|
21
25
|
import {
|
|
22
26
|
type Expect,
|
|
@@ -578,7 +582,7 @@ const detectNegation = (
|
|
|
578
582
|
};
|
|
579
583
|
|
|
580
584
|
const fail: FailFn = (reason?: string): never => {
|
|
581
|
-
throw new
|
|
585
|
+
throw new FailAssertionError({ message: reason });
|
|
582
586
|
};
|
|
583
587
|
|
|
584
588
|
/**
|
package/src/guards.ts
CHANGED
|
@@ -24,7 +24,7 @@ import type {
|
|
|
24
24
|
AssertionPart,
|
|
25
25
|
PhraseLiteralChoice,
|
|
26
26
|
} from './assertion/assertion-types.js';
|
|
27
|
-
import type { Constructor } from './types.js';
|
|
27
|
+
import type { Constructor, ZodTypeMap } from './types.js';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Returns `true` if the given value looks like a Zod v4 schema, determined by
|
|
@@ -33,28 +33,60 @@ import type { Constructor } from './types.js';
|
|
|
33
33
|
* Note: This relies on Zod's internal shape and is intended for runtime
|
|
34
34
|
* discrimination within this library.
|
|
35
35
|
*
|
|
36
|
-
* @template T
|
|
36
|
+
* @template T - The specific ZodType to check for (based on def.type)
|
|
37
37
|
* @param value - Value to test
|
|
38
38
|
* @returns Whether the value is `ZodType`-like
|
|
39
39
|
*/
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
export function isZodType<T extends keyof ZodTypeMap>(
|
|
41
|
+
value: unknown,
|
|
42
|
+
type: T,
|
|
43
|
+
): value is ZodTypeMap[T];
|
|
44
|
+
/**
|
|
45
|
+
* Returns `true` if the given value looks like a Zod v4 schema, determined by
|
|
46
|
+
* the presence of an internal {@link z.core.$ZodTypeDef} field.
|
|
47
|
+
*
|
|
48
|
+
* Note: This relies on Zod's internal shape and is intended for runtime
|
|
49
|
+
* discrimination within this library.
|
|
50
|
+
*
|
|
51
|
+
* @param value - Value to test
|
|
52
|
+
* @returns Whether the value is `ZodType`-like
|
|
53
|
+
*/
|
|
54
|
+
export function isZodType(value: unknown): value is z.ZodType;
|
|
55
|
+
export function isZodType<T extends keyof ZodTypeMap>(
|
|
56
|
+
value: unknown,
|
|
57
|
+
type?: T,
|
|
58
|
+
): value is T extends keyof ZodTypeMap ? ZodTypeMap[T] : z.ZodType {
|
|
59
|
+
const isValid =
|
|
60
|
+
isObject(value) &&
|
|
44
61
|
'def' in value &&
|
|
45
|
-
value.def &&
|
|
62
|
+
!!value.def &&
|
|
46
63
|
typeof value.def === 'object' &&
|
|
47
|
-
'type' in value.def
|
|
48
|
-
|
|
64
|
+
'type' in value.def;
|
|
65
|
+
|
|
66
|
+
if (!isValid) return false;
|
|
67
|
+
if (type === undefined) return true;
|
|
68
|
+
|
|
69
|
+
return (value as z.ZodType).def.type === type;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Type guard for a plain object.
|
|
74
|
+
*
|
|
75
|
+
* @param value Value to test
|
|
76
|
+
* @returns `true` if the value is a plain object, `false` otherwise
|
|
77
|
+
*/
|
|
78
|
+
export const isObject = (value: unknown): value is NonNullable<object> => {
|
|
79
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
80
|
+
};
|
|
49
81
|
|
|
50
82
|
/**
|
|
51
|
-
* Returns true if the given value is a {@link z.ZodPromise} schema.
|
|
83
|
+
* Returns `true` if the given value is a {@link z.ZodPromise} schema.
|
|
52
84
|
*
|
|
53
85
|
* @param value - Value to test
|
|
54
86
|
* @returns `true` if the value is a `ZodPromise` schema; `false` otherwise
|
|
55
87
|
*/
|
|
56
88
|
export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
57
|
-
isZodType(value
|
|
89
|
+
isZodType(value, 'promise');
|
|
58
90
|
|
|
59
91
|
/**
|
|
60
92
|
* Checks if a value is "promise-like", meaning it is a "thenable" object.
|
|
@@ -63,12 +95,7 @@ export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
|
63
95
|
* @returns `true` if the value is promise-like, `false` otherwise
|
|
64
96
|
*/
|
|
65
97
|
export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
|
|
66
|
-
|
|
67
|
-
value &&
|
|
68
|
-
typeof value === 'object' &&
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
70
|
-
typeof (value as any).then === 'function'
|
|
71
|
-
);
|
|
98
|
+
isObject(value) && 'then' in value && isFunction(value.then);
|
|
72
99
|
|
|
73
100
|
/**
|
|
74
101
|
* Returns `true` if the given value is a constructable function (i.e., a
|
|
@@ -85,7 +112,7 @@ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
|
|
|
85
112
|
* @param fn - Function to test
|
|
86
113
|
* @returns Whether the function is constructable
|
|
87
114
|
*/
|
|
88
|
-
export const
|
|
115
|
+
export const isConstructible = (fn: unknown): fn is Constructor => {
|
|
89
116
|
if (fn === Symbol || fn === BigInt) {
|
|
90
117
|
return false;
|
|
91
118
|
}
|
package/src/index.ts
CHANGED
package/src/schema.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* This module provides reusable Zod schemas for validating constructors,
|
|
5
5
|
* functions, property keys, promises, and other common JavaScript types used
|
|
6
|
-
* throughout the assertion system. These tend to work around the
|
|
6
|
+
* throughout the assertion system. These tend to work around the impedance
|
|
7
7
|
* mismatch between **BUPKIS** and Zod.
|
|
8
8
|
*
|
|
9
9
|
* These are used internally, but consumers may also find them useful.
|
|
@@ -36,7 +36,7 @@ import { z } from 'zod/v4';
|
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
38
|
isA,
|
|
39
|
-
|
|
39
|
+
isConstructible,
|
|
40
40
|
isFunction,
|
|
41
41
|
isNonNullObject,
|
|
42
42
|
isPromiseLike,
|
|
@@ -49,7 +49,7 @@ import { type Constructor, type MutableOrReadonly } from './types.js';
|
|
|
49
49
|
*
|
|
50
50
|
* This schema validates values that can be used as constructors, including ES6
|
|
51
51
|
* classes, traditional constructor functions, and built-in constructors. It
|
|
52
|
-
* uses the {@link
|
|
52
|
+
* uses the {@link isConstructible} guard function to determine if a value can be
|
|
53
53
|
* invoked with the `new` operator to create object instances.
|
|
54
54
|
*
|
|
55
55
|
* @privateRemarks
|
|
@@ -73,7 +73,7 @@ import { type Constructor, type MutableOrReadonly } from './types.js';
|
|
|
73
73
|
*/
|
|
74
74
|
|
|
75
75
|
export const ClassSchema = z
|
|
76
|
-
.custom<Constructor>(
|
|
76
|
+
.custom<Constructor>(isConstructible)
|
|
77
77
|
.register(BupkisRegistry, { name: 'ClassSchema' })
|
|
78
78
|
.describe('Class / Constructor');
|
|
79
79
|
|
|
@@ -242,9 +242,12 @@ export const StrongSetSchema = z
|
|
|
242
242
|
* `Object.prototype`, making them useful as pure data containers or
|
|
243
243
|
* dictionaries.
|
|
244
244
|
*
|
|
245
|
-
* @
|
|
245
|
+
* @privateRemarks
|
|
246
246
|
* The schema is registered in the `BupkisRegistry` with the name
|
|
247
247
|
* `ObjectWithNullPrototype` for later reference and type checking purposes.
|
|
248
|
+
*
|
|
249
|
+
* Changing this to be a `ZodRecord` would be nice, but that would end up
|
|
250
|
+
* blasting away the original object's prototype.
|
|
248
251
|
* @example
|
|
249
252
|
*
|
|
250
253
|
* ```typescript
|
|
@@ -260,14 +263,22 @@ export const StrongSetSchema = z
|
|
|
260
263
|
* ```
|
|
261
264
|
*
|
|
262
265
|
* @group Schema
|
|
266
|
+
* @see Aliases: {@link NullProtoObjectSchema}, {@link DictionarySchema}
|
|
263
267
|
*/
|
|
264
|
-
export const
|
|
268
|
+
export const DictionarySchema = z
|
|
265
269
|
.custom<Record<PropertyKey, unknown>>(
|
|
266
270
|
(value) => isNonNullObject(value) && Object.getPrototypeOf(value) === null,
|
|
267
271
|
)
|
|
268
272
|
.describe('Object with null prototype')
|
|
269
273
|
.register(BupkisRegistry, { name: 'ObjectWithNullPrototype' });
|
|
270
274
|
|
|
275
|
+
/**
|
|
276
|
+
* {@inheritDoc DictionarySchema}
|
|
277
|
+
*
|
|
278
|
+
* @group Schema
|
|
279
|
+
*/
|
|
280
|
+
export const NullProtoObjectSchema = DictionarySchema;
|
|
281
|
+
|
|
271
282
|
/**
|
|
272
283
|
* A Zod schema that validates functions declared with the `async` keyword.
|
|
273
284
|
*
|
|
@@ -276,7 +287,7 @@ export const NullProtoObjectSchema = z
|
|
|
276
287
|
* function's internal `[[ToString]]` representation to distinguish async
|
|
277
288
|
* functions from regular functions that might return Promises.
|
|
278
289
|
*
|
|
279
|
-
* @
|
|
290
|
+
* @privateRemarks
|
|
280
291
|
* The schema is registered in the `BupkisRegistry` with the name
|
|
281
292
|
* `AsyncFunctionSchema` for later reference and type checking purposes. This
|
|
282
293
|
* schema cannot reliably detect functions that return Promises but are not
|
|
@@ -318,7 +329,7 @@ export const AsyncFunctionSchema = FunctionSchema.refine(
|
|
|
318
329
|
* if it converts to `true` when evaluated in a boolean context - essentially
|
|
319
330
|
* any value that is not one of the eight falsy values.
|
|
320
331
|
*
|
|
321
|
-
* @
|
|
332
|
+
* @privateRemarks
|
|
322
333
|
* The schema is registered in the `BupkisRegistry` with the name `Truthy` and
|
|
323
334
|
* indicates that it accepts anything as valid input for evaluation.
|
|
324
335
|
* @example
|
|
@@ -354,7 +365,7 @@ export const TruthySchema = z
|
|
|
354
365
|
* in JavaScript are: `false`, `0`, `-0`, `0n`, `""` (empty string), `null`,
|
|
355
366
|
* `undefined`, and `NaN`.
|
|
356
367
|
*
|
|
357
|
-
* @
|
|
368
|
+
* @privateRemarks
|
|
358
369
|
* The schema is registered in the `BupkisRegistry` with the name `Falsy` and
|
|
359
370
|
* indicates that it accepts anything as valid input for evaluation.
|
|
360
371
|
* @example
|
|
@@ -392,7 +403,7 @@ export const FalsySchema = z
|
|
|
392
403
|
* distinguishing them from objects and functions which are non-primitive
|
|
393
404
|
* reference types.
|
|
394
405
|
*
|
|
395
|
-
* @
|
|
406
|
+
* @privateRemarks
|
|
396
407
|
* The schema is registered in the `BupkisRegistry` with the name `Primitive`
|
|
397
408
|
* and indicates that it accepts primitive values as valid input.
|
|
398
409
|
* @example
|
|
@@ -474,7 +485,7 @@ export const ArrayLikeSchema = z
|
|
|
474
485
|
* It ensures the validated value is a proper regular expression object with all
|
|
475
486
|
* associated methods and properties.
|
|
476
487
|
*
|
|
477
|
-
* @
|
|
488
|
+
* @privateRemarks
|
|
478
489
|
* The schema is registered in the `BupkisRegistry` with the name `RegExp` for
|
|
479
490
|
* later reference and type checking purposes.
|
|
480
491
|
* @example
|
package/src/types.ts
CHANGED
|
@@ -269,14 +269,7 @@ export interface CreateAsyncAssertionFn {
|
|
|
269
269
|
parts: Parts,
|
|
270
270
|
impl: Impl,
|
|
271
271
|
): AssertionFunctionAsync<Parts, Impl, Slots>;
|
|
272
|
-
}
|
|
273
|
-
* @template BaseSyncAssertions Base set of synchronous
|
|
274
|
-
* {@link Assertion | Assertions}; will be the builtin sync assertions, at
|
|
275
|
-
* minimum)
|
|
276
|
-
* @template BaseAsyncAssertions Base set of asynchronous
|
|
277
|
-
* {@link Assertion | Assertions}; will be the builtin async assertions, at
|
|
278
|
-
* minimum)
|
|
279
|
-
*/
|
|
272
|
+
}
|
|
280
273
|
|
|
281
274
|
/**
|
|
282
275
|
* The main synchronous assertion function.
|
|
@@ -684,3 +677,42 @@ export interface UseFn<
|
|
|
684
677
|
ExtendedAsyncAssertions
|
|
685
678
|
>;
|
|
686
679
|
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Maps Zod `def.type` strings to their corresponding ZodType classes.
|
|
683
|
+
*
|
|
684
|
+
* This allows for type-safe discrimination of ZodTypes based on their internal
|
|
685
|
+
* `def.type` property in Zod v4.
|
|
686
|
+
*/
|
|
687
|
+
export interface ZodTypeMap {
|
|
688
|
+
any: z.ZodAny;
|
|
689
|
+
array: z.ZodArray<any>;
|
|
690
|
+
bigint: z.ZodBigInt;
|
|
691
|
+
boolean: z.ZodBoolean;
|
|
692
|
+
catch: z.ZodCatch<any>;
|
|
693
|
+
date: z.ZodDate;
|
|
694
|
+
default: z.ZodDefault<any>;
|
|
695
|
+
enum: z.ZodEnum<any>;
|
|
696
|
+
function: z.ZodFunction<any, any>;
|
|
697
|
+
lazy: z.ZodLazy<any>;
|
|
698
|
+
literal: z.ZodLiteral<any>;
|
|
699
|
+
map: z.ZodMap<any, any>;
|
|
700
|
+
never: z.ZodNever;
|
|
701
|
+
null: z.ZodNull;
|
|
702
|
+
nullable: z.ZodNullable<any>;
|
|
703
|
+
number: z.ZodNumber;
|
|
704
|
+
object: z.ZodObject<any>;
|
|
705
|
+
optional: z.ZodOptional<any>;
|
|
706
|
+
pipe: z.ZodPipe<any, any>;
|
|
707
|
+
promise: z.ZodPromise<any>;
|
|
708
|
+
readonly: z.ZodReadonly<any>;
|
|
709
|
+
record: z.ZodRecord<any, any>;
|
|
710
|
+
set: z.ZodSet<any>;
|
|
711
|
+
string: z.ZodString;
|
|
712
|
+
symbol: z.ZodSymbol;
|
|
713
|
+
tuple: z.ZodTuple<any>;
|
|
714
|
+
undefined: z.ZodUndefined;
|
|
715
|
+
union: z.ZodUnion<any>;
|
|
716
|
+
unknown: z.ZodUnknown;
|
|
717
|
+
void: z.ZodVoid;
|
|
718
|
+
}
|