@plugjs/expect5 0.4.4 → 0.4.5
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/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/execution/executor.cjs.map +1 -1
- package/dist/execution/executor.d.ts +1 -1
- package/dist/execution/executor.mjs +1 -1
- package/dist/execution/executor.mjs.map +1 -1
- package/dist/execution/setup.cjs.map +1 -1
- package/dist/execution/setup.d.ts +1 -1
- package/dist/execution/setup.mjs +1 -1
- package/dist/execution/setup.mjs.map +1 -1
- package/dist/expectation/async.cjs +58 -45
- package/dist/expectation/async.cjs.map +1 -1
- package/dist/expectation/async.d.ts +53 -52
- package/dist/expectation/async.mjs +59 -42
- package/dist/expectation/async.mjs.map +1 -1
- package/dist/expectation/diff.cjs.map +1 -1
- package/dist/expectation/diff.mjs +6 -1
- package/dist/expectation/diff.mjs.map +1 -1
- package/dist/expectation/expect.cjs +11 -165
- package/dist/expectation/expect.cjs.map +2 -2
- package/dist/expectation/expect.d.ts +10 -112
- package/dist/expectation/expect.mjs +12 -207
- package/dist/expectation/expect.mjs.map +2 -2
- package/dist/expectation/expectations.cjs +549 -0
- package/dist/expectation/expectations.cjs.map +6 -0
- package/dist/expectation/expectations.d.ts +454 -0
- package/dist/expectation/expectations.mjs +530 -0
- package/dist/expectation/expectations.mjs.map +6 -0
- package/dist/expectation/include.cjs +43 -41
- package/dist/expectation/include.cjs.map +1 -1
- package/dist/expectation/include.d.ts +3 -19
- package/dist/expectation/include.mjs +43 -41
- package/dist/expectation/include.mjs.map +1 -1
- package/dist/expectation/matchers.cjs +350 -0
- package/dist/expectation/matchers.cjs.map +6 -0
- package/dist/expectation/matchers.d.ts +375 -0
- package/dist/expectation/matchers.mjs +328 -0
- package/dist/expectation/matchers.mjs.map +6 -0
- package/dist/expectation/print.cjs.map +1 -1
- package/dist/expectation/print.d.ts +2 -2
- package/dist/expectation/print.mjs.map +1 -1
- package/dist/expectation/types.cjs +17 -23
- package/dist/expectation/types.cjs.map +1 -1
- package/dist/expectation/types.d.ts +7 -51
- package/dist/expectation/types.mjs +17 -22
- package/dist/expectation/types.mjs.map +1 -1
- package/dist/globals.d.ts +2 -2
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.mjs +11 -12
- package/dist/index.mjs.map +1 -1
- package/dist/test.cjs +34 -5
- package/dist/test.cjs.map +1 -1
- package/dist/test.d.ts +2 -2
- package/dist/test.mjs +35 -6
- package/dist/test.mjs.map +1 -1
- package/package.json +2 -2
- package/src/cli.mts +1 -1
- package/src/execution/executor.ts +1 -3
- package/src/execution/setup.ts +1 -3
- package/src/expectation/async.ts +116 -145
- package/src/expectation/diff.ts +7 -3
- package/src/expectation/expect.ts +22 -362
- package/src/expectation/expectations.ts +971 -0
- package/src/expectation/include.ts +59 -75
- package/src/expectation/matchers.ts +698 -0
- package/src/expectation/print.ts +8 -4
- package/src/expectation/types.ts +22 -94
- package/src/globals.ts +2 -2
- package/src/index.ts +17 -10
- package/src/test.ts +34 -10
- package/dist/expectation/basic.cjs +0 -188
- package/dist/expectation/basic.cjs.map +0 -6
- package/dist/expectation/basic.d.ts +0 -90
- package/dist/expectation/basic.mjs +0 -156
- package/dist/expectation/basic.mjs.map +0 -6
- package/dist/expectation/throwing.cjs +0 -58
- package/dist/expectation/throwing.cjs.map +0 -6
- package/dist/expectation/throwing.d.ts +0 -36
- package/dist/expectation/throwing.mjs +0 -32
- package/dist/expectation/throwing.mjs.map +0 -6
- package/dist/expectation/trivial.cjs +0 -96
- package/dist/expectation/trivial.cjs.map +0 -6
- package/dist/expectation/trivial.d.ts +0 -13
- package/dist/expectation/trivial.mjs +0 -61
- package/dist/expectation/trivial.mjs.map +0 -6
- package/src/expectation/basic.ts +0 -413
- package/src/expectation/throwing.ts +0 -106
- package/src/expectation/trivial.ts +0 -107
|
@@ -0,0 +1,971 @@
|
|
|
1
|
+
import { diff, type Diff } from './diff'
|
|
2
|
+
import { toInclude, toMatchContents } from './include'
|
|
3
|
+
import { type Matchers } from './matchers'
|
|
4
|
+
import {
|
|
5
|
+
ExpectationError,
|
|
6
|
+
isMatcher,
|
|
7
|
+
prefixType,
|
|
8
|
+
stringifyConstructor,
|
|
9
|
+
stringifyValue,
|
|
10
|
+
typeOf,
|
|
11
|
+
type Constructor,
|
|
12
|
+
type TypeMappings,
|
|
13
|
+
type TypeName,
|
|
14
|
+
} from './types'
|
|
15
|
+
|
|
16
|
+
/* ========================================================================== *
|
|
17
|
+
* TYPES SUPPORTING EXPECTATIONS *
|
|
18
|
+
* ========================================================================== */
|
|
19
|
+
|
|
20
|
+
/** An assertion function, for sub-expectations and value introspection */
|
|
21
|
+
export type AssertionFunction<T = unknown> = (assert: Expectations<T>) => void | Expectations
|
|
22
|
+
|
|
23
|
+
/** Return the type asserted by an {@link AssertionFunction} or `T` */
|
|
24
|
+
export type AssertedType<T, F extends AssertionFunction<any>, R = ReturnType<F>> =
|
|
25
|
+
R extends Expectations<infer I> ?
|
|
26
|
+
unknown extends I ?
|
|
27
|
+
T : // returns Expectations<unknown>, use T
|
|
28
|
+
I : // returns Expectations<something>, use "something"
|
|
29
|
+
T // returns something else (void), use T
|
|
30
|
+
|
|
31
|
+
export type InferMatchers<T> =
|
|
32
|
+
T extends Matchers<infer V> ? V :
|
|
33
|
+
T extends Record<any, any> ? { [ k in keyof T ] : InferMatchers<T[k]> } :
|
|
34
|
+
T
|
|
35
|
+
|
|
36
|
+
/** Simple wrapper defining the _parent_ instance of an {@link Expectations}. */
|
|
37
|
+
type ExpectationsParent = {
|
|
38
|
+
/** Parent {@link Expectations} instance */
|
|
39
|
+
expectations: Expectations,
|
|
40
|
+
/** Property associating _this_ to the parent */
|
|
41
|
+
prop: string | number | symbol,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ========================================================================== *
|
|
45
|
+
* EXPECTATIONS *
|
|
46
|
+
* ========================================================================== */
|
|
47
|
+
|
|
48
|
+
/** Main class containing all supported expectations */
|
|
49
|
+
export class Expectations<T = unknown> {
|
|
50
|
+
/** The {@link NegativeExpectations} associated with _this_ */
|
|
51
|
+
readonly not: NegativeExpectations<T>
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create an {@link Expectations} instance associated with the specified
|
|
55
|
+
* value and error remarks.
|
|
56
|
+
*
|
|
57
|
+
* Optionally a parent {@link Expectations} instance can be specified.
|
|
58
|
+
*/
|
|
59
|
+
constructor(
|
|
60
|
+
/** The value associated with this instance */
|
|
61
|
+
readonly value: T,
|
|
62
|
+
/** Optional additional _remarks_ to associate with errors */
|
|
63
|
+
readonly remarks: string | undefined, // required, but migth be undefined
|
|
64
|
+
/**
|
|
65
|
+
* An optional {@link ExpectationsParent} defining _this_ to be a
|
|
66
|
+
* child of another {@link Expectations} instance
|
|
67
|
+
*/
|
|
68
|
+
readonly parent?: ExpectationsParent,
|
|
69
|
+
) {
|
|
70
|
+
this.not = new NegativeExpectations(this)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Throw an {@link ExpectationError} associated with _this_ */
|
|
74
|
+
protected _fail(details: string, diff?: Diff): never {
|
|
75
|
+
throw new ExpectationError(this, details, diff)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* ------------------------------------------------------------------------ *
|
|
79
|
+
* BASIC *
|
|
80
|
+
* ------------------------------------------------------------------------ */
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Expects the value to be of the specified _extended_ {@link TypeName type},
|
|
84
|
+
* and (if specified) further asserts it with an {@link AssertionFunction}.
|
|
85
|
+
*
|
|
86
|
+
* Negation: {@link NegativeExpectations.toBeA `not.toBeA(...)`}
|
|
87
|
+
*/
|
|
88
|
+
toBeA<
|
|
89
|
+
Name extends TypeName,
|
|
90
|
+
Mapped extends TypeMappings[Name],
|
|
91
|
+
Assert extends AssertionFunction<Mapped>,
|
|
92
|
+
>(
|
|
93
|
+
type: Name,
|
|
94
|
+
assertion?: Assert,
|
|
95
|
+
): Expectations<AssertedType<Mapped, Assert>> {
|
|
96
|
+
if (typeOf(this.value) === type) {
|
|
97
|
+
if (assertion) assertion(this as Expectations<any>)
|
|
98
|
+
return this as Expectations<any>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this._fail(`to be ${prefixType(type)}`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* ------------------------------------------------------------------------ */
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Expects the value to be a `number` within a given +/- _delta_ range of the
|
|
108
|
+
* specified expected value.
|
|
109
|
+
*
|
|
110
|
+
* Negation: {@link NegativeExpectations.toBeCloseTo `not.toBeCloseTo(...)`}
|
|
111
|
+
*/
|
|
112
|
+
toBeCloseTo(value: number, delta: number): Expectations<number>
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Expects the value to be a `bigint` within a given +/- _delta_ range of the
|
|
116
|
+
* specified expected value.
|
|
117
|
+
*
|
|
118
|
+
* Negation: {@link NegativeExpectations.toBeCloseTo `not.toBeCloseTo(...)`}
|
|
119
|
+
*/
|
|
120
|
+
toBeCloseTo(value: bigint, delta: bigint): Expectations<bigint>
|
|
121
|
+
|
|
122
|
+
toBeCloseTo(value: number | bigint, delta: number | bigint): Expectations {
|
|
123
|
+
const min = (value as number) - (delta as number)
|
|
124
|
+
const max = (value as number) + (delta as number)
|
|
125
|
+
return this.toBeWithinRange(min, max)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ------------------------------------------------------------------------ */
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Expects the value to be neither `null` nor `undefined`.
|
|
132
|
+
*
|
|
133
|
+
* Negation: {@link NegativeExpectations.toBeDefined `not.toBeDefined()`}
|
|
134
|
+
*/
|
|
135
|
+
toBeDefined(): Expectations<T> {
|
|
136
|
+
if ((this.value !== null) && (this.value !== undefined)) return this
|
|
137
|
+
this._fail(`to be neither ${stringifyValue(null)} nor ${stringifyValue(undefined)}`)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ------------------------------------------------------------------------ */
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Expect the value to be an instance of {@link Error}.
|
|
144
|
+
*
|
|
145
|
+
* If specified, the {@link Error}'s own message will be further expected to
|
|
146
|
+
* either match the specified {@link RegExp}, or equal the specified `string`.
|
|
147
|
+
*/
|
|
148
|
+
toBeError(
|
|
149
|
+
message?: string | RegExp
|
|
150
|
+
): Expectations<Error>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Expect the value to be an instance of {@link Error} and further asserts
|
|
154
|
+
* it to be an instance of the specifed {@link Error} {@link Constructor}.
|
|
155
|
+
*
|
|
156
|
+
* If specified, the {@link Error}'s own message will be further expected to
|
|
157
|
+
* either match the specified {@link RegExp}, or equal the specified `string`.
|
|
158
|
+
*/
|
|
159
|
+
toBeError<Class extends Constructor<Error>>(
|
|
160
|
+
constructor: Class,
|
|
161
|
+
message?: string | RegExp,
|
|
162
|
+
): Expectations<InstanceType<Class>>
|
|
163
|
+
|
|
164
|
+
toBeError(
|
|
165
|
+
constructorOrMessage?: string | RegExp | Constructor,
|
|
166
|
+
maybeMessage?: string | RegExp,
|
|
167
|
+
): Expectations {
|
|
168
|
+
const [ constructor, message ] =
|
|
169
|
+
typeof constructorOrMessage === 'function' ?
|
|
170
|
+
[ constructorOrMessage, maybeMessage ] :
|
|
171
|
+
[ Error, constructorOrMessage ]
|
|
172
|
+
|
|
173
|
+
if (message === undefined) return this.toBeInstanceOf(constructor)
|
|
174
|
+
|
|
175
|
+
return this.toBeInstanceOf(constructor, (assert) => {
|
|
176
|
+
assert.toHaveProperty('message', (assertMessage) => {
|
|
177
|
+
assertMessage.toBeA('string')
|
|
178
|
+
if (typeof message === 'string') assertMessage.toStrictlyEqual(message)
|
|
179
|
+
else assertMessage.toMatch(message)
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* ------------------------------------------------------------------------ */
|
|
185
|
+
|
|
186
|
+
/** Expects the value strictly equal to `false`. */
|
|
187
|
+
toBeFalse(): Expectations<false> {
|
|
188
|
+
return this.toStrictlyEqual(false)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* ------------------------------------------------------------------------ */
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Expects the value to be _falsy_ (zero, empty string, `false`, ...).
|
|
195
|
+
*
|
|
196
|
+
* Negation: {@link Expectations.toBeTruthy `toBeTruthy()`}
|
|
197
|
+
*/
|
|
198
|
+
toBeFalsy(): Expectations<T> {
|
|
199
|
+
if (! this.value) return this
|
|
200
|
+
this._fail('to be falsy')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ------------------------------------------------------------------------ */
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Expects the value to be a `number` greater than the specified expected
|
|
207
|
+
* value.
|
|
208
|
+
*
|
|
209
|
+
* Negation: {@link Expectations.toBeLessThanOrEqual `toBeLessThanOrEqual(...)`}
|
|
210
|
+
*/
|
|
211
|
+
toBeGreaterThan(value: number): Expectations<number>
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Expects the value to be a `bigint` greater than the specified expected
|
|
215
|
+
* value.
|
|
216
|
+
*
|
|
217
|
+
* Negation: {@link Expectations.toBeLessThanOrEqual `toBeLessThanOrEqual(...)`}
|
|
218
|
+
*/
|
|
219
|
+
toBeGreaterThan(value: bigint): Expectations<bigint>
|
|
220
|
+
|
|
221
|
+
toBeGreaterThan(value: number | bigint): Expectations {
|
|
222
|
+
this.toBeA(typeof value)
|
|
223
|
+
if ((this.value as any) > value) return this
|
|
224
|
+
this._fail(`to be greater than ${stringifyValue(value)}`)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* ------------------------------------------------------------------------ */
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Expects the value to be a `number` greater than or equal to the specified
|
|
231
|
+
* expected value.
|
|
232
|
+
*
|
|
233
|
+
* Negation: {@link Expectations.toBeLessThan `toBeLessThan(...)`}
|
|
234
|
+
*/
|
|
235
|
+
toBeGreaterThanOrEqual(value: number): Expectations<number>
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Expects the value to be a `bigint` greater than or equal to the specified
|
|
239
|
+
* expected value.
|
|
240
|
+
*
|
|
241
|
+
* Negation: {@link Expectations.toBeLessThan `toBeLessThan(...)`}
|
|
242
|
+
*/
|
|
243
|
+
toBeGreaterThanOrEqual(value: bigint): Expectations<bigint>
|
|
244
|
+
|
|
245
|
+
toBeGreaterThanOrEqual(value: number | bigint): Expectations {
|
|
246
|
+
this.toBeA(typeof value)
|
|
247
|
+
if ((this.value as any) >= value) return this
|
|
248
|
+
this._fail(`to be greater than or equal to ${stringifyValue(value)}`)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* ------------------------------------------------------------------------ */
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Expects the value to be an instance of the specified {@link Constructor},
|
|
255
|
+
* and (if specified) further asserts it with an {@link AssertionFunction}.
|
|
256
|
+
*
|
|
257
|
+
* Negation: {@link NegativeExpectations.toBeInstanceOf `not.toInstanceOf(...)`}
|
|
258
|
+
*/
|
|
259
|
+
toBeInstanceOf<
|
|
260
|
+
Class extends Constructor,
|
|
261
|
+
Assert extends AssertionFunction<InstanceType<Class>>,
|
|
262
|
+
>(
|
|
263
|
+
constructor: Class,
|
|
264
|
+
assertion?: Assert,
|
|
265
|
+
): Expectations<AssertedType<InstanceType<Class>, Assert>> {
|
|
266
|
+
if (this.value instanceof constructor) {
|
|
267
|
+
if (assertion) assertion(this as Expectations<any>)
|
|
268
|
+
return this as Expectations<any>
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this._fail(`to be an instance of ${stringifyConstructor(constructor)}`)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* ------------------------------------------------------------------------ */
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Expects the value to be a `number` less than the specified expected value.
|
|
278
|
+
*
|
|
279
|
+
* Negation: {@link Expectations.toBeGreaterThanOrEqual `toBeGreaterThanOrEqual(...)`}
|
|
280
|
+
*/
|
|
281
|
+
toBeLessThan(value: number): Expectations<number>
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Expects the value to be a `bigint` less than the specified expected value.
|
|
285
|
+
*
|
|
286
|
+
* Negation: {@link Expectations.toBeGreaterThanOrEqual `toBeGreaterThanOrEqual(...)`}
|
|
287
|
+
*/
|
|
288
|
+
toBeLessThan(value: bigint): Expectations<bigint>
|
|
289
|
+
|
|
290
|
+
toBeLessThan(value: number | bigint): Expectations {
|
|
291
|
+
this.toBeA(typeof value)
|
|
292
|
+
if ((this.value as any) < value) return this
|
|
293
|
+
this._fail(`to be less than ${stringifyValue(value)}`)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ------------------------------------------------------------------------ */
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Expects the value to be a `number` less than or equal to the specified
|
|
300
|
+
* expected value.
|
|
301
|
+
*
|
|
302
|
+
* Negation: {@link Expectations.toBeGreaterThan `toBeGreaterThan(...)`}
|
|
303
|
+
*/
|
|
304
|
+
toBeLessThanOrEqual(value: number): Expectations<number>
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Expects the value to be a `bigint` less than or equal to the specified
|
|
308
|
+
* expected value.
|
|
309
|
+
*
|
|
310
|
+
* Negation: {@link Expectations.toBeGreaterThan `toBeGreaterThan(...)`}
|
|
311
|
+
*/
|
|
312
|
+
toBeLessThanOrEqual(value: bigint): Expectations<bigint>
|
|
313
|
+
|
|
314
|
+
toBeLessThanOrEqual(value: number | bigint): Expectations {
|
|
315
|
+
this.toBeA(typeof value)
|
|
316
|
+
if ((this.value as any) <= value) return this
|
|
317
|
+
this._fail(`to be less than or equal to ${stringifyValue(value)}`)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* ------------------------------------------------------------------------ */
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Expects the value to be `NaN`.
|
|
324
|
+
*
|
|
325
|
+
* Negation: {@link NegativeExpectations.toBeNaN `not.toBeNaN()`}
|
|
326
|
+
*/
|
|
327
|
+
toBeNaN(): Expectations<number> {
|
|
328
|
+
const expectations = this.toBeA('number')
|
|
329
|
+
if (isNaN(expectations.value)) return expectations
|
|
330
|
+
this._fail(`to be ${stringifyValue(NaN)}`)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* ------------------------------------------------------------------------ */
|
|
334
|
+
|
|
335
|
+
/** Expects the value to strictly equal `null`. */
|
|
336
|
+
toBeNull(): Expectations<null> {
|
|
337
|
+
return this.toStrictlyEqual(null)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* ------------------------------------------------------------------------ */
|
|
341
|
+
|
|
342
|
+
/** Expects the value to strictly equal `true`. */
|
|
343
|
+
toBeTrue(): Expectations<true> {
|
|
344
|
+
return this.toStrictlyEqual(true)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* ------------------------------------------------------------------------ */
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Expects the value to be _falsy_ (non-zero, non-empty string, ...).
|
|
351
|
+
*
|
|
352
|
+
* Negation: {@link Expectations.toBeFalsy `toBeFalsy()`}
|
|
353
|
+
*/
|
|
354
|
+
toBeTruthy(): Expectations<T> {
|
|
355
|
+
if (this.value) return this
|
|
356
|
+
this._fail('to be truthy')
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* ------------------------------------------------------------------------ */
|
|
360
|
+
|
|
361
|
+
/** Expects the value to strictly equal `undefined`. */
|
|
362
|
+
toBeUndefined(): Expectations<undefined> {
|
|
363
|
+
return this.toStrictlyEqual(undefined)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* ------------------------------------------------------------------------ */
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Expects the value to be a `number` within the specified range where the
|
|
370
|
+
* minimum and maximum values are inclusive.
|
|
371
|
+
*
|
|
372
|
+
* Negation: {@link NegativeExpectations.toBeWithinRange `not.toBeWithinRange(...)`}
|
|
373
|
+
*/
|
|
374
|
+
toBeWithinRange(min: number, max: number): Expectations<number>
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Expects the value to be a `bigint` within the specified range where the
|
|
378
|
+
* minimum and maximum values are inclusive.
|
|
379
|
+
*
|
|
380
|
+
* Negation: {@link NegativeExpectations.toBeWithinRange `not.toBeWithinRange(...)`}
|
|
381
|
+
*/
|
|
382
|
+
toBeWithinRange(min: bigint, max: bigint): Expectations<bigint>
|
|
383
|
+
|
|
384
|
+
toBeWithinRange(min: number | bigint, max: number | bigint): Expectations {
|
|
385
|
+
if (max < min) {
|
|
386
|
+
const num = max
|
|
387
|
+
max = min
|
|
388
|
+
min = num
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
this.toBeA(typeof min)
|
|
392
|
+
|
|
393
|
+
if (((this.value as any) < min) || ((this.value as any) > max)) {
|
|
394
|
+
this._fail(`to be within ${stringifyValue(min)}...${stringifyValue(max)}`)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return this
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* ------------------------------------------------------------------------ */
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Expects the value to be _deep equal to_ the specified expected one.
|
|
404
|
+
*
|
|
405
|
+
* Negation: {@link NegativeExpectations.toEqual `not.toEqual(...)`}
|
|
406
|
+
*/
|
|
407
|
+
toEqual<Type>(expected: Type): Expectations<InferMatchers<Type>> {
|
|
408
|
+
if ((this.value as any) === expected) return this as Expectations<any>
|
|
409
|
+
|
|
410
|
+
const result = diff(this.value, expected)
|
|
411
|
+
|
|
412
|
+
if (result.diff) {
|
|
413
|
+
if (isMatcher(expected)) {
|
|
414
|
+
this._fail('to satisfy expectation matcher', result)
|
|
415
|
+
} else {
|
|
416
|
+
this._fail(`to equal ${stringifyValue(expected)}`, result)
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
return this as Expectations<any>
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ------------------------------------------------------------------------ */
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Expects the value to have a `number` _property_ `length` with the specified
|
|
427
|
+
* expected value.
|
|
428
|
+
*
|
|
429
|
+
* Negation: {@link NegativeExpectations.toHaveLength `not.toHaveLength(...)`}
|
|
430
|
+
*/
|
|
431
|
+
toHaveLength(length: number): Expectations<T & { length: number }> {
|
|
432
|
+
this.toBeDefined()
|
|
433
|
+
|
|
434
|
+
const realLength = (this.value as any)['length']
|
|
435
|
+
if (typeof realLength !== 'number') {
|
|
436
|
+
this._fail('to have a numeric "length" property')
|
|
437
|
+
} else if (realLength !== length) {
|
|
438
|
+
this._fail(`to have length ${length}`)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return this as Expectations<any>
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/* ------------------------------------------------------------------------ */
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Expects the value to have the specified _property_ and (if specified)
|
|
448
|
+
* further asserts its value with an {@link AssertionFunction}.
|
|
449
|
+
*
|
|
450
|
+
* Negation: {@link NegativeExpectations.toHaveProperty `not.toHaveProperty(...)`}
|
|
451
|
+
*/
|
|
452
|
+
toHaveProperty<
|
|
453
|
+
Prop extends string | number | symbol,
|
|
454
|
+
Assert extends AssertionFunction,
|
|
455
|
+
>(
|
|
456
|
+
property: Prop,
|
|
457
|
+
assertion?: Assert,
|
|
458
|
+
): Expectations<T & { [keyt in Prop] : AssertedType<unknown, Assert> }> {
|
|
459
|
+
this.toBeDefined()
|
|
460
|
+
|
|
461
|
+
const propertyValue = (this.value as any)[property]
|
|
462
|
+
|
|
463
|
+
if (propertyValue === undefined) {
|
|
464
|
+
this._fail(`to have property "${String(property)}"`)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (assertion) {
|
|
468
|
+
try {
|
|
469
|
+
const parent: ExpectationsParent = { expectations: this, prop: property }
|
|
470
|
+
const expectations = new Expectations(propertyValue, this.remarks, parent)
|
|
471
|
+
assertion(expectations)
|
|
472
|
+
} catch (error) {
|
|
473
|
+
// any caught error difference gets remapped as a property diff
|
|
474
|
+
if ((error instanceof ExpectationError) && (error.diff)) {
|
|
475
|
+
error.diff = {
|
|
476
|
+
diff: true,
|
|
477
|
+
value: this.value,
|
|
478
|
+
props: { [property]: error.diff },
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// re-throw
|
|
483
|
+
throw error
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return this as Expectations<any>
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/* ------------------------------------------------------------------------ */
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Expects the value to have a `number` _property_ `size` with the specified
|
|
493
|
+
* expected value.
|
|
494
|
+
*
|
|
495
|
+
* Negation: {@link NegativeExpectations.toHaveSize `not.toHaveSize(...)`}
|
|
496
|
+
*/
|
|
497
|
+
toHaveSize(size: number): Expectations<T & { size: number }> {
|
|
498
|
+
this.toBeDefined()
|
|
499
|
+
|
|
500
|
+
const realSize = (this.value as any)['size']
|
|
501
|
+
if (typeof realSize !== 'number') {
|
|
502
|
+
this._fail('to have a numeric "size" property')
|
|
503
|
+
} else if (realSize !== size) {
|
|
504
|
+
this._fail(`to have size ${size}`)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return this as Expectations<any>
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/* ------------------------------------------------------------------------ */
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Expect the value to include _all_ properties from the specified _object_.
|
|
514
|
+
*
|
|
515
|
+
* If the object being expected is a {@link Map}, the properties specified
|
|
516
|
+
* here will be treated as _mappings_ for said {@link Map}.
|
|
517
|
+
*
|
|
518
|
+
* Negation: {@link NegativeExpectations.toInclude `not.toInclude(...)`}
|
|
519
|
+
*/
|
|
520
|
+
toInclude<P extends Record<string, any>>(properties: P): Expectations<T>
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Expect the value to include _all_ mappings from the specified {@link Map}.
|
|
524
|
+
*
|
|
525
|
+
* Negation: {@link NegativeExpectations.toInclude `not.toInclude(...)`}
|
|
526
|
+
*/
|
|
527
|
+
toInclude(mappings: Map<any, any>): Expectations<T>
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Expect the value to be an {@link Iterable} object includind _all_ values
|
|
531
|
+
* from the specified {@link Set}, in any order.
|
|
532
|
+
*
|
|
533
|
+
* Negation: {@link NegativeExpectations.toInclude `not.toInclude(...)`}
|
|
534
|
+
*/
|
|
535
|
+
toInclude(entries: Set<any>): Expectations<T>
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Expect the value to be an {@link Iterable} object includind _all_ values
|
|
539
|
+
* from the specified _array_, in any order.
|
|
540
|
+
*
|
|
541
|
+
* Negation: {@link NegativeExpectations.toInclude `not.toInclude(...)`}
|
|
542
|
+
*/
|
|
543
|
+
toInclude(values: any[]): Expectations<T>
|
|
544
|
+
|
|
545
|
+
toInclude(
|
|
546
|
+
contents: Record<string, any> | Map<any, any> | Set<any> | any[],
|
|
547
|
+
): Expectations {
|
|
548
|
+
toInclude(this, false, contents)
|
|
549
|
+
return this
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/* ------------------------------------------------------------------------ */
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Expects the value to be a `string` _matching_ the specified sub-`string`
|
|
556
|
+
* or {@link RegExp}.
|
|
557
|
+
*
|
|
558
|
+
* Negation: {@link NegativeExpectations.toMatch `not.toMatch(...)`}
|
|
559
|
+
*/
|
|
560
|
+
toMatch<Matcher extends string | RegExp>(
|
|
561
|
+
matcher: Matcher,
|
|
562
|
+
): Expectations<string> {
|
|
563
|
+
const expectations = this.toBeA('string')
|
|
564
|
+
|
|
565
|
+
if (expectations.value.match(matcher)) return expectations
|
|
566
|
+
|
|
567
|
+
this._fail(`to match ${stringifyValue(matcher)}`)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* ------------------------------------------------------------------------ */
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Expect the value to be an {@link Iterable} object includind _all_ values
|
|
574
|
+
* (and only those values) from the specified _array_ or {@link Set},
|
|
575
|
+
* in any order.
|
|
576
|
+
*/
|
|
577
|
+
toMatchContents(contents: any[] | Set<any>): Expectations<T> {
|
|
578
|
+
toMatchContents(this, contents)
|
|
579
|
+
return this
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* ------------------------------------------------------------------------ */
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Expects the value to be _strictly equal to_ the specified expected one.
|
|
586
|
+
*
|
|
587
|
+
* Negation: {@link NegativeExpectations.toStrictlyEqual `not.toStrictlyEqual(...)`}
|
|
588
|
+
*/
|
|
589
|
+
toStrictlyEqual<Type>(expected: Type): Expectations<Type> {
|
|
590
|
+
if ((this.value as any) === expected) return this as Expectations<any>
|
|
591
|
+
|
|
592
|
+
const diff = { diff: true, value: this.value, expected }
|
|
593
|
+
this._fail(`to strictly equal ${stringifyValue(expected)}`, diff)
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* ------------------------------------------------------------------------ */
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Expects the value to be a `function` throwing, and (if specified) further
|
|
600
|
+
* asserts the thrown value with an {@link AssertionFunction}.
|
|
601
|
+
*
|
|
602
|
+
* Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
|
|
603
|
+
*/
|
|
604
|
+
toThrow(assert?: AssertionFunction): Expectations<() => any> {
|
|
605
|
+
const func = this.toBeA('function')
|
|
606
|
+
|
|
607
|
+
let passed = false
|
|
608
|
+
try {
|
|
609
|
+
func.value()
|
|
610
|
+
passed = true
|
|
611
|
+
} catch (thrown) {
|
|
612
|
+
if (assert) assert(new Expectations(thrown, this.remarks))
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (passed) this._fail('to throw')
|
|
616
|
+
return this as Expectations<any>
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* ------------------------------------------------------------------------ */
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Expects the value to be a `function` throwing an {@link Error}.
|
|
623
|
+
*
|
|
624
|
+
* If specified, the {@link Error}'s own message will be further expected to
|
|
625
|
+
* either match the specified {@link RegExp}, or equal to the specified
|
|
626
|
+
* `string`.
|
|
627
|
+
*
|
|
628
|
+
* Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
|
|
629
|
+
*/
|
|
630
|
+
toThrowError(
|
|
631
|
+
message?: string | RegExp
|
|
632
|
+
): Expectations<() => any>
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Expects the value to be a `function` throwing an instance of the
|
|
636
|
+
* {@link Error} identified by the specified {@link Constructor}.
|
|
637
|
+
*
|
|
638
|
+
* If specified, the {@link Error}'s own message will be further expected to
|
|
639
|
+
* either match the specified {@link RegExp}, or equal to the specified
|
|
640
|
+
* `string`.
|
|
641
|
+
*
|
|
642
|
+
* Negation: {@link NegativeExpectations.toThrow `not.toThrow()`}
|
|
643
|
+
*/
|
|
644
|
+
toThrowError<Class extends Constructor<Error>>(
|
|
645
|
+
constructor: Class,
|
|
646
|
+
message?: string | RegExp,
|
|
647
|
+
): Expectations<() => any>
|
|
648
|
+
|
|
649
|
+
toThrowError(
|
|
650
|
+
constructorOrMessage?: string | RegExp | Constructor,
|
|
651
|
+
maybeMessage?: string | RegExp,
|
|
652
|
+
): Expectations {
|
|
653
|
+
const [ constructor, message ] =
|
|
654
|
+
typeof constructorOrMessage === 'function' ?
|
|
655
|
+
[ constructorOrMessage, maybeMessage ] :
|
|
656
|
+
[ Error, constructorOrMessage ]
|
|
657
|
+
|
|
658
|
+
return this.toThrow((assert) =>
|
|
659
|
+
assert.toBeError(constructor, message))
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/* ========================================================================== *
|
|
664
|
+
* NEGATIVE EXPECTATIONS *
|
|
665
|
+
* ========================================================================== */
|
|
666
|
+
|
|
667
|
+
/** Negative expectations, as a subset of (meaningful) expectations. */
|
|
668
|
+
export class NegativeExpectations<T = unknown> {
|
|
669
|
+
/** For convenience, the value associated with the {@link Expectations} */
|
|
670
|
+
private readonly _value: T
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Create a {@link NegativeExpectations} instance associated with the
|
|
674
|
+
* specified (positive) {@link Expectations}.
|
|
675
|
+
*/
|
|
676
|
+
constructor(
|
|
677
|
+
/** The {@link Expectations} instance associated with this one */
|
|
678
|
+
private readonly _expectations: Expectations<T>,
|
|
679
|
+
) {
|
|
680
|
+
this._value = _expectations.value
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** Throw an {@link ExpectationError} associated with _this_ */
|
|
684
|
+
private _fail(details: string, diff?: Diff): never {
|
|
685
|
+
throw new ExpectationError(this._expectations, details, diff)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* ------------------------------------------------------------------------ */
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Expects the value _**NOT**_ to be of the specified _extended_
|
|
692
|
+
* {@link TypeName type}.
|
|
693
|
+
*
|
|
694
|
+
* Negates: {@link Expectations.toBeA `toBeA(...)`}
|
|
695
|
+
*/
|
|
696
|
+
toBeA(type: TypeName): Expectations<T> {
|
|
697
|
+
if (typeOf(this._value) !== type) return this._expectations
|
|
698
|
+
this._fail(`not to be ${prefixType(type)}`)
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/* ------------------------------------------------------------------------ */
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Expects the value to be a `number` _**OUTSIDE**_ of the given +/- _delta_
|
|
705
|
+
* range of the specified expected value.
|
|
706
|
+
*
|
|
707
|
+
* Negates: {@link Expectations.toBeCloseTo `toBeCloseTo(...)`}
|
|
708
|
+
*/
|
|
709
|
+
toBeCloseTo(value: number, delta: number): Expectations<number>
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Expects the value to be a `bigint` _**OUTSIDE**_ of the given +/- _delta_
|
|
713
|
+
* range of the specified expected value.
|
|
714
|
+
*
|
|
715
|
+
* Negates: {@link Expectations.toBeCloseTo `toBeCloseTo(...)`}
|
|
716
|
+
*/
|
|
717
|
+
toBeCloseTo(value: bigint, delta: bigint): Expectations<bigint>
|
|
718
|
+
|
|
719
|
+
toBeCloseTo(value: number | bigint, delta: number | bigint): Expectations {
|
|
720
|
+
const min = (value as number) - (delta as number)
|
|
721
|
+
const max = (value as number) + (delta as number)
|
|
722
|
+
return this.toBeWithinRange(min, max)
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/* ------------------------------------------------------------------------ */
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Expects the value to be either `null` or `undefined`.
|
|
729
|
+
*
|
|
730
|
+
* Negates: {@link Expectations.toBeDefined `toBeDefined()`}
|
|
731
|
+
*/
|
|
732
|
+
toBeDefined(): Expectations<null | undefined> {
|
|
733
|
+
if ((this._value === null) || (this._value === undefined)) {
|
|
734
|
+
return this._expectations as Expectations<any>
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
this._fail(`to be ${stringifyValue(null)} or ${stringifyValue(undefined)}`)
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/* ------------------------------------------------------------------------ */
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Expects the value _**NOT**_ to be an instance of the specified
|
|
744
|
+
* {@link Constructor}.
|
|
745
|
+
*
|
|
746
|
+
* Negates: {@link Expectations.toBeInstanceOf `toBeInstanceOf(...)`}
|
|
747
|
+
*/
|
|
748
|
+
toBeInstanceOf(constructor: Constructor): Expectations<T> {
|
|
749
|
+
if (this._value instanceof constructor) {
|
|
750
|
+
this._fail(`not to be an instance of ${stringifyConstructor(constructor)}`)
|
|
751
|
+
}
|
|
752
|
+
return this._expectations
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/* ------------------------------------------------------------------------ */
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Expects the value _**NOT**_ to be `NaN`.
|
|
759
|
+
*
|
|
760
|
+
* Negates: {@link Expectations.toBeNaN `toBeNaN()`}
|
|
761
|
+
*/
|
|
762
|
+
toBeNaN(): Expectations<number> {
|
|
763
|
+
const expectations = this._expectations.toBeA('number')
|
|
764
|
+
if (isNaN(expectations.value)) this._fail(`not to be ${stringifyValue(NaN)}`)
|
|
765
|
+
return expectations
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/* ------------------------------------------------------------------------ */
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Expects the value to be a `number` _**OUTSIDE**_ of the specified range
|
|
772
|
+
* where minimum and maximum values are inclusive.
|
|
773
|
+
*
|
|
774
|
+
* Negates: {@link Expectations.toBeWithinRange `toBeWithinRange(...)`}
|
|
775
|
+
*/
|
|
776
|
+
toBeWithinRange(min: number, max: number): Expectations<number>
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Expects the value to be a `bigint` _**OUTSIDE**_ of the specified range
|
|
780
|
+
* where minimum and maximum values are inclusive.
|
|
781
|
+
*
|
|
782
|
+
* Negates: {@link Expectations.toBeWithinRange `toBeWithinRange(...)`}
|
|
783
|
+
*/
|
|
784
|
+
toBeWithinRange(min: bigint, max: bigint): Expectations<bigint>
|
|
785
|
+
|
|
786
|
+
toBeWithinRange(min: number | bigint, max: number | bigint): Expectations {
|
|
787
|
+
if (max < min) {
|
|
788
|
+
const num = max
|
|
789
|
+
max = min
|
|
790
|
+
min = num
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
this._expectations.toBeA(typeof min)
|
|
794
|
+
|
|
795
|
+
if (((this._value as any) >= min) && ((this._value as any) <= max)) {
|
|
796
|
+
this._fail(`not to be within ${stringifyValue(min)}...${stringifyValue(max)}`)
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return this._expectations
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/* ------------------------------------------------------------------------ */
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Expects the value _**NOT**_ to be _deep equal to_ the specified expected
|
|
806
|
+
* one.
|
|
807
|
+
*
|
|
808
|
+
* Negates: {@link Expectations.toEqual `toEqual(...)`}
|
|
809
|
+
*/
|
|
810
|
+
toEqual(expected: any): Expectations<T> {
|
|
811
|
+
let result: Diff = { diff: false, value: this._value }
|
|
812
|
+
if (this._value !== expected) {
|
|
813
|
+
result = diff(this._value, expected)
|
|
814
|
+
if (result.diff) return this._expectations
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (isMatcher(expected)) {
|
|
818
|
+
this._fail('not to satisfy expectation matcher', result)
|
|
819
|
+
} else {
|
|
820
|
+
this._fail(`not to equal ${stringifyValue(expected)}`, result)
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/* ------------------------------------------------------------------------ */
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Expects the value to have a `number` _property_ `length` _different_ from
|
|
828
|
+
* the specified expected value.
|
|
829
|
+
*
|
|
830
|
+
* Negates: {@link Expectations.toHaveLength `toHaveLength(...)`}
|
|
831
|
+
*/
|
|
832
|
+
toHaveLength(length: number): Expectations<T & { length: number }> {
|
|
833
|
+
this._expectations.toBeDefined()
|
|
834
|
+
|
|
835
|
+
const realLength = (this._value as any)['length']
|
|
836
|
+
if (typeof realLength !== 'number') {
|
|
837
|
+
this._fail('to have a numeric "length" property')
|
|
838
|
+
} else if (realLength === length) {
|
|
839
|
+
this._fail(`not to have length ${length}`)
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return this._expectations as Expectations<any>
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* ------------------------------------------------------------------------ */
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* Expects the value _**NOT**_ to have the specified _property_.
|
|
849
|
+
*
|
|
850
|
+
* Negates: {@link Expectations.toHaveProperty `toHaveProperty(...)`}
|
|
851
|
+
*/
|
|
852
|
+
toHaveProperty(property: string | number | symbol): Expectations<T> {
|
|
853
|
+
this._expectations.toBeDefined()
|
|
854
|
+
|
|
855
|
+
const propertyValue = (this._value as any)[property]
|
|
856
|
+
if (propertyValue === undefined) return this._expectations
|
|
857
|
+
this._fail(`not to have property "${String(property)}"`)
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/* ------------------------------------------------------------------------ */
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Expects the value to have a `number` _property_ `size` _different_ from
|
|
864
|
+
* the specified expected value.
|
|
865
|
+
*
|
|
866
|
+
* Negates: {@link Expectations.toHaveSize `toHaveSize(...)`}
|
|
867
|
+
*/
|
|
868
|
+
toHaveSize(size: number): Expectations<T & { size: number }> {
|
|
869
|
+
this._expectations.toBeDefined()
|
|
870
|
+
|
|
871
|
+
const realSize = (this._value as any)['size']
|
|
872
|
+
if (typeof realSize !== 'number') {
|
|
873
|
+
this._fail('to have a numeric "size" property')
|
|
874
|
+
} else if (realSize === size) {
|
|
875
|
+
this._fail(`not to have size ${size}`)
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return this._expectations as Expectations<any>
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/* ------------------------------------------------------------------------ */
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Expect the value to include _none_ of the properties from the specified
|
|
885
|
+
* _object_.
|
|
886
|
+
*
|
|
887
|
+
* If the object being expected is a {@link Map}, the properties specified
|
|
888
|
+
* here will be treated as _mappings_ for said {@link Map}.
|
|
889
|
+
*
|
|
890
|
+
* Negates: {@link Expectations.toInclude `toInclude(...)`}
|
|
891
|
+
*/
|
|
892
|
+
toInclude<P extends Record<string, any>>(properties: P): Expectations<T>
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Expect the value to include _none_ of the mappings from the specified
|
|
896
|
+
* {@link Map}.
|
|
897
|
+
*
|
|
898
|
+
* Negates: {@link Expectations.toInclude `toInclude(...)`}
|
|
899
|
+
*/
|
|
900
|
+
toInclude(mappings: Map<any, any>): Expectations<T>
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Expect the value to be an {@link Iterable} object includind _none_ of the
|
|
904
|
+
* values from the specified {@link Set}.
|
|
905
|
+
*
|
|
906
|
+
* Negates: {@link Expectations.toInclude `toInclude(...)`}
|
|
907
|
+
*/
|
|
908
|
+
toInclude(entries: Set<any>): Expectations<T>
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Expect the value to be an {@link Iterable} object includind _none_ of the
|
|
912
|
+
* values from the specified _array_.
|
|
913
|
+
*
|
|
914
|
+
* Negates: {@link Expectations.toInclude `toInclude(...)`}
|
|
915
|
+
*/
|
|
916
|
+
toInclude(values: any[]): Expectations<T>
|
|
917
|
+
|
|
918
|
+
toInclude(
|
|
919
|
+
contents: Record<string, any> | Map<any, any> | Set<any> | any[],
|
|
920
|
+
): Expectations {
|
|
921
|
+
toInclude(this._expectations, true, contents)
|
|
922
|
+
return this._expectations
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/* ------------------------------------------------------------------------ */
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Expects the value to be a `string` _**NOT MATCHING**_ the specified
|
|
929
|
+
* sub-`string` or {@link RegExp}.
|
|
930
|
+
*
|
|
931
|
+
* Negates: {@link Expectations.toMatch `toMatch(...)`}
|
|
932
|
+
*/
|
|
933
|
+
toMatch(matcher: string | RegExp): Expectations<string> {
|
|
934
|
+
const expectations = this._expectations.toBeA('string')
|
|
935
|
+
|
|
936
|
+
if (! expectations.value.match(matcher)) return expectations
|
|
937
|
+
|
|
938
|
+
this._fail(`not to match ${stringifyValue(matcher)}`)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/* ------------------------------------------------------------------------ */
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Expects the value to be a `function` not throwing anything.
|
|
945
|
+
*
|
|
946
|
+
* Negates: {@link Expectations.toThrow `toThrow(...)`}
|
|
947
|
+
*/
|
|
948
|
+
toThrow(): Expectations<() => any> {
|
|
949
|
+
const expectations = this._expectations.toBeA('function')
|
|
950
|
+
|
|
951
|
+
try {
|
|
952
|
+
expectations.value()
|
|
953
|
+
return expectations
|
|
954
|
+
} catch (caught) {
|
|
955
|
+
this._fail('not to throw')
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/* ------------------------------------------------------------------------ */
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Expects the value _**NOT**_ to be _strictly equal to_ the specified
|
|
963
|
+
* expected one.
|
|
964
|
+
*
|
|
965
|
+
* Negates: {@link Expectations.toStrictlyEqual `toStrictlyEqual(...)`}
|
|
966
|
+
*/
|
|
967
|
+
toStrictlyEqual(expected: any): Expectations<T> {
|
|
968
|
+
if (this._value !== expected) return this._expectations
|
|
969
|
+
this._fail(`not to strictly equal ${stringifyValue(expected)}`)
|
|
970
|
+
}
|
|
971
|
+
}
|