ai-tests 2.0.2 → 2.1.3
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/.turbo/turbo-build.log +4 -5
- package/.turbo/turbo-test.log +8 -0
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/dist/assertions.d.ts +79 -7
- package/dist/assertions.d.ts.map +1 -1
- package/dist/assertions.js +80 -14
- package/dist/assertions.js.map +1 -1
- package/package.json +13 -14
- package/src/assertions.js +383 -0
- package/src/assertions.ts +143 -32
- package/src/cli.js +76 -0
- package/src/index.js +18 -0
- package/src/local.js +62 -0
- package/src/runner.js +214 -0
- package/src/types.js +4 -0
- package/src/worker.js +91 -0
- package/test/assertions.test.js +493 -0
- package/test/index.test.js +42 -0
- package/test/local.test.js +27 -0
- package/test/runner.test.js +315 -0
- package/test/type-safety-assertions.test.ts +201 -0
- package/test/worker.test.js +162 -0
- package/vitest.config.js +10 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion utilities powered by Chai
|
|
3
|
+
*
|
|
4
|
+
* Exposes expect, should, and assert APIs via RPC.
|
|
5
|
+
* Uses Chai under the hood for battle-tested assertions.
|
|
6
|
+
*/
|
|
7
|
+
import * as chai from 'chai';
|
|
8
|
+
import { RpcTarget } from 'cloudflare:workers';
|
|
9
|
+
// Initialize chai's should
|
|
10
|
+
chai.should();
|
|
11
|
+
/**
|
|
12
|
+
* Wrapper around Chai's expect that extends RpcTarget
|
|
13
|
+
* This allows the assertion chain to work over RPC with promise pipelining
|
|
14
|
+
*/
|
|
15
|
+
export class Assertion extends RpcTarget {
|
|
16
|
+
// Using 'any' to avoid complex type gymnastics with Chai's chainable types
|
|
17
|
+
// (Deep, Nested, etc. don't match Chai.Assertion directly)
|
|
18
|
+
assertion;
|
|
19
|
+
constructor(value, message) {
|
|
20
|
+
super();
|
|
21
|
+
this.assertion = chai.expect(value, message);
|
|
22
|
+
}
|
|
23
|
+
// Chainable language chains
|
|
24
|
+
get to() { return this; }
|
|
25
|
+
get be() { return this; }
|
|
26
|
+
get been() { return this; }
|
|
27
|
+
get is() { return this; }
|
|
28
|
+
get that() { return this; }
|
|
29
|
+
get which() { return this; }
|
|
30
|
+
get and() { return this; }
|
|
31
|
+
get has() { return this; }
|
|
32
|
+
get have() { return this; }
|
|
33
|
+
get with() { return this; }
|
|
34
|
+
get at() { return this; }
|
|
35
|
+
get of() { return this; }
|
|
36
|
+
get same() { return this; }
|
|
37
|
+
get but() { return this; }
|
|
38
|
+
get does() { return this; }
|
|
39
|
+
get still() { return this; }
|
|
40
|
+
get also() { return this; }
|
|
41
|
+
// Negation
|
|
42
|
+
get not() {
|
|
43
|
+
this.assertion = this.assertion.not;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
// Deep flag
|
|
47
|
+
get deep() {
|
|
48
|
+
this.assertion = this.assertion.deep;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
// Nested flag
|
|
52
|
+
get nested() {
|
|
53
|
+
this.assertion = this.assertion.nested;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
// Own flag
|
|
57
|
+
get own() {
|
|
58
|
+
this.assertion = this.assertion.own;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
// Ordered flag
|
|
62
|
+
get ordered() {
|
|
63
|
+
this.assertion = this.assertion.ordered;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
// Any flag
|
|
67
|
+
get any() {
|
|
68
|
+
this.assertion = this.assertion.any;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
// All flag
|
|
72
|
+
get all() {
|
|
73
|
+
this.assertion = this.assertion.all;
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
// Length chain
|
|
77
|
+
get length() {
|
|
78
|
+
this.assertion = this.assertion.length;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
// Type assertions
|
|
82
|
+
get ok() { this.assertion.ok; return this; }
|
|
83
|
+
get true() { this.assertion.true; return this; }
|
|
84
|
+
get false() { this.assertion.false; return this; }
|
|
85
|
+
get null() { this.assertion.null; return this; }
|
|
86
|
+
get undefined() { this.assertion.undefined; return this; }
|
|
87
|
+
get NaN() { this.assertion.NaN; return this; }
|
|
88
|
+
get exist() { this.assertion.exist; return this; }
|
|
89
|
+
get empty() { this.assertion.empty; return this; }
|
|
90
|
+
get arguments() { this.assertion.arguments; return this; }
|
|
91
|
+
// Value assertions
|
|
92
|
+
equal(value, message) {
|
|
93
|
+
this.assertion.equal(value, message);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
equals(value, message) {
|
|
97
|
+
return this.equal(value, message);
|
|
98
|
+
}
|
|
99
|
+
eq(value, message) {
|
|
100
|
+
return this.equal(value, message);
|
|
101
|
+
}
|
|
102
|
+
eql(value, message) {
|
|
103
|
+
this.assertion.eql(value, message);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
eqls(value, message) {
|
|
107
|
+
return this.eql(value, message);
|
|
108
|
+
}
|
|
109
|
+
above(value, message) {
|
|
110
|
+
this.assertion.above(value, message);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
gt(value, message) {
|
|
114
|
+
return this.above(value, message);
|
|
115
|
+
}
|
|
116
|
+
greaterThan(value, message) {
|
|
117
|
+
return this.above(value, message);
|
|
118
|
+
}
|
|
119
|
+
least(value, message) {
|
|
120
|
+
this.assertion.least(value, message);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
gte(value, message) {
|
|
124
|
+
return this.least(value, message);
|
|
125
|
+
}
|
|
126
|
+
greaterThanOrEqual(value, message) {
|
|
127
|
+
return this.least(value, message);
|
|
128
|
+
}
|
|
129
|
+
below(value, message) {
|
|
130
|
+
this.assertion.below(value, message);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
lt(value, message) {
|
|
134
|
+
return this.below(value, message);
|
|
135
|
+
}
|
|
136
|
+
lessThan(value, message) {
|
|
137
|
+
return this.below(value, message);
|
|
138
|
+
}
|
|
139
|
+
most(value, message) {
|
|
140
|
+
this.assertion.most(value, message);
|
|
141
|
+
return this;
|
|
142
|
+
}
|
|
143
|
+
lte(value, message) {
|
|
144
|
+
return this.most(value, message);
|
|
145
|
+
}
|
|
146
|
+
lessThanOrEqual(value, message) {
|
|
147
|
+
return this.most(value, message);
|
|
148
|
+
}
|
|
149
|
+
within(start, finish, message) {
|
|
150
|
+
this.assertion.within(start, finish, message);
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
instanceof(constructor, message) {
|
|
154
|
+
this.assertion.instanceof(constructor, message);
|
|
155
|
+
return this;
|
|
156
|
+
}
|
|
157
|
+
instanceOf(constructor, message) {
|
|
158
|
+
return this.instanceof(constructor, message);
|
|
159
|
+
}
|
|
160
|
+
property(name, value, message) {
|
|
161
|
+
if (arguments.length > 1) {
|
|
162
|
+
this.assertion.property(name, value, message);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.assertion.property(name);
|
|
166
|
+
}
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
ownProperty(name, message) {
|
|
170
|
+
this.assertion.ownProperty(name, message);
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
haveOwnProperty(name, message) {
|
|
174
|
+
return this.ownProperty(name, message);
|
|
175
|
+
}
|
|
176
|
+
ownPropertyDescriptor(name, descriptor, message) {
|
|
177
|
+
this.assertion.ownPropertyDescriptor(name, descriptor, message);
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
lengthOf(n, message) {
|
|
181
|
+
this.assertion.lengthOf(n, message);
|
|
182
|
+
return this;
|
|
183
|
+
}
|
|
184
|
+
match(re, message) {
|
|
185
|
+
this.assertion.match(re, message);
|
|
186
|
+
return this;
|
|
187
|
+
}
|
|
188
|
+
matches(re, message) {
|
|
189
|
+
return this.match(re, message);
|
|
190
|
+
}
|
|
191
|
+
string(str, message) {
|
|
192
|
+
this.assertion.string(str, message);
|
|
193
|
+
return this;
|
|
194
|
+
}
|
|
195
|
+
keys(...keys) {
|
|
196
|
+
this.assertion.keys(...keys);
|
|
197
|
+
return this;
|
|
198
|
+
}
|
|
199
|
+
key(...keys) {
|
|
200
|
+
return this.keys(...keys);
|
|
201
|
+
}
|
|
202
|
+
throw(errorLike, errMsgMatcher, message) {
|
|
203
|
+
this.assertion.throw(errorLike, errMsgMatcher, message);
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
throws(errorLike, errMsgMatcher, message) {
|
|
207
|
+
return this.throw(errorLike, errMsgMatcher, message);
|
|
208
|
+
}
|
|
209
|
+
Throw(errorLike, errMsgMatcher, message) {
|
|
210
|
+
return this.throw(errorLike, errMsgMatcher, message);
|
|
211
|
+
}
|
|
212
|
+
respondTo(method, message) {
|
|
213
|
+
this.assertion.respondTo(method, message);
|
|
214
|
+
return this;
|
|
215
|
+
}
|
|
216
|
+
respondsTo(method, message) {
|
|
217
|
+
return this.respondTo(method, message);
|
|
218
|
+
}
|
|
219
|
+
satisfy(matcher, message) {
|
|
220
|
+
this.assertion.satisfy(matcher, message);
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
satisfies(matcher, message) {
|
|
224
|
+
return this.satisfy(matcher, message);
|
|
225
|
+
}
|
|
226
|
+
closeTo(expected, delta, message) {
|
|
227
|
+
this.assertion.closeTo(expected, delta, message);
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
approximately(expected, delta, message) {
|
|
231
|
+
return this.closeTo(expected, delta, message);
|
|
232
|
+
}
|
|
233
|
+
members(set, message) {
|
|
234
|
+
this.assertion.members(set, message);
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
oneOf(list, message) {
|
|
238
|
+
this.assertion.oneOf(list, message);
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
include(value, message) {
|
|
242
|
+
this.assertion.include(value, message);
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
includes(value, message) {
|
|
246
|
+
return this.include(value, message);
|
|
247
|
+
}
|
|
248
|
+
contain(value, message) {
|
|
249
|
+
return this.include(value, message);
|
|
250
|
+
}
|
|
251
|
+
contains(value, message) {
|
|
252
|
+
return this.include(value, message);
|
|
253
|
+
}
|
|
254
|
+
a(type, message) {
|
|
255
|
+
this.assertion.a(type, message);
|
|
256
|
+
return this;
|
|
257
|
+
}
|
|
258
|
+
an(type, message) {
|
|
259
|
+
return this.a(type, message);
|
|
260
|
+
}
|
|
261
|
+
// Vitest-compatible aliases
|
|
262
|
+
toBe(value) {
|
|
263
|
+
this.assertion.equal(value);
|
|
264
|
+
return this;
|
|
265
|
+
}
|
|
266
|
+
toEqual(value) {
|
|
267
|
+
this.assertion.deep.equal(value);
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
toStrictEqual(value) {
|
|
271
|
+
this.assertion.deep.equal(value);
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
toBeTruthy() {
|
|
275
|
+
this.assertion.ok;
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
toBeFalsy() {
|
|
279
|
+
this.assertion.not.ok;
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
toBeNull() {
|
|
283
|
+
this.assertion.null;
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
toBeUndefined() {
|
|
287
|
+
this.assertion.undefined;
|
|
288
|
+
return this;
|
|
289
|
+
}
|
|
290
|
+
toBeDefined() {
|
|
291
|
+
this.assertion.not.undefined;
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
toBeNaN() {
|
|
295
|
+
this.assertion.NaN;
|
|
296
|
+
return this;
|
|
297
|
+
}
|
|
298
|
+
toContain(value) {
|
|
299
|
+
this.assertion.include(value);
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
toHaveLength(length) {
|
|
303
|
+
this.assertion.lengthOf(length);
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
toHaveProperty(path, value) {
|
|
307
|
+
if (arguments.length > 1) {
|
|
308
|
+
this.assertion.nested.property(path, value);
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
this.assertion.nested.property(path);
|
|
312
|
+
}
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
toMatch(pattern) {
|
|
316
|
+
if (typeof pattern === 'string') {
|
|
317
|
+
this.assertion.include(pattern);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.assertion.match(pattern);
|
|
321
|
+
}
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
toMatchObject(obj) {
|
|
325
|
+
this.assertion.deep.include(obj);
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
toThrow(expected) {
|
|
329
|
+
if (expected) {
|
|
330
|
+
this.assertion.throw(expected);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
this.assertion.throw();
|
|
334
|
+
}
|
|
335
|
+
return this;
|
|
336
|
+
}
|
|
337
|
+
toBeGreaterThan(n) {
|
|
338
|
+
this.assertion.above(n);
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
toBeLessThan(n) {
|
|
342
|
+
this.assertion.below(n);
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
toBeGreaterThanOrEqual(n) {
|
|
346
|
+
this.assertion.least(n);
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
toBeLessThanOrEqual(n) {
|
|
350
|
+
this.assertion.most(n);
|
|
351
|
+
return this;
|
|
352
|
+
}
|
|
353
|
+
toBeCloseTo(n, digits = 2) {
|
|
354
|
+
const delta = Math.pow(10, -digits) / 2;
|
|
355
|
+
this.assertion.closeTo(n, delta);
|
|
356
|
+
return this;
|
|
357
|
+
}
|
|
358
|
+
toBeInstanceOf(cls) {
|
|
359
|
+
this.assertion.instanceof(cls);
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
toBeTypeOf(type) {
|
|
363
|
+
this.assertion.a(type);
|
|
364
|
+
return this;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Assert API - TDD style assertions
|
|
369
|
+
*/
|
|
370
|
+
export const assert = chai.assert;
|
|
371
|
+
/**
|
|
372
|
+
* Create an expect assertion
|
|
373
|
+
*/
|
|
374
|
+
export function expect(value, message) {
|
|
375
|
+
return new Assertion(value, message);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Create a should-style assertion
|
|
379
|
+
* Since we can't modify Object.prototype over RPC, this takes a value
|
|
380
|
+
*/
|
|
381
|
+
export function should(value) {
|
|
382
|
+
return new Assertion(value);
|
|
383
|
+
}
|
package/src/assertions.ts
CHANGED
|
@@ -11,14 +11,65 @@ import { RpcTarget } from 'cloudflare:workers'
|
|
|
11
11
|
// Initialize chai's should
|
|
12
12
|
chai.should()
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Type for constructor functions that can be used with instanceof assertions.
|
|
16
|
+
* Matches any class or function that can construct instances.
|
|
17
|
+
*/
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
type Constructor<T = unknown> = new (...args: any[]) => T
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Type for error-like values that can be used with throw assertions.
|
|
23
|
+
* Chai's throw() accepts:
|
|
24
|
+
* - Error constructor (e.g., TypeError, RangeError)
|
|
25
|
+
* - Error instance (e.g., new Error('message'))
|
|
26
|
+
* - String for message matching
|
|
27
|
+
* - RegExp for pattern matching
|
|
28
|
+
*/
|
|
29
|
+
type ThrowableMatch = string | RegExp | Error | Constructor<Error>
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Internal type for Chai assertion chain.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* **Why we use Chai.Assertion here:**
|
|
36
|
+
*
|
|
37
|
+
* Chai's fluent API uses a chainable pattern where each flag (.not, .deep, .nested, etc.)
|
|
38
|
+
* returns a different TypeScript interface:
|
|
39
|
+
* - Chai.Assertion: Base type from expect()
|
|
40
|
+
* - Chai.Deep: Returned after .deep (has subset of properties)
|
|
41
|
+
* - Chai.Nested, Chai.Own, etc.: Other flag-specific types
|
|
42
|
+
*
|
|
43
|
+
* These types form a complex hierarchy where not all methods exist on all types.
|
|
44
|
+
* For example, Chai.Deep has .equal() but not .ok, while Chai.KeyFilter has
|
|
45
|
+
* .keys() but not .equal().
|
|
46
|
+
*
|
|
47
|
+
* Our wrapper class stores the current assertion state and calls methods on it.
|
|
48
|
+
* At runtime, all these methods exist because Chai's actual implementation always
|
|
49
|
+
* has them - the type restrictions are for API guidance, not runtime behavior.
|
|
50
|
+
*
|
|
51
|
+
* We use Chai.Assertion (the most complete type) because:
|
|
52
|
+
* 1. It has all the methods we need to call
|
|
53
|
+
* 2. The assignment from flag types to Chai.Assertion is unsound in strict TS,
|
|
54
|
+
* but at runtime these objects have all the methods we need
|
|
55
|
+
* 3. We wrap the result in our own Assertion class, so the internal type
|
|
56
|
+
* doesn't leak to consumers
|
|
57
|
+
*
|
|
58
|
+
* The type assertions in the flag getters (.deep, .nested, etc.) are intentional
|
|
59
|
+
* and documented. They bridge Chai's complex type hierarchy to our simpler wrapper.
|
|
60
|
+
*/
|
|
61
|
+
type ChaiAssertionChain = Chai.Assertion
|
|
62
|
+
|
|
14
63
|
/**
|
|
15
64
|
* Wrapper around Chai's expect that extends RpcTarget
|
|
16
65
|
* This allows the assertion chain to work over RPC with promise pipelining
|
|
17
66
|
*/
|
|
18
67
|
export class Assertion extends RpcTarget {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Internal Chai assertion chain.
|
|
70
|
+
* @see ChaiAssertionChain for explanation of the type choice.
|
|
71
|
+
*/
|
|
72
|
+
private assertion: ChaiAssertionChain
|
|
22
73
|
|
|
23
74
|
constructor(value: unknown, message?: string) {
|
|
24
75
|
super()
|
|
@@ -44,51 +95,79 @@ export class Assertion extends RpcTarget {
|
|
|
44
95
|
get still() { return this }
|
|
45
96
|
get also() { return this }
|
|
46
97
|
|
|
47
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Negation flag - inverts the assertion.
|
|
100
|
+
* @remarks Type cast needed: Chai's .not returns Chai.Assertion, which matches our type.
|
|
101
|
+
*/
|
|
48
102
|
get not(): Assertion {
|
|
49
103
|
this.assertion = this.assertion.not
|
|
50
104
|
return this
|
|
51
105
|
}
|
|
52
106
|
|
|
53
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Deep flag - enables deep equality comparisons.
|
|
109
|
+
* @remarks Type cast needed: Chai's .deep returns Chai.Deep, but at runtime
|
|
110
|
+
* it has all the methods we need. We cast to maintain our wrapper's type.
|
|
111
|
+
*/
|
|
54
112
|
get deep(): Assertion {
|
|
55
|
-
this.assertion = this.assertion.deep
|
|
113
|
+
this.assertion = this.assertion.deep as ChaiAssertionChain
|
|
56
114
|
return this
|
|
57
115
|
}
|
|
58
116
|
|
|
59
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Nested flag - enables nested property access with dot notation.
|
|
119
|
+
* @remarks Type cast needed: Chai's .nested returns Chai.Nested, cast to our chain type.
|
|
120
|
+
*/
|
|
60
121
|
get nested(): Assertion {
|
|
61
|
-
this.assertion = this.assertion.nested
|
|
122
|
+
this.assertion = this.assertion.nested as ChaiAssertionChain
|
|
62
123
|
return this
|
|
63
124
|
}
|
|
64
125
|
|
|
65
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Own flag - only checks own properties, not inherited.
|
|
128
|
+
* @remarks Type cast needed: Chai's .own returns Chai.Own, cast to our chain type.
|
|
129
|
+
*/
|
|
66
130
|
get own(): Assertion {
|
|
67
|
-
this.assertion = this.assertion.own
|
|
131
|
+
this.assertion = this.assertion.own as ChaiAssertionChain
|
|
68
132
|
return this
|
|
69
133
|
}
|
|
70
134
|
|
|
71
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Ordered flag - requires members to be in order.
|
|
137
|
+
* @remarks Type cast needed: Chai's .ordered returns Chai.Ordered, cast to our chain type.
|
|
138
|
+
*/
|
|
72
139
|
get ordered(): Assertion {
|
|
73
|
-
this.assertion = this.assertion.ordered
|
|
140
|
+
this.assertion = this.assertion.ordered as ChaiAssertionChain
|
|
74
141
|
return this
|
|
75
142
|
}
|
|
76
143
|
|
|
77
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Any flag - requires at least one key match.
|
|
146
|
+
* @remarks Type cast needed: Chai's .any returns Chai.KeyFilter, cast to our chain type.
|
|
147
|
+
*/
|
|
78
148
|
get any(): Assertion {
|
|
79
|
-
this.assertion = this.assertion.any
|
|
149
|
+
this.assertion = this.assertion.any as ChaiAssertionChain
|
|
80
150
|
return this
|
|
81
151
|
}
|
|
82
152
|
|
|
83
|
-
|
|
153
|
+
/**
|
|
154
|
+
* All flag - requires all keys to match.
|
|
155
|
+
* @remarks Type cast needed: Chai's .all returns Chai.KeyFilter, cast to our chain type.
|
|
156
|
+
*/
|
|
84
157
|
get all(): Assertion {
|
|
85
|
-
this.assertion = this.assertion.all
|
|
158
|
+
this.assertion = this.assertion.all as ChaiAssertionChain
|
|
86
159
|
return this
|
|
87
160
|
}
|
|
88
161
|
|
|
89
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Length chain - for asserting on length property.
|
|
164
|
+
* @remarks Type cast needed: Chai's .length returns Chai.Length which is quite different
|
|
165
|
+
* from Chai.Assertion (it's also callable). We cast through unknown because Length
|
|
166
|
+
* doesn't directly overlap with Assertion in TypeScript's structural type system,
|
|
167
|
+
* but at runtime the object has all methods we need.
|
|
168
|
+
*/
|
|
90
169
|
get length(): Assertion {
|
|
91
|
-
this.assertion = this.assertion.length
|
|
170
|
+
this.assertion = this.assertion.length as unknown as ChaiAssertionChain
|
|
92
171
|
return this
|
|
93
172
|
}
|
|
94
173
|
|
|
@@ -183,12 +262,12 @@ export class Assertion extends RpcTarget {
|
|
|
183
262
|
return this
|
|
184
263
|
}
|
|
185
264
|
|
|
186
|
-
instanceof(constructor:
|
|
187
|
-
this.assertion.instanceof(constructor
|
|
265
|
+
instanceof(constructor: Constructor, message?: string) {
|
|
266
|
+
this.assertion.instanceof(constructor, message)
|
|
188
267
|
return this
|
|
189
268
|
}
|
|
190
269
|
|
|
191
|
-
instanceOf(constructor:
|
|
270
|
+
instanceOf(constructor: Constructor, message?: string) {
|
|
192
271
|
return this.instanceof(constructor, message)
|
|
193
272
|
}
|
|
194
273
|
|
|
@@ -211,7 +290,11 @@ export class Assertion extends RpcTarget {
|
|
|
211
290
|
}
|
|
212
291
|
|
|
213
292
|
ownPropertyDescriptor(name: string, descriptor?: PropertyDescriptor, message?: string) {
|
|
214
|
-
|
|
293
|
+
if (descriptor !== undefined) {
|
|
294
|
+
this.assertion.ownPropertyDescriptor(name, descriptor, message)
|
|
295
|
+
} else {
|
|
296
|
+
this.assertion.ownPropertyDescriptor(name, message)
|
|
297
|
+
}
|
|
215
298
|
return this
|
|
216
299
|
}
|
|
217
300
|
|
|
@@ -243,16 +326,37 @@ export class Assertion extends RpcTarget {
|
|
|
243
326
|
return this.keys(...keys)
|
|
244
327
|
}
|
|
245
328
|
|
|
246
|
-
|
|
247
|
-
|
|
329
|
+
/**
|
|
330
|
+
* Asserts that the function throws an error.
|
|
331
|
+
*
|
|
332
|
+
* @param errorLike - Error constructor, instance, string, or RegExp to match
|
|
333
|
+
* @param errMsgMatcher - String or RegExp to match error message (when errorLike is constructor/instance)
|
|
334
|
+
* @param message - Custom assertion message
|
|
335
|
+
*
|
|
336
|
+
* @remarks
|
|
337
|
+
* Chai's throw() has two overloads that TypeScript can't resolve with our union type.
|
|
338
|
+
* We use runtime type checking to call the appropriate overload, then use a type
|
|
339
|
+
* assertion to satisfy TypeScript. This is safe because Chai accepts all these
|
|
340
|
+
* combinations at runtime.
|
|
341
|
+
*/
|
|
342
|
+
throw(errorLike?: ThrowableMatch, errMsgMatcher?: string | RegExp, message?: string) {
|
|
343
|
+
if (errorLike === undefined) {
|
|
344
|
+
this.assertion.throw()
|
|
345
|
+
} else if (typeof errorLike === 'string' || errorLike instanceof RegExp) {
|
|
346
|
+
// First overload: (expected?: string | RegExp, message?: string)
|
|
347
|
+
this.assertion.throw(errorLike, errMsgMatcher as string | undefined)
|
|
348
|
+
} else {
|
|
349
|
+
// Second overload: (constructor: Error | Function, expected?: string | RegExp, message?: string)
|
|
350
|
+
this.assertion.throw(errorLike as Error | Function, errMsgMatcher, message)
|
|
351
|
+
}
|
|
248
352
|
return this
|
|
249
353
|
}
|
|
250
354
|
|
|
251
|
-
throws(errorLike?:
|
|
355
|
+
throws(errorLike?: ThrowableMatch, errMsgMatcher?: string | RegExp, message?: string) {
|
|
252
356
|
return this.throw(errorLike, errMsgMatcher, message)
|
|
253
357
|
}
|
|
254
358
|
|
|
255
|
-
Throw(errorLike?:
|
|
359
|
+
Throw(errorLike?: ThrowableMatch, errMsgMatcher?: string | RegExp, message?: string) {
|
|
256
360
|
return this.throw(errorLike, errMsgMatcher, message)
|
|
257
361
|
}
|
|
258
362
|
|
|
@@ -398,11 +502,18 @@ export class Assertion extends RpcTarget {
|
|
|
398
502
|
return this
|
|
399
503
|
}
|
|
400
504
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
505
|
+
/**
|
|
506
|
+
* Vitest-compatible: Asserts that the function throws.
|
|
507
|
+
* @remarks Uses same runtime type checking as throw() to handle Chai's overloads.
|
|
508
|
+
*/
|
|
509
|
+
toThrow(expected?: ThrowableMatch) {
|
|
510
|
+
if (expected === undefined) {
|
|
405
511
|
this.assertion.throw()
|
|
512
|
+
} else if (typeof expected === 'string' || expected instanceof RegExp) {
|
|
513
|
+
this.assertion.throw(expected)
|
|
514
|
+
} else {
|
|
515
|
+
// Error instance or constructor
|
|
516
|
+
this.assertion.throw(expected as Error | Function)
|
|
406
517
|
}
|
|
407
518
|
return this
|
|
408
519
|
}
|
|
@@ -433,8 +544,8 @@ export class Assertion extends RpcTarget {
|
|
|
433
544
|
return this
|
|
434
545
|
}
|
|
435
546
|
|
|
436
|
-
toBeInstanceOf(cls:
|
|
437
|
-
this.assertion.instanceof(cls
|
|
547
|
+
toBeInstanceOf(cls: Constructor) {
|
|
548
|
+
this.assertion.instanceof(cls)
|
|
438
549
|
return this
|
|
439
550
|
}
|
|
440
551
|
|