bupkis 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.
- package/CHANGELOG.md +12 -0
- package/README.md +26 -2
- package/dist/commonjs/assertion/impl/async.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/async.js +6 -2
- package/dist/commonjs/assertion/impl/async.js.map +1 -1
- package/dist/commonjs/assertion/impl/callback.js +2 -2
- package/dist/commonjs/assertion/impl/callback.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-parametric.d.ts +5 -14
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.js +5 -5
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync.d.ts +6 -15
- package/dist/commonjs/assertion/impl/sync.d.ts.map +1 -1
- package/dist/commonjs/bootstrap.d.ts +6 -15
- package/dist/commonjs/bootstrap.d.ts.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 +2 -1
- package/dist/commonjs/constant.js.map +1 -1
- package/dist/commonjs/guards.d.ts +74 -1
- package/dist/commonjs/guards.d.ts.map +1 -1
- package/dist/commonjs/guards.js +78 -1
- package/dist/commonjs/guards.js.map +1 -1
- package/dist/commonjs/index.d.ts +7 -16
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/schema.d.ts +66 -10
- package/dist/commonjs/schema.d.ts.map +1 -1
- package/dist/commonjs/schema.js +87 -12
- package/dist/commonjs/schema.js.map +1 -1
- package/dist/commonjs/types.d.ts +132 -1
- 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/value-to-schema.d.ts.map +1 -1
- package/dist/commonjs/value-to-schema.js +20 -0
- package/dist/commonjs/value-to-schema.js.map +1 -1
- package/dist/esm/assertion/impl/async.d.ts.map +1 -1
- package/dist/esm/assertion/impl/async.js +7 -3
- package/dist/esm/assertion/impl/async.js.map +1 -1
- package/dist/esm/assertion/impl/callback.js +3 -3
- package/dist/esm/assertion/impl/callback.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-parametric.d.ts +5 -14
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.js +6 -6
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/esm/assertion/impl/sync.d.ts +6 -15
- package/dist/esm/assertion/impl/sync.d.ts.map +1 -1
- package/dist/esm/bootstrap.d.ts +6 -15
- package/dist/esm/bootstrap.d.ts.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 +1 -0
- package/dist/esm/constant.js.map +1 -1
- package/dist/esm/guards.d.ts +74 -1
- package/dist/esm/guards.d.ts.map +1 -1
- package/dist/esm/guards.js +76 -0
- package/dist/esm/guards.js.map +1 -1
- package/dist/esm/index.d.ts +7 -16
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/schema.d.ts +66 -10
- package/dist/esm/schema.d.ts.map +1 -1
- package/dist/esm/schema.js +87 -12
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/types.d.ts +132 -1
- 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/value-to-schema.d.ts.map +1 -1
- package/dist/esm/value-to-schema.js +21 -1
- package/dist/esm/value-to-schema.js.map +1 -1
- package/package.json +1 -1
- package/src/assertion/impl/async.ts +7 -3
- package/src/assertion/impl/callback.ts +3 -3
- package/src/assertion/impl/sync-basic.ts +5 -2
- package/src/assertion/impl/sync-collection.ts +3 -3
- package/src/assertion/impl/sync-parametric.ts +7 -6
- package/src/constant.ts +2 -0
- package/src/guards.ts +81 -1
- package/src/index.ts +2 -0
- package/src/schema.ts +99 -12
- package/src/types.ts +165 -2
- package/src/use.ts +22 -0
- package/src/value-to-schema.ts +25 -0
package/src/schema.ts
CHANGED
|
@@ -37,15 +37,21 @@ import { z } from 'zod/v4';
|
|
|
37
37
|
import {
|
|
38
38
|
isA,
|
|
39
39
|
isConstructible,
|
|
40
|
+
isExpectItExecutor,
|
|
40
41
|
isFunction,
|
|
41
42
|
isNonNullObject,
|
|
42
43
|
isPromiseLike,
|
|
43
44
|
} from './guards.js';
|
|
44
45
|
import { BupkisRegistry } from './metadata.js';
|
|
45
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
type Constructor,
|
|
48
|
+
type ExpectItExecutor,
|
|
49
|
+
type MutableOrReadonly,
|
|
50
|
+
type SatisfyPatternValue,
|
|
51
|
+
} from './types.js';
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
|
-
* A Zod schema that validates JavaScript
|
|
54
|
+
* A Zod schema that validates JavaScript constructible functions.
|
|
49
55
|
*
|
|
50
56
|
* This schema validates values that can be used as constructors, including ES6
|
|
51
57
|
* classes, traditional constructor functions, and built-in constructors. It
|
|
@@ -54,28 +60,28 @@ import { type Constructor, type MutableOrReadonly } from './types.js';
|
|
|
54
60
|
*
|
|
55
61
|
* @privateRemarks
|
|
56
62
|
* The schema is registered in the {@link BupkisRegistry} with the name
|
|
57
|
-
* `
|
|
63
|
+
* `ConstructibleSchema` for later reference and type checking purposes.
|
|
58
64
|
* @example
|
|
59
65
|
*
|
|
60
66
|
* ```typescript
|
|
61
67
|
* class MyClass {}
|
|
62
68
|
* function MyConstructor() {}
|
|
63
69
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
+
* ConstructibleSchema.parse(MyClass); // ✓ Valid
|
|
71
|
+
* ConstructibleSchema.parse(MyConstructor); // ✓ Valid
|
|
72
|
+
* ConstructibleSchema.parse(Array); // ✓ Valid
|
|
73
|
+
* ConstructibleSchema.parse(Date); // ✓ Valid
|
|
74
|
+
* ConstructibleSchema.parse(() => {}); // ✗ Throws validation error
|
|
75
|
+
* ConstructibleSchema.parse({}); // ✗ Throws validation error
|
|
70
76
|
* ```
|
|
71
77
|
*
|
|
72
78
|
* @group Schema
|
|
73
79
|
*/
|
|
74
80
|
|
|
75
|
-
export const
|
|
81
|
+
export const ConstructibleSchema = z
|
|
76
82
|
.custom<Constructor>(isConstructible)
|
|
77
|
-
.register(BupkisRegistry, { name: '
|
|
78
|
-
.describe('
|
|
83
|
+
.register(BupkisRegistry, { name: 'ConstructibleSchema' })
|
|
84
|
+
.describe('Constructible Function');
|
|
79
85
|
|
|
80
86
|
/**
|
|
81
87
|
* A Zod schema that validates any JavaScript function.
|
|
@@ -506,3 +512,84 @@ export const RegExpSchema = z
|
|
|
506
512
|
.instanceof(RegExp)
|
|
507
513
|
.describe('A RegExp instance')
|
|
508
514
|
.register(BupkisRegistry, { name: 'RegExp' });
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* A recursive Zod schema that validates values allowed in "to satisfy"
|
|
518
|
+
* assertion patterns.
|
|
519
|
+
*
|
|
520
|
+
* This schema defines the allowed structure for patterns used with "to satisfy"
|
|
521
|
+
* and "to be like" assertions. It supports a recursive structure that can
|
|
522
|
+
* contain:
|
|
523
|
+
*
|
|
524
|
+
* - **Primitive values**: strings, numbers, booleans, null, undefined, bigint,
|
|
525
|
+
* symbols
|
|
526
|
+
* - **Regular expressions**: for pattern matching against string values
|
|
527
|
+
* - **ExpectItExecutor functions**: nested assertions created via `expect.it()`
|
|
528
|
+
* - **Objects**: with properties that recursively follow this same pattern
|
|
529
|
+
* - **Arrays**: with elements that recursively follow this same pattern
|
|
530
|
+
*
|
|
531
|
+
* The schema handles the special semantics required for "to satisfy"
|
|
532
|
+
* assertions:
|
|
533
|
+
*
|
|
534
|
+
* - RegExp values are used for pattern testing rather than exact matching
|
|
535
|
+
* - ExpectItExecutor functions are executed as nested assertions
|
|
536
|
+
* - Objects and arrays can contain any combination of the above recursively
|
|
537
|
+
*
|
|
538
|
+
* @privateRemarks
|
|
539
|
+
* Uses `z.lazy()` to handle recursive object and array structures without
|
|
540
|
+
* causing infinite recursion during schema definition. The schema is registered
|
|
541
|
+
* in the `BupkisRegistry` for reference and type checking.
|
|
542
|
+
* @example
|
|
543
|
+
*
|
|
544
|
+
* ```typescript
|
|
545
|
+
* // Primitive values
|
|
546
|
+
* SatisfyPatternSchema.parse('hello'); // ✓ Valid
|
|
547
|
+
* SatisfyPatternSchema.parse(42); // ✓ Valid
|
|
548
|
+
* SatisfyPatternSchema.parse(true); // ✓ Valid
|
|
549
|
+
*
|
|
550
|
+
* // Regular expressions for pattern matching
|
|
551
|
+
* SatisfyPatternSchema.parse(/test/); // ✓ Valid
|
|
552
|
+
*
|
|
553
|
+
* // ExpectItExecutor functions
|
|
554
|
+
* SatisfyPatternSchema.parse(expect.it('to be a string')); // ✓ Valid
|
|
555
|
+
*
|
|
556
|
+
* // Complex nested structures
|
|
557
|
+
* SatisfyPatternSchema.parse({
|
|
558
|
+
* name: expect.it('to be a string'),
|
|
559
|
+
* email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
560
|
+
* age: expect.it('to be greater than', 0),
|
|
561
|
+
* tags: [expect.it('to be a string')],
|
|
562
|
+
* }); // ✓ Valid
|
|
563
|
+
*
|
|
564
|
+
* // Invalid values
|
|
565
|
+
* SatisfyPatternSchema.parse(new Date()); // ✗ Invalid (not supported type)
|
|
566
|
+
* SatisfyPatternSchema.parse(() => {}); // ✗ Invalid (regular function, not ExpectItExecutor)
|
|
567
|
+
* ```
|
|
568
|
+
*
|
|
569
|
+
* @group Schema
|
|
570
|
+
*/
|
|
571
|
+
export const SatisfyPatternSchema: z.ZodType<SatisfyPatternValue> = z
|
|
572
|
+
.lazy(() =>
|
|
573
|
+
z.union([
|
|
574
|
+
// Primitive types
|
|
575
|
+
z.string(),
|
|
576
|
+
z.number(),
|
|
577
|
+
z.boolean(),
|
|
578
|
+
z.null(),
|
|
579
|
+
z.undefined(),
|
|
580
|
+
z.bigint(),
|
|
581
|
+
z.symbol(),
|
|
582
|
+
|
|
583
|
+
// Special types for "to satisfy" semantics
|
|
584
|
+
z.instanceof(RegExp), // For pattern matching
|
|
585
|
+
z.custom<ExpectItExecutor<any>>(isExpectItExecutor, {
|
|
586
|
+
message:
|
|
587
|
+
'Expected an ExpectItExecutor function (created with expect.it())',
|
|
588
|
+
}), // For nested assertions
|
|
589
|
+
|
|
590
|
+
// Recursive structures
|
|
591
|
+
z.array(SatisfyPatternSchema), // Arrays of satisfy patterns
|
|
592
|
+
z.record(z.string(), SatisfyPatternSchema), // Objects with satisfy pattern values
|
|
593
|
+
]),
|
|
594
|
+
)
|
|
595
|
+
.register(BupkisRegistry, { name: 'SatisfyPatternSchema' });
|
package/src/types.ts
CHANGED
|
@@ -53,6 +53,8 @@ import type {
|
|
|
53
53
|
RawAssertionImplSchemaSync,
|
|
54
54
|
} from './assertion/assertion-types.js';
|
|
55
55
|
|
|
56
|
+
import { type kExpectIt } from './constant.js';
|
|
57
|
+
|
|
56
58
|
/**
|
|
57
59
|
* Creates a negated version of a tuple of
|
|
58
60
|
* {@link AssertionPart | AssertionParts}.
|
|
@@ -110,8 +112,6 @@ export interface BaseExpect {
|
|
|
110
112
|
fail: FailFn;
|
|
111
113
|
}
|
|
112
114
|
|
|
113
|
-
export type * from './assertion/assertion-types.js';
|
|
114
|
-
|
|
115
115
|
/**
|
|
116
116
|
* The main API as returned by a {@link UseFn}.
|
|
117
117
|
*
|
|
@@ -166,6 +166,8 @@ export interface Bupkis<
|
|
|
166
166
|
>;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
export type * from './assertion/assertion-types.js';
|
|
170
|
+
|
|
169
171
|
/**
|
|
170
172
|
* Helper type to concatenate two tuples
|
|
171
173
|
*/
|
|
@@ -398,6 +400,125 @@ export type ExpectFunction<
|
|
|
398
400
|
}>
|
|
399
401
|
>;
|
|
400
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Creates embeddable assertion functions that can be used with `to satisfy`.
|
|
405
|
+
*
|
|
406
|
+
* This type generates a union of all possible `expect.it` function signatures
|
|
407
|
+
* based on the available synchronous assertions. Each assertion contributes its
|
|
408
|
+
* own function signature to create embeddable executors that can be used within
|
|
409
|
+
* object patterns for complex validation scenarios.
|
|
410
|
+
*
|
|
411
|
+
* The resulting functions are designed to be used exclusively within `'to
|
|
412
|
+
* satisfy'` assertion contexts, where they provide type-safe pattern matching
|
|
413
|
+
* for nested object structures. Direct execution of these functions outside of
|
|
414
|
+
* their intended context is not supported.
|
|
415
|
+
*
|
|
416
|
+
* @example
|
|
417
|
+
*
|
|
418
|
+
* ```typescript
|
|
419
|
+
* // Create embeddable assertion functions
|
|
420
|
+
* const isString = expect.it('to be a string');
|
|
421
|
+
* const isPositive = expect.it('to be greater than', 0);
|
|
422
|
+
*
|
|
423
|
+
* // Use within 'to satisfy' patterns
|
|
424
|
+
* expect(user, 'to satisfy', {
|
|
425
|
+
* name: isString,
|
|
426
|
+
* age: isPositive,
|
|
427
|
+
* email: /\S+@\S+/,
|
|
428
|
+
* });
|
|
429
|
+
* ```
|
|
430
|
+
*
|
|
431
|
+
* @template SyncAssertions - Array of synchronous assertion objects that define
|
|
432
|
+
* the available assertion logic for embeddable functions
|
|
433
|
+
* @see {@link ExpectItFunction} for individual function signature generation
|
|
434
|
+
* @see {@link ExpectItExecutor} for the executor function interface
|
|
435
|
+
*/
|
|
436
|
+
export type ExpectIt<
|
|
437
|
+
SyncAssertions extends AnySyncAssertions = BuiltinSyncAssertions,
|
|
438
|
+
> = UnionToIntersection<
|
|
439
|
+
TupleToUnion<{
|
|
440
|
+
[K in keyof SyncAssertions]: SyncAssertions[K] extends AnySyncAssertion
|
|
441
|
+
? SyncAssertions[K]['parts'] extends AssertionParts
|
|
442
|
+
? ExpectItFunction<SyncAssertions[K]['parts']>
|
|
443
|
+
: never
|
|
444
|
+
: never;
|
|
445
|
+
}>
|
|
446
|
+
>;
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Interface for executor functions created by `expect.it()`.
|
|
450
|
+
*
|
|
451
|
+
* ExpectItExecutor functions are the result of calling `expect.it()` with
|
|
452
|
+
* assertion parameters. They encapsulate the assertion logic and can be
|
|
453
|
+
* executed later within `'to satisfy'` pattern matching contexts. These
|
|
454
|
+
* functions are marked with an internal symbol to distinguish them from regular
|
|
455
|
+
* functions during pattern validation.
|
|
456
|
+
*
|
|
457
|
+
* The executor accepts a subject value and performs the embedded assertion
|
|
458
|
+
* logic against it. The subject type is constrained by the Zod schema that
|
|
459
|
+
* represents the first part of the assertion definition, ensuring type safety
|
|
460
|
+
* during pattern matching.
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
*
|
|
464
|
+
* ```typescript
|
|
465
|
+
* const isStringExecutor = expect.it('to be a string');
|
|
466
|
+
* // isStringExecutor is an ExpectItExecutor<z.ZodString>
|
|
467
|
+
*
|
|
468
|
+
* // Used within satisfy patterns
|
|
469
|
+
* expect({ name: 'Alice' }, 'to satisfy', {
|
|
470
|
+
* name: isStringExecutor, // Validates that name is a string
|
|
471
|
+
* });
|
|
472
|
+
* ```
|
|
473
|
+
*
|
|
474
|
+
* @template Subject - The Zod schema type that constrains the subject parameter
|
|
475
|
+
* @see {@link ExpectItFunction} for the factory function that creates executors
|
|
476
|
+
*/
|
|
477
|
+
export interface ExpectItExecutor<Subject extends z.ZodType> {
|
|
478
|
+
(subject: z.infer<Subject>): void;
|
|
479
|
+
[kExpectIt]: true;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Function signature for creating ExpectItExecutor instances.
|
|
484
|
+
*
|
|
485
|
+
* This type represents the factory function that creates embeddable assertion
|
|
486
|
+
* executors from assertion parts. It takes the assertion parameters (excluding
|
|
487
|
+
* the subject) and returns an executor function that can be embedded within
|
|
488
|
+
* `'to satisfy'` patterns.
|
|
489
|
+
*
|
|
490
|
+
* The function signature is derived from assertion parts by removing the first
|
|
491
|
+
* part (which becomes the subject type for the executor) and using the
|
|
492
|
+
* remaining parts as parameters. This allows for natural language assertion
|
|
493
|
+
* creation that mirrors the main `expect()` function but produces reusable
|
|
494
|
+
* executor functions.
|
|
495
|
+
*
|
|
496
|
+
* The resulting executor is constrained to only work with subjects that match
|
|
497
|
+
* the first assertion part, providing compile-time type safety for pattern
|
|
498
|
+
* matching scenarios.
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
*
|
|
502
|
+
* ```typescript
|
|
503
|
+
* // For assertion parts: [z.string(), 'to match', z.instanceof(RegExp)]
|
|
504
|
+
* // Results in function: (pattern: RegExp) => ExpectItExecutor<z.ZodString>
|
|
505
|
+
* const matchesPattern = expect.it('to match', /^[A-Z]/);
|
|
506
|
+
*
|
|
507
|
+
* expect({ code: 'ABC123' }, 'to satisfy', {
|
|
508
|
+
* code: matchesPattern, // Validates that code matches the pattern
|
|
509
|
+
* });
|
|
510
|
+
* ```
|
|
511
|
+
*
|
|
512
|
+
* @template Parts - Tuple of assertion parts that define the function signature
|
|
513
|
+
* and executor constraints
|
|
514
|
+
* @see {@link ExpectItExecutor} for the returned executor interface
|
|
515
|
+
* @see {@link TupleTail} for parameter extraction from assertion parts
|
|
516
|
+
* @see {@link SlotsFromParts} for type slot generation
|
|
517
|
+
*/
|
|
518
|
+
export type ExpectItFunction<Parts extends AssertionParts> = (
|
|
519
|
+
...args: MutableOrReadonly<TupleTail<SlotsFromParts<Parts>>>
|
|
520
|
+
) => Parts[0] extends z.ZodType ? ExpectItExecutor<Parts[0]> : never;
|
|
521
|
+
|
|
401
522
|
/**
|
|
402
523
|
* Properties of {@link expect}.
|
|
403
524
|
*/
|
|
@@ -412,6 +533,8 @@ export interface ExpectSyncProps<
|
|
|
412
533
|
*/
|
|
413
534
|
assertions: SyncAssertions;
|
|
414
535
|
|
|
536
|
+
it: ExpectIt<SyncAssertions>;
|
|
537
|
+
|
|
415
538
|
/**
|
|
416
539
|
* Function to add more assertions to this `expect()`, returning a new
|
|
417
540
|
* `expect()` and `expectAsync()` pair with the combined assertions.
|
|
@@ -600,6 +723,23 @@ export type MutableOrReadonly<Tuple extends readonly unknown[]> =
|
|
|
600
723
|
*/
|
|
601
724
|
export type Negation<S extends string> = `not ${S}`;
|
|
602
725
|
|
|
726
|
+
export type SatisfyPatternValue =
|
|
727
|
+
| bigint
|
|
728
|
+
| boolean
|
|
729
|
+
| ExpectItExecutor<any>
|
|
730
|
+
| Map<SatisfyPatternValue, SatisfyPatternValue>
|
|
731
|
+
| null
|
|
732
|
+
| number
|
|
733
|
+
| RegExp
|
|
734
|
+
| SatisfyPatternValue[]
|
|
735
|
+
| Set<SatisfyPatternValue>
|
|
736
|
+
| string
|
|
737
|
+
| symbol
|
|
738
|
+
| undefined
|
|
739
|
+
| WeakMap<Extract<SatisfyPatternValue, object>, SatisfyPatternValue>
|
|
740
|
+
| WeakSet<Extract<SatisfyPatternValue, object>>
|
|
741
|
+
| { [key: string]: SatisfyPatternValue };
|
|
742
|
+
|
|
603
743
|
/**
|
|
604
744
|
* Converts AssertionParts to complete function parameter types for expect
|
|
605
745
|
* functions.
|
|
@@ -646,6 +786,29 @@ export type SlotsFromParts<Parts extends AssertionParts> = NoNeverTuple<
|
|
|
646
786
|
: never
|
|
647
787
|
>;
|
|
648
788
|
|
|
789
|
+
/**
|
|
790
|
+
* Gets the tail (all elements except the first) of a tuple type.
|
|
791
|
+
*
|
|
792
|
+
* Unlike ArrayTail from type-fest, this preserves the tuple structure as a
|
|
793
|
+
* readonly tuple rather than converting to an array type.
|
|
794
|
+
*
|
|
795
|
+
* @example
|
|
796
|
+
*
|
|
797
|
+
* ```typescript
|
|
798
|
+
* type Example = TupleTail<readonly [string, number, boolean]>; // readonly [number, boolean]
|
|
799
|
+
* type Single = TupleTail<readonly [string]>; // readonly []
|
|
800
|
+
* type Empty = TupleTail<readonly []>; // readonly []
|
|
801
|
+
* ```
|
|
802
|
+
*
|
|
803
|
+
* @template T - The tuple type to get the tail of
|
|
804
|
+
*/
|
|
805
|
+
export type TupleTail<T extends readonly unknown[]> = T extends readonly [
|
|
806
|
+
unknown,
|
|
807
|
+
...infer Rest,
|
|
808
|
+
]
|
|
809
|
+
? Rest
|
|
810
|
+
: readonly [];
|
|
811
|
+
|
|
649
812
|
/**
|
|
650
813
|
* The type of a `use()` function.
|
|
651
814
|
*/
|
package/src/use.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type AnyAsyncAssertions,
|
|
6
6
|
type AnySyncAssertions,
|
|
7
7
|
} from './assertion/assertion-types.js';
|
|
8
|
+
import { kExpectIt } from './constant.js';
|
|
8
9
|
import {
|
|
9
10
|
createBaseExpect,
|
|
10
11
|
createExpectAsyncFunction,
|
|
@@ -12,11 +13,31 @@ import {
|
|
|
12
13
|
} from './expect.js';
|
|
13
14
|
import {
|
|
14
15
|
type Concat,
|
|
16
|
+
type ExpectIt,
|
|
15
17
|
type FilterAsyncAssertions,
|
|
16
18
|
type FilterSyncAssertions,
|
|
17
19
|
type UseFn,
|
|
18
20
|
} from './types.js';
|
|
19
21
|
|
|
22
|
+
const createExpectIt = <SyncAssertions extends AnySyncAssertions>(
|
|
23
|
+
expect: any,
|
|
24
|
+
): ExpectIt<SyncAssertions> => {
|
|
25
|
+
const expectIt = (...args: readonly unknown[]) => {
|
|
26
|
+
const func = Object.assign(
|
|
27
|
+
(subject: unknown) => {
|
|
28
|
+
const allArgs = [subject, ...args];
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
30
|
+
expect(...allArgs);
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
[kExpectIt]: true,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
return func;
|
|
37
|
+
};
|
|
38
|
+
return expectIt as unknown as ExpectIt<SyncAssertions>;
|
|
39
|
+
};
|
|
40
|
+
|
|
20
41
|
export function createUse<
|
|
21
42
|
const T extends AnySyncAssertions,
|
|
22
43
|
const U extends AnyAsyncAssertions,
|
|
@@ -50,6 +71,7 @@ export function createUse<
|
|
|
50
71
|
const expect = Object.assign(
|
|
51
72
|
expectFunction,
|
|
52
73
|
createBaseExpect(allSyncAssertions, allAsyncAssertions, 'sync'),
|
|
74
|
+
{ it: createExpectIt(expectFunction) },
|
|
53
75
|
);
|
|
54
76
|
const expectAsync = Object.assign(
|
|
55
77
|
expectAsyncFunction,
|
package/src/value-to-schema.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
isExpectItExecutor,
|
|
4
5
|
isNonNullObject,
|
|
5
6
|
isObject,
|
|
6
7
|
isPromiseLike,
|
|
@@ -100,6 +101,30 @@ export const valueToSchema = (
|
|
|
100
101
|
case 'boolean':
|
|
101
102
|
return literalPrimitives ? z.literal(value as boolean) : z.boolean();
|
|
102
103
|
case 'function':
|
|
104
|
+
// Check if this is an ExpectItExecutor
|
|
105
|
+
if (isExpectItExecutor(value)) {
|
|
106
|
+
// Only allow nested assertions when strict is false (e.g., "to satisfy" semantics)
|
|
107
|
+
if (strict) {
|
|
108
|
+
throw new TypeError(
|
|
109
|
+
'ExpectItExecutor (expect.it) functions are not allowed in strict mode. ' +
|
|
110
|
+
'Use "to satisfy" assertions for nested expectations.',
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
// Return a schema that executes the ExpectItExecutor when validated
|
|
114
|
+
return z.custom<unknown>(
|
|
115
|
+
(subject: unknown) => {
|
|
116
|
+
try {
|
|
117
|
+
value(subject);
|
|
118
|
+
return true;
|
|
119
|
+
} catch {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
message: 'Failed expect.it assertion',
|
|
125
|
+
},
|
|
126
|
+
);
|
|
127
|
+
}
|
|
103
128
|
return z.function();
|
|
104
129
|
case 'number':
|
|
105
130
|
return literalPrimitives ? z.literal(value as number) : z.number();
|