bupkis 0.1.2 → 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 +28 -0
- package/README.md +16 -16
- 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 +39 -84
- 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/create.d.ts +5 -33
- package/dist/commonjs/assertion/create.d.ts.map +1 -1
- package/dist/commonjs/assertion/create.js +17 -6
- package/dist/commonjs/assertion/create.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 +114 -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-esoteric.js +1 -1
- package/dist/commonjs/assertion/impl/sync-esoteric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts +37 -34
- package/dist/commonjs/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/commonjs/assertion/impl/sync-parametric.js +32 -47
- package/dist/commonjs/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/commonjs/assertion/impl/sync.d.ts +105 -58
- 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 +199 -85
- package/dist/commonjs/bootstrap.d.ts.map +1 -1
- package/dist/commonjs/bootstrap.js +19 -10
- 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 +32 -5
- package/dist/commonjs/error.d.ts.map +1 -1
- package/dist/commonjs/error.js +60 -5
- package/dist/commonjs/error.js.map +1 -1
- package/dist/commonjs/expect.d.ts +130 -3
- package/dist/commonjs/expect.d.ts.map +1 -1
- package/dist/commonjs/expect.js +116 -1
- package/dist/commonjs/expect.js.map +1 -1
- package/dist/commonjs/guards.d.ts +45 -20
- package/dist/commonjs/guards.d.ts.map +1 -1
- package/dist/commonjs/guards.js +56 -40
- package/dist/commonjs/guards.js.map +1 -1
- package/dist/commonjs/index.d.ts +241 -86
- package/dist/commonjs/index.d.ts.map +1 -1
- package/dist/commonjs/index.js +44 -42
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/metadata.d.ts +1 -27
- package/dist/commonjs/metadata.d.ts.map +1 -1
- package/dist/commonjs/metadata.js +16 -15
- package/dist/commonjs/metadata.js.map +1 -1
- package/dist/commonjs/schema.d.ts +76 -33
- package/dist/commonjs/schema.d.ts.map +1 -1
- package/dist/commonjs/schema.js +77 -34
- package/dist/commonjs/schema.js.map +1 -1
- package/dist/commonjs/types.d.ts +480 -39
- package/dist/commonjs/types.d.ts.map +1 -1
- package/dist/commonjs/types.js +12 -2
- package/dist/commonjs/types.js.map +1 -1
- package/dist/commonjs/util.d.ts +72 -49
- package/dist/commonjs/util.d.ts.map +1 -1
- package/dist/commonjs/util.js +175 -155
- 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 +39 -84
- 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/create.d.ts +5 -33
- package/dist/esm/assertion/create.d.ts.map +1 -1
- package/dist/esm/assertion/create.js +14 -4
- package/dist/esm/assertion/create.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 +113 -89
- 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 +37 -34
- package/dist/esm/assertion/impl/sync-parametric.d.ts.map +1 -1
- package/dist/esm/assertion/impl/sync-parametric.js +32 -47
- package/dist/esm/assertion/impl/sync-parametric.js.map +1 -1
- package/dist/esm/assertion/impl/sync.d.ts +105 -58
- 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 +199 -85
- package/dist/esm/bootstrap.d.ts.map +1 -1
- package/dist/esm/bootstrap.js +19 -10
- 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 +32 -5
- package/dist/esm/error.d.ts.map +1 -1
- package/dist/esm/error.js +59 -5
- package/dist/esm/error.js.map +1 -1
- package/dist/esm/expect.d.ts +130 -3
- package/dist/esm/expect.d.ts.map +1 -1
- package/dist/esm/expect.js +117 -2
- package/dist/esm/expect.js.map +1 -1
- package/dist/esm/guards.d.ts +45 -20
- package/dist/esm/guards.d.ts.map +1 -1
- package/dist/esm/guards.js +48 -31
- package/dist/esm/guards.js.map +1 -1
- package/dist/esm/index.d.ts +241 -86
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +46 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/metadata.d.ts +1 -27
- package/dist/esm/metadata.d.ts.map +1 -1
- package/dist/esm/metadata.js +2 -1
- package/dist/esm/metadata.js.map +1 -1
- package/dist/esm/schema.d.ts +76 -33
- package/dist/esm/schema.d.ts.map +1 -1
- package/dist/esm/schema.js +77 -34
- package/dist/esm/schema.js.map +1 -1
- package/dist/esm/types.d.ts +480 -39
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +12 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/util.d.ts +72 -49
- package/dist/esm/util.d.ts.map +1 -1
- package/dist/esm/util.js +159 -153
- 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 +94 -17
- package/src/assertion/assertion-async.ts +113 -3
- package/src/assertion/assertion-sync.ts +5 -2
- package/src/assertion/assertion-types.ts +52 -45
- package/src/assertion/assertion.ts +2 -17
- package/src/assertion/create.ts +16 -65
- package/src/assertion/impl/async.ts +132 -92
- 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 +41 -49
- package/src/assertion/impl/sync.ts +3 -0
- package/src/bootstrap.ts +21 -11
- package/src/constant.ts +8 -0
- package/src/error.ts +75 -4
- package/src/expect.ts +275 -20
- package/src/guards.ts +74 -69
- package/src/index.ts +72 -11
- package/src/metadata.ts +3 -4
- package/src/schema.ts +80 -36
- package/src/types.ts +625 -72
- package/src/util.ts +174 -222
- package/src/value-to-schema.ts +464 -0
- package/dist/commonjs/api.d.ts +0 -93
- package/dist/commonjs/api.d.ts.map +0 -1
- package/dist/commonjs/api.js +0 -8
- package/dist/commonjs/api.js.map +0 -1
- package/dist/esm/api.d.ts +0 -93
- package/dist/esm/api.d.ts.map +0 -1
- package/dist/esm/api.js +0 -7
- package/dist/esm/api.js.map +0 -1
- package/src/api.ts +0 -149
- package/src/schema.md +0 -15
package/src/expect.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import Debug from 'debug';
|
|
2
2
|
import { inspect } from 'util';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
type Expect,
|
|
6
|
-
type ExpectAsync,
|
|
7
|
-
type ExpectAsyncFunction,
|
|
8
|
-
type ExpectAsyncProps,
|
|
9
|
-
type ExpectFunction,
|
|
10
|
-
type ExpectSyncProps,
|
|
11
|
-
} from './api.js';
|
|
12
4
|
import {
|
|
13
5
|
type AnyAsyncAssertion,
|
|
14
6
|
type AnyAsyncAssertions,
|
|
@@ -24,19 +16,160 @@ import {
|
|
|
24
16
|
type ParsedValues,
|
|
25
17
|
} from './assertion/assertion-types.js';
|
|
26
18
|
import { createAssertion, createAsyncAssertion } from './assertion/create.js';
|
|
27
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
AssertionError,
|
|
21
|
+
FailAssertionError,
|
|
22
|
+
NegatedAssertionError,
|
|
23
|
+
} from './error.js';
|
|
28
24
|
import { isAssertionFailure, isString } from './guards.js';
|
|
25
|
+
import {
|
|
26
|
+
type Expect,
|
|
27
|
+
type ExpectAsync,
|
|
28
|
+
type ExpectAsyncFunction,
|
|
29
|
+
type ExpectAsyncProps,
|
|
30
|
+
type ExpectFunction,
|
|
31
|
+
type ExpectSyncProps,
|
|
32
|
+
type FailFn,
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
34
|
+
type UseFn,
|
|
35
|
+
} from './types.js';
|
|
29
36
|
import { createUse } from './use.js';
|
|
30
37
|
|
|
31
38
|
const debug = Debug('bupkis:expect');
|
|
32
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Creates an asynchronous expect function by extending a parent expectAsync
|
|
42
|
+
* function with additional assertions.
|
|
43
|
+
*
|
|
44
|
+
* This overload combines assertions from an existing parent expectAsync
|
|
45
|
+
* function with new assertions, creating a unified expectAsync function that
|
|
46
|
+
* supports both sets of assertions. The resulting function inherits all type
|
|
47
|
+
* information from both the parent and new assertions, providing complete
|
|
48
|
+
* TypeScript intellisense and type safety for Promise-based testing scenarios.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
*
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const baseExpectAsync = createExpectAsyncFunction(basicAsyncAssertions);
|
|
54
|
+
* const extendedExpectAsync = createExpectAsyncFunction(
|
|
55
|
+
* customAsyncAssertions,
|
|
56
|
+
* baseExpectAsync,
|
|
57
|
+
* );
|
|
58
|
+
*
|
|
59
|
+
* // Can use both basic and custom async assertions
|
|
60
|
+
* await extendedExpectAsync(promise, 'to resolve'); // From basic assertions
|
|
61
|
+
* await extendedExpectAsync(promise, 'to resolve with custom data'); // From custom assertions
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @param assertions - Array of new asynchronous assertion objects to add
|
|
65
|
+
* @param expect - Parent expectAsync function whose assertions will be
|
|
66
|
+
* inherited
|
|
67
|
+
* @returns ExpectAsync function with combined assertion types from both parent
|
|
68
|
+
* and new assertions
|
|
69
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
70
|
+
* (non-negated) mode
|
|
71
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails
|
|
72
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
73
|
+
* arguments
|
|
74
|
+
*/
|
|
33
75
|
export function createExpectAsyncFunction<
|
|
34
76
|
T extends AnyAsyncAssertions,
|
|
35
77
|
U extends ExpectAsync<AnyAsyncAssertions>,
|
|
36
78
|
>(assertions: T, expect: U): ExpectAsyncFunction<T & U['assertions']>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a new asynchronous expect function with the provided assertions.
|
|
82
|
+
*
|
|
83
|
+
* This overload creates a standalone expectAsync function from the provided
|
|
84
|
+
* assertions without inheriting from any parent function. This is typically
|
|
85
|
+
* used to create the initial expectAsync function or when you want a clean
|
|
86
|
+
* slate without any inherited assertions for Promise-based testing.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
*
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const expectAsync = createExpectAsyncFunction(asyncAssertions);
|
|
92
|
+
* await expectAsync(promise, 'to resolve');
|
|
93
|
+
* await expectAsync(rejectedPromise, 'to reject');
|
|
94
|
+
* await expectAsync(promise, 'to resolve to', expectedValue);
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @param assertions - Array of asynchronous assertion objects that define the
|
|
98
|
+
* available assertion phrases and Promise-based logic
|
|
99
|
+
* @returns An asynchronous expect function that can execute the provided
|
|
100
|
+
* assertions using natural language syntax
|
|
101
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
102
|
+
* (non-negated) mode
|
|
103
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails
|
|
104
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
105
|
+
* arguments
|
|
106
|
+
*/
|
|
37
107
|
export function createExpectAsyncFunction<T extends AnyAsyncAssertions>(
|
|
38
108
|
assertions: T,
|
|
39
109
|
): ExpectAsyncFunction<T>;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Implementation function that creates an asynchronous expect function with
|
|
113
|
+
* optional parent inheritance.
|
|
114
|
+
*
|
|
115
|
+
* This is the concrete implementation that handles both overload cases for
|
|
116
|
+
* Promise-based assertions. It creates an expectAsync function that uses a
|
|
117
|
+
* two-phase matching algorithm: first seeking exact phrase matches for optimal
|
|
118
|
+
* performance, then falling back to partial matches if needed. The function
|
|
119
|
+
* processes negation keywords, combines parent assertions with new ones, and
|
|
120
|
+
* ensures all operations are properly awaited.
|
|
121
|
+
*
|
|
122
|
+
* The matching algorithm prioritizes exact matches to minimize performance
|
|
123
|
+
* overhead, but provides flexibility through partial matching when exact
|
|
124
|
+
* phrases don't align. This enables natural language flexibility while
|
|
125
|
+
* maintaining execution speed for common async assertion patterns.
|
|
126
|
+
*
|
|
127
|
+
* @remarks
|
|
128
|
+
* The function performs async assertion matching in the following order:
|
|
129
|
+
*
|
|
130
|
+
* 1. Awaits `Promise.resolve()` to ensure the function is always asynchronous
|
|
131
|
+
* 2. Processes negation keywords ('not', 'to not') to determine assertion mode
|
|
132
|
+
* 3. Combines parent assertions (if provided) with new assertions in execution
|
|
133
|
+
* order
|
|
134
|
+
* 4. Attempts to parse arguments against each assertion's expected phrase pattern
|
|
135
|
+
* using `parseValuesAsync`
|
|
136
|
+
* 5. Prioritizes exact phrase matches over partial matches for performance
|
|
137
|
+
* 6. Executes the first successful match using {@link executeAsync} or throws an
|
|
138
|
+
* error if none found
|
|
139
|
+
*
|
|
140
|
+
* Performance considerations: The function loops through all available
|
|
141
|
+
* assertions for each call, but uses early termination when exact matches are
|
|
142
|
+
* found. For performance-critical code, consider using assertion functions with
|
|
143
|
+
* fewer total assertions or more specific phrase patterns to reduce matching
|
|
144
|
+
* overhead.
|
|
145
|
+
*
|
|
146
|
+
* All assertion execution is properly awaited to handle Promise-based
|
|
147
|
+
* validation logic, error handling, and negation scenarios in asynchronous
|
|
148
|
+
* contexts.
|
|
149
|
+
* @example
|
|
150
|
+
*
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // Used internally by both public overloads
|
|
153
|
+
* const expectAsync1 = createExpectAsyncFunction(assertions); // No parent
|
|
154
|
+
* const expectAsync2 = createExpectAsyncFunction(assertions, parent); // With parent
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* @param assertions - Array of asynchronous assertion objects to make available
|
|
158
|
+
* @param expect - Optional parent expectAsync function to inherit assertions
|
|
159
|
+
* from
|
|
160
|
+
* @returns Asynchronous expect function that processes natural language
|
|
161
|
+
* assertions with Promise support
|
|
162
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
163
|
+
* (non-negated) mode
|
|
164
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails (e.g.,
|
|
165
|
+
* `await expectAsync(promise, 'not to resolve')`)
|
|
166
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
167
|
+
* arguments
|
|
168
|
+
* @internal This is the concrete implementation used by the public overloads
|
|
169
|
+
* @see {@link createExpectSyncFunction} for creating synchronous expect functions
|
|
170
|
+
* @see {@link createAsyncAssertion} for creating individual async assertion objects
|
|
171
|
+
* @see {@link ExpectAsync} for the main expectAsync interface
|
|
172
|
+
*/
|
|
40
173
|
export function createExpectAsyncFunction<
|
|
41
174
|
T extends AnyAsyncAssertions,
|
|
42
175
|
U extends ExpectAsync<AnyAsyncAssertions>,
|
|
@@ -87,18 +220,136 @@ export function createExpectAsyncFunction<
|
|
|
87
220
|
return expectAsyncFunction;
|
|
88
221
|
}
|
|
89
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Creates a synchronous expect function by extending a parent expect function
|
|
225
|
+
* with additional assertions.
|
|
226
|
+
*
|
|
227
|
+
* This overload combines assertions from an existing parent expect function
|
|
228
|
+
* with new assertions, creating a unified expect function that supports both
|
|
229
|
+
* sets of assertions. The resulting function inherits all type information from
|
|
230
|
+
* both the parent and new assertions, providing complete TypeScript
|
|
231
|
+
* intellisense and type safety.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
*
|
|
235
|
+
* ```typescript
|
|
236
|
+
* const baseExpect = createExpectSyncFunction(basicAssertions);
|
|
237
|
+
* const extendedExpect = createExpectSyncFunction(
|
|
238
|
+
* customAssertions,
|
|
239
|
+
* baseExpect,
|
|
240
|
+
* );
|
|
241
|
+
*
|
|
242
|
+
* // Can use both basic and custom assertions
|
|
243
|
+
* extendedExpect(42, 'to be a number'); // From basic assertions
|
|
244
|
+
* extendedExpect(obj, 'to have custom prop'); // From custom assertions
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @param assertions - Array of new synchronous assertion objects to add
|
|
248
|
+
* @param expect - Parent expect function whose assertions will be inherited
|
|
249
|
+
* @returns Expect function with combined assertion types from both parent and
|
|
250
|
+
* new assertions
|
|
251
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
252
|
+
* (non-negated) mode
|
|
253
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails
|
|
254
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
255
|
+
* arguments
|
|
256
|
+
*/
|
|
90
257
|
export function createExpectSyncFunction<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
>(
|
|
258
|
+
Assertions extends AnySyncAssertions,
|
|
259
|
+
ParentExpect extends Expect<AnySyncAssertions>,
|
|
260
|
+
>(
|
|
261
|
+
assertions: Assertions,
|
|
262
|
+
expect: ParentExpect,
|
|
263
|
+
): ExpectFunction<Assertions & ParentExpect['assertions']>;
|
|
94
264
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
265
|
+
/**
|
|
266
|
+
* Creates a new synchronous expect function with the provided assertions.
|
|
267
|
+
*
|
|
268
|
+
* This overload creates a standalone expect function from the provided
|
|
269
|
+
* assertions without inheriting from any parent function. This is typically
|
|
270
|
+
* used to create the initial expect function or when you want a clean slate
|
|
271
|
+
* without any inherited assertions.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
*
|
|
275
|
+
* ```typescript
|
|
276
|
+
* const expect = createExpectSyncFunction(basicAssertions);
|
|
277
|
+
* expect(42, 'to be a number');
|
|
278
|
+
* expect('hello', 'to be a string');
|
|
279
|
+
* expect([], 'to be empty');
|
|
280
|
+
* ```
|
|
281
|
+
*
|
|
282
|
+
* @param assertions - Array of synchronous assertion objects that define the
|
|
283
|
+
* available assertion phrases and logic
|
|
284
|
+
* @returns A synchronous expect function that can execute the provided
|
|
285
|
+
* assertions using natural language syntax
|
|
286
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
287
|
+
* (non-negated) mode
|
|
288
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails
|
|
289
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
290
|
+
* arguments
|
|
291
|
+
*/
|
|
292
|
+
export function createExpectSyncFunction<Assertions extends AnySyncAssertions>(
|
|
293
|
+
assertions: Assertions,
|
|
294
|
+
): ExpectFunction<Assertions>;
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Implementation function that creates a synchronous expect function with
|
|
298
|
+
* optional parent inheritance.
|
|
299
|
+
*
|
|
300
|
+
* This is the concrete implementation that handles both overload cases. It
|
|
301
|
+
* creates an expect function that uses a two-phase matching algorithm: first
|
|
302
|
+
* seeking exact phrase matches for optimal performance, then falling back to
|
|
303
|
+
* partial matches if needed. The function processes negation keywords and
|
|
304
|
+
* combines parent assertions with new ones.
|
|
305
|
+
*
|
|
306
|
+
* The matching algorithm prioritizes exact matches to minimize performance
|
|
307
|
+
* overhead, but provides flexibility through partial matching when exact
|
|
308
|
+
* phrases don't align. This enables natural language flexibility while
|
|
309
|
+
* maintaining execution speed for common assertion patterns.
|
|
310
|
+
*
|
|
311
|
+
* @remarks
|
|
312
|
+
* The function performs assertion matching in the following order:
|
|
313
|
+
*
|
|
314
|
+
* 1. Processes negation keywords ('not', 'to not') to determine assertion mode
|
|
315
|
+
* 2. Combines parent assertions (if provided) with new assertions in execution
|
|
316
|
+
* order
|
|
317
|
+
* 3. Attempts to parse arguments against each assertion's expected phrase pattern
|
|
318
|
+
* 4. Prioritizes exact phrase matches over partial matches for performance
|
|
319
|
+
* 5. Executes the first successful match or throws an error if none found
|
|
320
|
+
*
|
|
321
|
+
* Performance considerations: The function loops through all available
|
|
322
|
+
* assertions for each call, but uses early termination when exact matches are
|
|
323
|
+
* found. For performance-critical code, consider using assertion functions with
|
|
324
|
+
* fewer total assertions or more specific phrase patterns to reduce matching
|
|
325
|
+
* overhead.
|
|
326
|
+
* @example
|
|
327
|
+
*
|
|
328
|
+
* ```typescript
|
|
329
|
+
* // Used internally by both public overloads
|
|
330
|
+
* const expect1 = createExpectSyncFunction(assertions); // No parent
|
|
331
|
+
* const expect2 = createExpectSyncFunction(assertions, parent); // With parent
|
|
332
|
+
* ```
|
|
333
|
+
*
|
|
334
|
+
* @param assertions - Array of synchronous assertion objects to make available
|
|
335
|
+
* @param expect - Optional parent expect function to inherit assertions from
|
|
336
|
+
* @returns Synchronous expect function that processes natural language
|
|
337
|
+
* assertions
|
|
338
|
+
* @throws {@link AssertionError} When an assertion fails in normal
|
|
339
|
+
* (non-negated) mode
|
|
340
|
+
* @throws {@link NegatedAssertionError} When a negated assertion fails (e.g.,
|
|
341
|
+
* `expect(42, 'not to be a number')`)
|
|
342
|
+
* @throws {Error} When no matching assertion can be found for the provided
|
|
343
|
+
* arguments
|
|
344
|
+
* @internal This is the concrete implementation used by the public overloads
|
|
345
|
+
* @see {@link createExpectAsyncFunction} for creating asynchronous expect functions
|
|
346
|
+
* @see {@link createAssertion} for creating individual assertion objects
|
|
347
|
+
* @see {@link Expect} for the main expect interface
|
|
348
|
+
*/
|
|
98
349
|
export function createExpectSyncFunction<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
>(assertions:
|
|
350
|
+
Assertions extends AnySyncAssertions,
|
|
351
|
+
ParentExpect extends Expect<AnySyncAssertions>,
|
|
352
|
+
>(assertions: Assertions, expect?: ParentExpect) {
|
|
102
353
|
debug(
|
|
103
354
|
'Creating expect function with %d assertions',
|
|
104
355
|
assertions.length + (expect?.assertions.length ?? 0),
|
|
@@ -330,10 +581,14 @@ const detectNegation = (
|
|
|
330
581
|
};
|
|
331
582
|
};
|
|
332
583
|
|
|
333
|
-
const fail = (reason?: string): never => {
|
|
334
|
-
throw new
|
|
584
|
+
const fail: FailFn = (reason?: string): never => {
|
|
585
|
+
throw new FailAssertionError({ message: reason });
|
|
335
586
|
};
|
|
336
587
|
|
|
588
|
+
/**
|
|
589
|
+
* Used by a {@link UseFn} to create base properties of the {@link Expect} and
|
|
590
|
+
* {@link ExpectAsync} functions.
|
|
591
|
+
*/
|
|
337
592
|
export function createBaseExpect<
|
|
338
593
|
T extends AnySyncAssertions,
|
|
339
594
|
U extends AnyAsyncAssertions,
|
package/src/guards.ts
CHANGED
|
@@ -2,52 +2,91 @@
|
|
|
2
2
|
* Type guard functions and runtime type checking utilities.
|
|
3
3
|
*
|
|
4
4
|
* This module provides various type guard functions for runtime type checking,
|
|
5
|
-
* including guards for Zod schemas, constructors,
|
|
6
|
-
* assertion parts. These are used throughout the library for safe type
|
|
5
|
+
* including guards for Zod schemas, constructors, {@link PromiseLike} objects,
|
|
6
|
+
* and assertion parts. These are used throughout the library for safe type
|
|
7
7
|
* narrowing and validation.
|
|
8
8
|
*
|
|
9
|
+
* @category API
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import * as guards from 'bupkis/guards';
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
9
16
|
* @packageDocumentation
|
|
10
17
|
*/
|
|
11
18
|
|
|
12
19
|
import { type Primitive } from 'type-fest';
|
|
13
|
-
import { z } from 'zod';
|
|
20
|
+
import { z } from 'zod/v4';
|
|
14
21
|
|
|
15
22
|
import type {
|
|
16
23
|
AssertionFailure,
|
|
17
24
|
AssertionPart,
|
|
18
25
|
PhraseLiteralChoice,
|
|
19
26
|
} from './assertion/assertion-types.js';
|
|
20
|
-
import type { Constructor } from './types.js';
|
|
27
|
+
import type { Constructor, ZodTypeMap } from './types.js';
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
|
-
* Returns true if the given value looks like a Zod schema
|
|
24
|
-
* the presence of an internal
|
|
30
|
+
* Returns `true` if the given value looks like a Zod v4 schema, determined by
|
|
31
|
+
* the presence of an internal {@link z.core.$ZodTypeDef} field.
|
|
25
32
|
*
|
|
26
33
|
* Note: This relies on Zod's internal shape and is intended for runtime
|
|
27
34
|
* discrimination within this library.
|
|
28
35
|
*
|
|
29
|
-
* @template T
|
|
36
|
+
* @template T - The specific ZodType to check for (based on def.type)
|
|
30
37
|
* @param value - Value to test
|
|
31
|
-
* @returns Whether the value is
|
|
38
|
+
* @returns Whether the value is `ZodType`-like
|
|
32
39
|
*/
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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) &&
|
|
37
61
|
'def' in value &&
|
|
38
|
-
value.def &&
|
|
62
|
+
!!value.def &&
|
|
39
63
|
typeof value.def === 'object' &&
|
|
40
|
-
'type' in value.def
|
|
41
|
-
|
|
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
|
+
}
|
|
42
71
|
|
|
43
72
|
/**
|
|
44
|
-
*
|
|
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
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns `true` if the given value is a {@link z.ZodPromise} schema.
|
|
45
84
|
*
|
|
46
85
|
* @param value - Value to test
|
|
47
86
|
* @returns `true` if the value is a `ZodPromise` schema; `false` otherwise
|
|
48
87
|
*/
|
|
49
88
|
export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
50
|
-
isZodType(value
|
|
89
|
+
isZodType(value, 'promise');
|
|
51
90
|
|
|
52
91
|
/**
|
|
53
92
|
* Checks if a value is "promise-like", meaning it is a "thenable" object.
|
|
@@ -56,23 +95,24 @@ export const isZodPromise = (value: unknown): value is z.ZodPromise =>
|
|
|
56
95
|
* @returns `true` if the value is promise-like, `false` otherwise
|
|
57
96
|
*/
|
|
58
97
|
export const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
|
|
59
|
-
|
|
60
|
-
value &&
|
|
61
|
-
typeof value === 'object' &&
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
63
|
-
typeof (value as any).then === 'function'
|
|
64
|
-
);
|
|
98
|
+
isObject(value) && 'then' in value && isFunction(value.then);
|
|
65
99
|
|
|
66
100
|
/**
|
|
67
|
-
* Returns true if the given value is a constructable function (i.e., a
|
|
101
|
+
* Returns `true` if the given value is a constructable function (i.e., a
|
|
102
|
+
* class).
|
|
68
103
|
*
|
|
69
|
-
* This
|
|
70
|
-
*
|
|
104
|
+
* This works by wrapping `fn` in a {@link Proxy}, attaching a no-op
|
|
105
|
+
* {@link ProxyHandler.construct} trap to it, then attempting to construct the
|
|
106
|
+
* proxy via `new`.
|
|
71
107
|
*
|
|
108
|
+
* @privateRemarks
|
|
109
|
+
* This may be the only way we can determine, at runtime, if a function is a
|
|
110
|
+
* constructor without actually calling it. I am unsure if this only works for
|
|
111
|
+
* classes.
|
|
72
112
|
* @param fn - Function to test
|
|
73
113
|
* @returns Whether the function is constructable
|
|
74
114
|
*/
|
|
75
|
-
export const
|
|
115
|
+
export const isConstructible = (fn: unknown): fn is Constructor => {
|
|
76
116
|
if (fn === Symbol || fn === BigInt) {
|
|
77
117
|
return false;
|
|
78
118
|
}
|
|
@@ -121,14 +161,16 @@ const AssertionFailureSchema: z.ZodType<AssertionFailure> = z.object({
|
|
|
121
161
|
.describe('A human-readable message describing the failure'),
|
|
122
162
|
});
|
|
123
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Type guard for a {@link AssertionFailure} object
|
|
166
|
+
*
|
|
167
|
+
* @param value Value to check
|
|
168
|
+
* @returns `true` if the value is an `AssertionFailure`, `false` otherwise
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
124
171
|
export const isAssertionFailure = (value: unknown): value is AssertionFailure =>
|
|
125
172
|
AssertionFailureSchema.safeParse(value).success;
|
|
126
173
|
|
|
127
|
-
export const isAsyncFunction = (
|
|
128
|
-
value: unknown,
|
|
129
|
-
): value is (...args: any[]) => Promise<any> =>
|
|
130
|
-
isFunction(value) && value.constructor.name === 'AsyncFunction';
|
|
131
|
-
|
|
132
174
|
/**
|
|
133
175
|
* Type guard for a string value
|
|
134
176
|
*
|
|
@@ -179,43 +221,6 @@ export const isPhraseLiteralChoice = (
|
|
|
179
221
|
export const isPhraseLiteral = (value: AssertionPart): value is string =>
|
|
180
222
|
isString(value) && !value.startsWith('not ');
|
|
181
223
|
|
|
182
|
-
export type PrimitiveTypeName =
|
|
183
|
-
| 'bigint'
|
|
184
|
-
| 'boolean'
|
|
185
|
-
| 'function'
|
|
186
|
-
| 'null'
|
|
187
|
-
| 'number'
|
|
188
|
-
| 'object'
|
|
189
|
-
| 'string'
|
|
190
|
-
| 'symbol'
|
|
191
|
-
| 'undefined';
|
|
192
|
-
|
|
193
|
-
export type PrimitiveTypeNameToType<T extends PrimitiveTypeName> =
|
|
194
|
-
T extends 'undefined'
|
|
195
|
-
? undefined
|
|
196
|
-
: T extends 'object'
|
|
197
|
-
? null | object
|
|
198
|
-
: T extends 'function'
|
|
199
|
-
? (...args: any[]) => any
|
|
200
|
-
: T extends 'string'
|
|
201
|
-
? string
|
|
202
|
-
: T extends 'number'
|
|
203
|
-
? number
|
|
204
|
-
: T extends 'boolean'
|
|
205
|
-
? boolean
|
|
206
|
-
: T extends 'bigint'
|
|
207
|
-
? bigint
|
|
208
|
-
: T extends 'symbol'
|
|
209
|
-
? symbol
|
|
210
|
-
: never;
|
|
211
|
-
|
|
212
|
-
export const isType = <T extends PrimitiveTypeName>(
|
|
213
|
-
a: unknown,
|
|
214
|
-
b: T,
|
|
215
|
-
): a is PrimitiveTypeNameToType<T> => {
|
|
216
|
-
return typeof a === b;
|
|
217
|
-
};
|
|
218
|
-
|
|
219
224
|
export const isA = <T extends Constructor>(
|
|
220
225
|
value: unknown,
|
|
221
226
|
ctor: T,
|
package/src/index.ts
CHANGED
|
@@ -6,24 +6,85 @@
|
|
|
6
6
|
* guards, schema definitions, utility functions, and error types.
|
|
7
7
|
*
|
|
8
8
|
* @module bupkis
|
|
9
|
+
* @category API
|
|
10
|
+
* @example
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { expect, expectAsync, z, createAssertion } from 'bupkis';
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @showGroups
|
|
9
17
|
*/
|
|
10
18
|
|
|
11
|
-
import {
|
|
12
|
-
export type * from './api.js';
|
|
19
|
+
import { z } from 'zod/v4';
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
import { expect as sacrificialExpect } from './bootstrap.js';
|
|
15
22
|
export { expect, expectAsync } from './bootstrap.js';
|
|
16
23
|
|
|
17
|
-
export
|
|
18
|
-
export * as guards from './guards.js';
|
|
24
|
+
export { AssertionError } from './error.js';
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Re-export of most (all?) types defined within <span
|
|
28
|
+
* class="bupkis">Bupkis</span>.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { types } from 'bupkis';
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
22
36
|
export type * as types from './types.js';
|
|
23
|
-
export * as util from './util.js';
|
|
24
37
|
|
|
25
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Re-export of {@link https://zod.dev Zod v4} for use in custom assertion
|
|
40
|
+
* implementations.
|
|
41
|
+
*/
|
|
42
|
+
export { z };
|
|
26
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @primaryExport
|
|
46
|
+
*/
|
|
47
|
+
export type {
|
|
48
|
+
Bupkis,
|
|
49
|
+
CreateAssertionFn,
|
|
50
|
+
CreateAsyncAssertionFn,
|
|
51
|
+
Expect,
|
|
52
|
+
ExpectAsync,
|
|
53
|
+
FailFn,
|
|
54
|
+
UseFn,
|
|
55
|
+
ZodTypeMap,
|
|
56
|
+
} from './types.js';
|
|
27
57
|
export { createAssertion, createAsyncAssertion, fail, use };
|
|
28
|
-
const {
|
|
29
|
-
|
|
58
|
+
const {
|
|
59
|
+
/**
|
|
60
|
+
* The main factory function for creating asynchronous assertions.
|
|
61
|
+
*
|
|
62
|
+
* Exported from the entry point; is also a property of {@link Expect} and
|
|
63
|
+
* {@link ExpectAsync}.
|
|
64
|
+
*
|
|
65
|
+
* @function
|
|
66
|
+
*/
|
|
67
|
+
createAssertion,
|
|
68
|
+
/**
|
|
69
|
+
* The main factory function for creating asynchronous assertions.
|
|
70
|
+
*
|
|
71
|
+
* Exported from the entry point; is also a property of {@link Expect} and
|
|
72
|
+
* {@link ExpectAsync}.
|
|
73
|
+
*
|
|
74
|
+
* @function
|
|
75
|
+
*/
|
|
76
|
+
createAsyncAssertion,
|
|
77
|
+
/**
|
|
78
|
+
* {@inheritDoc FailFn}
|
|
79
|
+
*
|
|
80
|
+
* @function
|
|
81
|
+
*/
|
|
82
|
+
fail,
|
|
83
|
+
/**
|
|
84
|
+
* {@inheritDoc UseFn}
|
|
85
|
+
*
|
|
86
|
+
* @function
|
|
87
|
+
*/
|
|
88
|
+
use,
|
|
89
|
+
..._rest
|
|
90
|
+
} = sacrificialExpect;
|
package/src/metadata.ts
CHANGED
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
* Defines Bupkis' Zod metadata registry
|
|
3
3
|
*
|
|
4
4
|
* @packageDocumentation
|
|
5
|
+
* @internal
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
+
import { z } from 'zod/v4';
|
|
8
9
|
|
|
9
10
|
import { kStringLiteral } from './constant.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Metadata stored in Zod registry
|
|
13
|
-
*
|
|
14
|
-
* @knipignore
|
|
15
14
|
*/
|
|
16
|
-
|
|
15
|
+
type BupkisMeta = z.infer<typeof BupkisRegistrySchema>;
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Zod metadata registry for Bupkis
|