bupkis 0.2.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.
- package/CHANGELOG.md +27 -0
- package/README.md +35 -11
- 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 +118 -90
- 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-basic.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-basic.js +1 -1
- package/dist/commonjs/assertion/impl/sync-basic.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-collection.d.ts +1 -1
- package/dist/commonjs/assertion/impl/sync-collection.js +3 -3
- package/dist/commonjs/assertion/impl/sync-collection.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 +22 -28
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.js +35 -50
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync.d.ts +68 -30
- 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 +147 -52
- 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.d.ts +1 -1
- package/dist/commonjs/constant.d.ts.map +1 -1
- package/dist/commonjs/constant.js +8 -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 +96 -5
- package/dist/commonjs/guards.d.ts.map +1 -1
- package/dist/commonjs/guards.js +104 -25
- package/dist/commonjs/guards.js.map +1 -1
- package/dist/commonjs/index.d.ts +146 -51
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/schema.d.ts +84 -18
- package/dist/commonjs/schema.d.ts.map +1 -1
- package/dist/commonjs/schema.js +107 -22
- package/dist/commonjs/schema.js.map +1 -1
- package/dist/commonjs/types.d.ts +171 -9
- package/dist/commonjs/types.d.ts.map +1 -1
- package/dist/commonjs/use.d.ts.map +1 -1
- package/dist/commonjs/use.js +15 -1
- package/dist/commonjs/use.js.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 +329 -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 +118 -90
- 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-basic.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-basic.js +2 -2
- package/dist/esm/assertion/impl/sync-basic.js.map +1 -1
- package/dist/esm/assertion/impl/sync-collection.d.ts +1 -1
- package/dist/esm/assertion/impl/sync-collection.js +3 -3
- package/dist/esm/assertion/impl/sync-collection.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 +22 -28
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.js +36 -51
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/esm/assertion/impl/sync.d.ts +68 -30
- 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 +147 -52
- 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.d.ts +1 -1
- package/dist/esm/constant.d.ts.map +1 -1
- package/dist/esm/constant.js +7 -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 +96 -5
- package/dist/esm/guards.d.ts.map +1 -1
- package/dist/esm/guards.js +98 -21
- package/dist/esm/guards.js.map +1 -1
- package/dist/esm/index.d.ts +146 -51
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/schema.d.ts +84 -18
- package/dist/esm/schema.d.ts.map +1 -1
- package/dist/esm/schema.js +107 -22
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/types.d.ts +171 -9
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/use.d.ts.map +1 -1
- package/dist/esm/use.js +15 -1
- package/dist/esm/use.js.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 +325 -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 +137 -93
- package/src/assertion/impl/callback.ts +882 -0
- package/src/assertion/impl/index.ts +1 -1
- package/src/assertion/impl/sync-basic.ts +5 -2
- package/src/assertion/impl/sync-collection.ts +3 -3
- package/src/assertion/impl/sync-esoteric.ts +2 -2
- package/src/assertion/impl/sync-parametric.ts +47 -54
- package/src/assertion/impl/sync.ts +3 -0
- package/src/bootstrap.ts +1 -2
- package/src/constant.ts +10 -0
- package/src/error.ts +57 -3
- package/src/expect.ts +6 -2
- package/src/guards.ts +125 -18
- package/src/index.ts +3 -0
- package/src/schema.ts +121 -23
- package/src/types.ts +205 -10
- package/src/use.ts +22 -0
- package/src/util.ts +168 -223
- package/src/value-to-schema.ts +489 -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';
|
|
@@ -3,7 +3,7 @@ import { z } from 'zod/v4';
|
|
|
3
3
|
import { BupkisRegistry } from '../../metadata.js';
|
|
4
4
|
import {
|
|
5
5
|
AsyncFunctionSchema,
|
|
6
|
-
|
|
6
|
+
ConstructibleSchema,
|
|
7
7
|
FalsySchema,
|
|
8
8
|
FunctionSchema,
|
|
9
9
|
PrimitiveSchema,
|
|
@@ -64,7 +64,10 @@ export const BasicAssertions = [
|
|
|
64
64
|
createAssertion(['to be undefined'], z.undefined()),
|
|
65
65
|
createAssertion([['to be an array', 'to be array']], z.array(z.any())),
|
|
66
66
|
createAssertion([['to be a date', 'to be a Date']], z.date()),
|
|
67
|
-
createAssertion(
|
|
67
|
+
createAssertion(
|
|
68
|
+
[['to be a class', 'to be a constructor']],
|
|
69
|
+
ConstructibleSchema,
|
|
70
|
+
),
|
|
68
71
|
createAssertion(['to be a primitive'], PrimitiveSchema),
|
|
69
72
|
|
|
70
73
|
createAssertion(
|
|
@@ -5,9 +5,9 @@ import { StrongMapSchema, StrongSetSchema } from '../../schema.js';
|
|
|
5
5
|
import { createAssertion } from '../create.js';
|
|
6
6
|
|
|
7
7
|
export const CollectionAssertions = [
|
|
8
|
-
// Map assertions (including WeakMap)
|
|
8
|
+
// Map assertions (including WeakMap) - use StrongMapSchema instead of z.map for better type inference
|
|
9
9
|
createAssertion(
|
|
10
|
-
[
|
|
10
|
+
[StrongMapSchema, ['to contain', 'to include'], z.any()],
|
|
11
11
|
(subject, key) => subject.has(key),
|
|
12
12
|
),
|
|
13
13
|
// Size-based assertions only for strong Maps (not WeakMaps)
|
|
@@ -21,7 +21,7 @@ export const CollectionAssertions = [
|
|
|
21
21
|
),
|
|
22
22
|
// Set assertions (including WeakSet)
|
|
23
23
|
createAssertion(
|
|
24
|
-
[
|
|
24
|
+
[StrongSetSchema, ['to contain', 'to include'], z.any()],
|
|
25
25
|
(subject, value) => subject.has(value),
|
|
26
26
|
),
|
|
27
27
|
// Size-based assertions only for strong Sets (not WeakSets)
|
|
@@ -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) =>
|
|
@@ -4,14 +4,19 @@ import { z } from 'zod/v4';
|
|
|
4
4
|
import { isA, isError, isNonNullObject, isString } from '../../guards.js';
|
|
5
5
|
import {
|
|
6
6
|
ArrayLikeSchema,
|
|
7
|
-
|
|
7
|
+
ConstructibleSchema,
|
|
8
8
|
FunctionSchema,
|
|
9
9
|
RegExpSchema,
|
|
10
|
+
SatisfyPatternSchema,
|
|
10
11
|
StrongMapSchema,
|
|
11
12
|
StrongSetSchema,
|
|
12
13
|
WrappedPromiseLikeSchema,
|
|
13
14
|
} from '../../schema.js';
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
valueToSchema,
|
|
17
|
+
valueToSchemaOptionsForDeepEqual,
|
|
18
|
+
valueToSchemaOptionsForSatisfies,
|
|
19
|
+
} from '../../value-to-schema.js';
|
|
15
20
|
import { createAssertion } from '../create.js';
|
|
16
21
|
|
|
17
22
|
const trapError = (fn: () => unknown): unknown => {
|
|
@@ -51,7 +56,7 @@ const knownTypes = Object.freeze(
|
|
|
51
56
|
|
|
52
57
|
export const ParametricAssertions = [
|
|
53
58
|
createAssertion(
|
|
54
|
-
[['to be an instance of', 'to be a'],
|
|
59
|
+
[['to be an instance of', 'to be a'], ConstructibleSchema],
|
|
55
60
|
(_, ctor) => z.instanceof(ctor),
|
|
56
61
|
),
|
|
57
62
|
createAssertion(
|
|
@@ -304,19 +309,19 @@ export const ParametricAssertions = [
|
|
|
304
309
|
}
|
|
305
310
|
},
|
|
306
311
|
),
|
|
307
|
-
// @ts-expect-error fix later
|
|
308
312
|
createAssertion(
|
|
309
313
|
[
|
|
310
314
|
z.looseObject({}),
|
|
311
315
|
['to deep equal', 'to deeply equal'],
|
|
312
316
|
z.looseObject({}),
|
|
313
317
|
],
|
|
314
|
-
(_, expected) => valueToSchema(expected,
|
|
318
|
+
(_, expected) => valueToSchema(expected, valueToSchemaOptionsForDeepEqual),
|
|
315
319
|
),
|
|
316
|
-
// @ts-expect-error fix later
|
|
317
320
|
createAssertion(
|
|
318
321
|
[ArrayLikeSchema, ['to deep equal', 'to deeply equal'], ArrayLikeSchema],
|
|
319
|
-
(_, expected) =>
|
|
322
|
+
(_, expected) => {
|
|
323
|
+
return valueToSchema(expected, valueToSchemaOptionsForDeepEqual);
|
|
324
|
+
},
|
|
320
325
|
),
|
|
321
326
|
createAssertion([FunctionSchema, 'to throw'], (subject) => {
|
|
322
327
|
const error = trapError(subject);
|
|
@@ -328,7 +333,7 @@ export const ParametricAssertions = [
|
|
|
328
333
|
}
|
|
329
334
|
}),
|
|
330
335
|
createAssertion(
|
|
331
|
-
[FunctionSchema, ['to throw a', 'to thrown an'],
|
|
336
|
+
[FunctionSchema, ['to throw a', 'to thrown an'], ConstructibleSchema],
|
|
332
337
|
(subject, ctor) => {
|
|
333
338
|
const error = trapError(subject);
|
|
334
339
|
if (!error) {
|
|
@@ -376,10 +381,7 @@ export const ParametricAssertions = [
|
|
|
376
381
|
.or(z.coerce.string().regex(param))
|
|
377
382
|
.safeParse(error).success;
|
|
378
383
|
} else if (isNonNullObject(param)) {
|
|
379
|
-
const schema = valueToSchema(param,
|
|
380
|
-
literalPrimitives: true,
|
|
381
|
-
strict: true,
|
|
382
|
-
});
|
|
384
|
+
const schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
|
|
383
385
|
return schema.safeParse(error).success;
|
|
384
386
|
} else {
|
|
385
387
|
throw new TypeError(`Invalid parameter schema: ${inspect(param)}`);
|
|
@@ -390,12 +392,13 @@ export const ParametricAssertions = [
|
|
|
390
392
|
[
|
|
391
393
|
FunctionSchema,
|
|
392
394
|
['to throw a', 'to thrown an'],
|
|
393
|
-
|
|
395
|
+
ConstructibleSchema,
|
|
394
396
|
'satisfying',
|
|
395
397
|
z.union([z.string(), z.instanceof(RegExp), z.looseObject({})]),
|
|
396
398
|
],
|
|
397
399
|
(subject, ctor, param) => {
|
|
398
400
|
const error = trapError(subject);
|
|
401
|
+
|
|
399
402
|
if (!isA(error, ctor)) {
|
|
400
403
|
return {
|
|
401
404
|
actual: error,
|
|
@@ -405,47 +408,32 @@ export const ParametricAssertions = [
|
|
|
405
408
|
: `Expected function to throw an instance of ${ctor.name}, but it threw a non-object value: ${error as unknown}`,
|
|
406
409
|
};
|
|
407
410
|
}
|
|
408
|
-
|
|
411
|
+
let schema: undefined | z.ZodType;
|
|
412
|
+
// TODO: can valueToSchema handle the first two conditional branches?
|
|
409
413
|
if (isString(param)) {
|
|
410
|
-
|
|
414
|
+
schema = z
|
|
411
415
|
.looseObject({
|
|
412
|
-
message: z.coerce.string().
|
|
416
|
+
message: z.coerce.string().pipe(z.literal(param)),
|
|
413
417
|
})
|
|
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
|
-
}
|
|
418
|
+
.or(z.coerce.string().pipe(z.literal(param)));
|
|
423
419
|
} else if (isA(param, RegExp)) {
|
|
424
|
-
|
|
420
|
+
schema = z
|
|
425
421
|
.looseObject({
|
|
426
422
|
message: z.coerce.string().regex(param),
|
|
427
423
|
})
|
|
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
|
-
}
|
|
424
|
+
.or(z.coerce.string().regex(param));
|
|
437
425
|
} else if (isNonNullObject(param)) {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
426
|
+
schema = valueToSchema(param, valueToSchemaOptionsForSatisfies);
|
|
427
|
+
}
|
|
428
|
+
if (!schema) {
|
|
429
|
+
throw new TypeError(
|
|
430
|
+
`Invalid parameter schema: ${inspect(param, { depth: 2 })}`,
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const result = schema.safeParse(error);
|
|
435
|
+
if (!result.success) {
|
|
436
|
+
return result.error;
|
|
449
437
|
}
|
|
450
438
|
},
|
|
451
439
|
),
|
|
@@ -455,7 +443,15 @@ export const ParametricAssertions = [
|
|
|
455
443
|
['includes', 'contains', 'to include', 'to contain'],
|
|
456
444
|
z.string(),
|
|
457
445
|
],
|
|
458
|
-
(subject, expected) =>
|
|
446
|
+
(subject, expected) => {
|
|
447
|
+
if (!subject.includes(expected)) {
|
|
448
|
+
return {
|
|
449
|
+
actual: subject,
|
|
450
|
+
expected: `string including "${expected}"`,
|
|
451
|
+
message: `Expected "${subject}" to include "${expected}"`,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
},
|
|
459
455
|
),
|
|
460
456
|
|
|
461
457
|
createAssertion([z.string(), 'to match', RegExpSchema], (subject, regex) =>
|
|
@@ -465,16 +461,13 @@ export const ParametricAssertions = [
|
|
|
465
461
|
[
|
|
466
462
|
z.looseObject({}).nonoptional(),
|
|
467
463
|
['to satisfy', 'to be like'],
|
|
468
|
-
|
|
464
|
+
SatisfyPatternSchema,
|
|
469
465
|
],
|
|
470
|
-
(
|
|
471
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
472
|
-
valueToSchema(shape) as unknown as z.ZodType<{}, z.core.$loose>,
|
|
466
|
+
(_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
|
|
473
467
|
),
|
|
474
468
|
createAssertion(
|
|
475
|
-
[ArrayLikeSchema, ['to satisfy', 'to be like'],
|
|
476
|
-
(
|
|
477
|
-
valueToSchema(shape) as unknown as typeof ArrayLikeSchema,
|
|
469
|
+
[ArrayLikeSchema, ['to satisfy', 'to be like'], SatisfyPatternSchema],
|
|
470
|
+
(_subject, shape) => valueToSchema(shape, valueToSchemaOptionsForSatisfies),
|
|
478
471
|
),
|
|
479
472
|
createAssertion(
|
|
480
473
|
[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
|
*
|
|
@@ -35,3 +43,5 @@ export const kBupkisAssertionError: unique symbol = Symbol('bupkis-error');
|
|
|
35
43
|
export const kBupkisNegatedAssertionError: unique symbol = Symbol(
|
|
36
44
|
'bupkis-negated-error',
|
|
37
45
|
);
|
|
46
|
+
|
|
47
|
+
export const kExpectIt: unique symbol = Symbol('bupkis-expect-it');
|
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,9 @@ 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, ExpectItExecutor, ZodTypeMap } from './types.js';
|
|
28
|
+
|
|
29
|
+
import { kExpectIt } from './constant.js';
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Returns `true` if the given value looks like a Zod v4 schema, determined by
|
|
@@ -33,28 +35,60 @@ import type { Constructor } from './types.js';
|
|
|
33
35
|
* Note: This relies on Zod's internal shape and is intended for runtime
|
|
34
36
|
* discrimination within this library.
|
|
35
37
|
*
|
|
36
|
-
* @template T
|
|
38
|
+
* @template T - The specific ZodType to check for (based on def.type)
|
|
37
39
|
* @param value - Value to test
|
|
38
40
|
* @returns Whether the value is `ZodType`-like
|
|
39
41
|
*/
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
export function isZodType<T extends keyof ZodTypeMap>(
|
|
43
|
+
value: unknown,
|
|
44
|
+
type: T,
|
|
45
|
+
): value is ZodTypeMap[T];
|
|
46
|
+
/**
|
|
47
|
+
* Returns `true` if the given value looks like a Zod v4 schema, determined by
|
|
48
|
+
* the presence of an internal {@link z.core.$ZodTypeDef} field.
|
|
49
|
+
*
|
|
50
|
+
* Note: This relies on Zod's internal shape and is intended for runtime
|
|
51
|
+
* discrimination within this library.
|
|
52
|
+
*
|
|
53
|
+
* @param value - Value to test
|
|
54
|
+
* @returns Whether the value is `ZodType`-like
|
|
55
|
+
*/
|
|
56
|
+
export function isZodType(value: unknown): value is z.ZodType;
|
|
57
|
+
export function isZodType<T extends keyof ZodTypeMap>(
|
|
58
|
+
value: unknown,
|
|
59
|
+
type?: T,
|
|
60
|
+
): value is T extends keyof ZodTypeMap ? ZodTypeMap[T] : z.ZodType {
|
|
61
|
+
const isValid =
|
|
62
|
+
isObject(value) &&
|
|
44
63
|
'def' in value &&
|
|
45
|
-
value.def &&
|
|
64
|
+
!!value.def &&
|
|
46
65
|
typeof value.def === 'object' &&
|
|
47
|
-
'type' in value.def
|
|
48
|
-
|
|
66
|
+
'type' in value.def;
|
|
67
|
+
|
|
68
|
+
if (!isValid) return false;
|
|
69
|
+
if (type === undefined) return true;
|
|
70
|
+
|
|
71
|
+
return (value as z.ZodType).def.type === type;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Type guard for a plain object.
|
|
76
|
+
*
|
|
77
|
+
* @param value Value to test
|
|
78
|
+
* @returns `true` if the value is a plain object, `false` otherwise
|
|
79
|
+
*/
|
|
80
|
+
export const isObject = (value: unknown): value is NonNullable<object> => {
|
|
81
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
82
|
+
};
|
|
49
83
|
|
|
50
84
|
/**
|
|
51
|
-
* Returns true if the given value is a {@link z.ZodPromise} schema.
|
|
85
|
+
* Returns `true` if the given value is a {@link z.ZodPromise} schema.
|
|
52
86
|
*
|
|
53
87
|
* @param value - Value to test
|
|
54
88
|
* @returns `true` if the value is a `ZodPromise` schema; `false` otherwise
|
|
55
89
|
*/
|
|
56
90
|
export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
57
|
-
isZodType(value
|
|
91
|
+
isZodType(value, 'promise');
|
|
58
92
|
|
|
59
93
|
/**
|
|
60
94
|
* Checks if a value is "promise-like", meaning it is a "thenable" object.
|
|
@@ -63,12 +97,7 @@ export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
|
63
97
|
* @returns `true` if the value is promise-like, `false` otherwise
|
|
64
98
|
*/
|
|
65
99
|
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
|
-
);
|
|
100
|
+
isObject(value) && 'then' in value && isFunction(value.then);
|
|
72
101
|
|
|
73
102
|
/**
|
|
74
103
|
* Returns `true` if the given value is a constructable function (i.e., a
|
|
@@ -85,7 +114,7 @@ export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
|
|
|
85
114
|
* @param fn - Function to test
|
|
86
115
|
* @returns Whether the function is constructable
|
|
87
116
|
*/
|
|
88
|
-
export const
|
|
117
|
+
export const isConstructible = (fn: unknown): fn is Constructor => {
|
|
89
118
|
if (fn === Symbol || fn === BigInt) {
|
|
90
119
|
return false;
|
|
91
120
|
}
|
|
@@ -194,6 +223,30 @@ export const isPhraseLiteralChoice = (
|
|
|
194
223
|
export const isPhraseLiteral = (value: AssertionPart): value is string =>
|
|
195
224
|
isString(value) && !value.startsWith('not ');
|
|
196
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Generic type guard for instanceof checks.
|
|
228
|
+
*
|
|
229
|
+
* This function provides a type-safe way to check if a value is an instance of
|
|
230
|
+
* a given constructor, with proper type narrowing for TypeScript. It combines
|
|
231
|
+
* the null/object check with instanceof to ensure the value is a valid object
|
|
232
|
+
* before performing the instance check.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
*
|
|
236
|
+
* ```typescript
|
|
237
|
+
* const obj = new Date();
|
|
238
|
+
* if (isA(obj, Date)) {
|
|
239
|
+
* // obj is now typed as Date
|
|
240
|
+
* console.log(obj.getTime());
|
|
241
|
+
* }
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @template T - The constructor type to check against
|
|
245
|
+
* @param value - Value to test
|
|
246
|
+
* @param ctor - Constructor function to check instanceof
|
|
247
|
+
* @returns `true` if the value is an instance of the constructor, `false`
|
|
248
|
+
* otherwise
|
|
249
|
+
*/
|
|
197
250
|
export const isA = <T extends Constructor>(
|
|
198
251
|
value: unknown,
|
|
199
252
|
ctor: T,
|
|
@@ -201,4 +254,58 @@ export const isA = <T extends Constructor>(
|
|
|
201
254
|
return isNonNullObject(value) && value instanceof ctor;
|
|
202
255
|
};
|
|
203
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Type guard for Error instances.
|
|
259
|
+
*
|
|
260
|
+
* This function checks if a value is an instance of the Error class or any of
|
|
261
|
+
* its subclasses. It's useful for error handling and type narrowing when
|
|
262
|
+
* working with unknown values that might be errors.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
*
|
|
266
|
+
* ```typescript
|
|
267
|
+
* try {
|
|
268
|
+
* throw new TypeError('Something went wrong');
|
|
269
|
+
* } catch (err) {
|
|
270
|
+
* if (isError(err)) {
|
|
271
|
+
* // err is now typed as Error
|
|
272
|
+
* console.log(err.message);
|
|
273
|
+
* }
|
|
274
|
+
* }
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* @param value - Value to test
|
|
278
|
+
* @returns `true` if the value is an Error instance, `false` otherwise
|
|
279
|
+
*/
|
|
204
280
|
export const isError = (value: unknown): value is Error => isA(value, Error);
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Type guard for {@link ExpectItExecutor} functions.
|
|
284
|
+
*
|
|
285
|
+
* This function checks if a value is an {@link ExpectItExecutor} function
|
|
286
|
+
* created by {@link bupkis!expect.it | expect.it()}. {@link ExpectItExecutor}
|
|
287
|
+
* functions are special functions that contain assertion logic and are marked
|
|
288
|
+
* with an internal symbol for identification. They are used in nested
|
|
289
|
+
* assertions within "to satisfy" patterns and other complex assertion
|
|
290
|
+
* scenarios.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
*
|
|
294
|
+
* ```typescript
|
|
295
|
+
* const executor = expect.it('to be a string');
|
|
296
|
+
* if (isExpectItExecutor(executor)) {
|
|
297
|
+
* // executor is now typed as ExpectItExecutor
|
|
298
|
+
* // Can be used in satisfaction patterns
|
|
299
|
+
* }
|
|
300
|
+
* ```
|
|
301
|
+
*
|
|
302
|
+
* @template Subject - The subject type that the executor function operates on
|
|
303
|
+
* @param value - Value to test
|
|
304
|
+
* @returns `true` if the value is an ExpectItExecutor function, `false`
|
|
305
|
+
* otherwise
|
|
306
|
+
*/
|
|
307
|
+
export const isExpectItExecutor = <Subject extends z.ZodType = z.ZodUnknown>(
|
|
308
|
+
value: unknown,
|
|
309
|
+
): value is ExpectItExecutor<Subject> => {
|
|
310
|
+
return isFunction(value) && kExpectIt in value && value[kExpectIt] === true;
|
|
311
|
+
};
|
package/src/index.ts
CHANGED