@plugjs/expect5 0.4.1 → 0.4.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/dist/cli.mjs +1 -1
- package/dist/expectation/async.cjs +73 -0
- package/dist/expectation/async.cjs.map +6 -0
- package/dist/expectation/async.d.ts +54 -0
- package/dist/expectation/async.mjs +46 -0
- package/dist/expectation/async.mjs.map +6 -0
- package/dist/expectation/basic.cjs +155 -183
- package/dist/expectation/basic.cjs.map +1 -1
- package/dist/expectation/basic.d.ts +90 -47
- package/dist/expectation/basic.mjs +142 -163
- package/dist/expectation/basic.mjs.map +1 -1
- package/dist/expectation/diff.cjs +7 -7
- package/dist/expectation/diff.cjs.map +1 -1
- package/dist/expectation/diff.mjs +7 -7
- package/dist/expectation/diff.mjs.map +1 -1
- package/dist/expectation/expect.cjs +94 -108
- package/dist/expectation/expect.cjs.map +2 -2
- package/dist/expectation/expect.d.ts +103 -130
- package/dist/expectation/expect.mjs +131 -137
- package/dist/expectation/expect.mjs.map +2 -2
- package/dist/expectation/include.cjs +50 -61
- package/dist/expectation/include.cjs.map +1 -1
- package/dist/expectation/include.d.ts +19 -10
- package/dist/expectation/include.mjs +53 -57
- package/dist/expectation/include.mjs.map +1 -1
- package/dist/expectation/throwing.cjs +27 -27
- package/dist/expectation/throwing.cjs.map +1 -1
- package/dist/expectation/throwing.d.ts +36 -8
- package/dist/expectation/throwing.mjs +26 -26
- package/dist/expectation/throwing.mjs.map +1 -1
- package/dist/expectation/trivial.cjs +96 -0
- package/dist/expectation/trivial.cjs.map +6 -0
- package/dist/expectation/trivial.d.ts +13 -0
- package/dist/expectation/trivial.mjs +61 -0
- package/dist/expectation/trivial.mjs.map +6 -0
- package/dist/expectation/types.cjs +9 -12
- package/dist/expectation/types.cjs.map +1 -1
- package/dist/expectation/types.d.ts +52 -10
- package/dist/expectation/types.mjs +8 -10
- package/dist/expectation/types.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/expectation/async.ts +151 -0
- package/src/expectation/basic.ts +356 -156
- package/src/expectation/diff.ts +8 -9
- package/src/expectation/expect.ts +239 -268
- package/src/expectation/include.ts +93 -59
- package/src/expectation/throwing.ts +102 -41
- package/src/expectation/trivial.ts +107 -0
- package/src/expectation/types.ts +82 -25
- package/src/index.ts +2 -0
- package/dist/expectation/void.cjs +0 -111
- package/dist/expectation/void.cjs.map +0 -6
- package/dist/expectation/void.d.ts +0 -39
- package/dist/expectation/void.mjs +0 -77
- package/dist/expectation/void.mjs.map +0 -6
- package/src/expectation/void.ts +0 -80
|
@@ -1,69 +1,103 @@
|
|
|
1
1
|
import { diff } from './diff'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ExpectationError,
|
|
4
|
+
stringifyObjectType,
|
|
5
|
+
stringifyValue,
|
|
6
|
+
} from './types'
|
|
3
7
|
|
|
4
8
|
import type { Diff } from './diff'
|
|
5
|
-
import type {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
import type { Expectations } from './expect'
|
|
10
|
+
import type { ExpectationsContext } from './types'
|
|
11
|
+
|
|
12
|
+
/* === TO INCLUDE =========================================================== */
|
|
13
|
+
|
|
14
|
+
/** Expect the value to include _all_ properties from the specified _object_. */
|
|
15
|
+
function toInclude<T, P extends Record<string, any>>(this: T, properties: P): T
|
|
16
|
+
/** Expect the value to include _all_ mappings from the specified {@link Map}. */
|
|
17
|
+
function toInclude<T>(this: T, mappings: Map<any, any>): T
|
|
18
|
+
/** Expect the value to include _all_ values from the specified {@link Set}. */
|
|
19
|
+
function toInclude<T>(this: T, entries: Set<any>): T
|
|
20
|
+
/** Expect the value to include _all_ values in any order from the specified _array_. */
|
|
21
|
+
function toInclude<T>(this: T, values: any[]): T
|
|
22
|
+
|
|
23
|
+
/* Overloaded function implementation */
|
|
24
|
+
function toInclude(
|
|
25
|
+
this: ExpectationsContext,
|
|
26
|
+
expected:
|
|
27
|
+
| Record<string, any>
|
|
28
|
+
| Map<any, any>
|
|
29
|
+
| Set<any>
|
|
30
|
+
| any [],
|
|
31
|
+
): Expectations {
|
|
32
|
+
// get diff depending on type of "expected"
|
|
33
|
+
if (expected instanceof Map) return includesMappings(this, expected)
|
|
34
|
+
if (expected instanceof Set) return includesValues(this, expected)
|
|
35
|
+
if (Array.isArray(expected)) return includesValues(this, new Set(expected))
|
|
36
|
+
if (expected instanceof Object) return includesProps(this, expected)
|
|
37
|
+
throw new TypeError(`Invalid type for "toInclude(...)": ${stringifyValue(expected)}`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* === TO MATCH CONTENTS ==================================================== */
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Expect the value to include _all_ values (and only those values, in any
|
|
44
|
+
* order) from the specified _array_.
|
|
45
|
+
*/
|
|
46
|
+
function toMatchContents<T>(this: T, contents: any[]): T
|
|
47
|
+
/**
|
|
48
|
+
* Expect the value to include _all_ values (and only those values, in any
|
|
49
|
+
* order) from the specified {@link Set}.
|
|
50
|
+
*/
|
|
51
|
+
function toMatchContents<T>(this: T, contents: Set<any>): T
|
|
52
|
+
|
|
53
|
+
/* Overloaded function implementation */
|
|
54
|
+
function toMatchContents(
|
|
55
|
+
this: ExpectationsContext,
|
|
56
|
+
contents: any[] | Set<any>,
|
|
57
|
+
): Expectations {
|
|
58
|
+
let actual: Set<any>
|
|
59
|
+
let expected: Set<any>
|
|
60
|
+
try {
|
|
61
|
+
actual = new Set(this.value as any)
|
|
62
|
+
expected = new Set(contents)
|
|
63
|
+
} catch (error) {
|
|
64
|
+
throw new ExpectationError(this, 'to be an iterable object', false)
|
|
23
65
|
}
|
|
66
|
+
|
|
67
|
+
const result = diff(actual, expected)
|
|
68
|
+
delete result.error // remove extra error message about size differences...
|
|
69
|
+
if (result.diff === this.negative) return this.expects
|
|
70
|
+
throw new ExpectationError(this,
|
|
71
|
+
`to match contents of ${stringifyObjectType(contents)}`,
|
|
72
|
+
{ ...result, value: this.value })
|
|
24
73
|
}
|
|
25
74
|
|
|
26
|
-
|
|
27
|
-
expect(
|
|
28
|
-
context: Expectations,
|
|
29
|
-
negative: boolean,
|
|
30
|
-
contents: any[] | Set<any>,
|
|
31
|
-
): void {
|
|
32
|
-
let actual: Set<any>
|
|
33
|
-
let expected: Set<any>
|
|
34
|
-
try {
|
|
35
|
-
actual = new Set(context.value as any)
|
|
36
|
-
expected = new Set(contents)
|
|
37
|
-
} catch (error) {
|
|
38
|
-
throw new ExpectationError(context, false, 'to be an iterable object')
|
|
39
|
-
}
|
|
75
|
+
/* === EXPORTS ============================================================== */
|
|
40
76
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
throw new ExpectationError(
|
|
45
|
-
context,
|
|
46
|
-
negative,
|
|
47
|
-
`to match contents of ${stringifyObjectType(contents)}`,
|
|
48
|
-
{ ...result, value: context.value })
|
|
49
|
-
}
|
|
77
|
+
export {
|
|
78
|
+
toInclude,
|
|
79
|
+
toMatchContents,
|
|
50
80
|
}
|
|
51
81
|
|
|
52
|
-
|
|
82
|
+
/* ========================================================================== *
|
|
83
|
+
* INTERNALS *
|
|
84
|
+
* ========================================================================== */
|
|
85
|
+
|
|
86
|
+
function includesProps(context: ExpectationsContext, expected: Record<string, any>): Expectations {
|
|
53
87
|
// simple include for maps with objects...
|
|
54
88
|
if (context.value instanceof Map) {
|
|
55
|
-
return includesMappings(context,
|
|
89
|
+
return includesMappings(context, new Map(Object.entries(expected)))
|
|
56
90
|
}
|
|
57
91
|
|
|
58
92
|
// we really need an object as actual
|
|
59
|
-
context.toBeInstanceOf(Object)
|
|
93
|
+
context.expects.toBeInstanceOf(Object)
|
|
60
94
|
const actual: Record<string, any> = context.value as any
|
|
61
95
|
|
|
62
96
|
// get expected key set and process...
|
|
63
97
|
const keys = new Set(Object.keys(expected))
|
|
64
98
|
const props: Record<string, Diff> = {}
|
|
65
99
|
|
|
66
|
-
if (negative) {
|
|
100
|
+
if (context.negative) {
|
|
67
101
|
// only consider keys... if they exist, fail!
|
|
68
102
|
for (const key of keys) {
|
|
69
103
|
if ((actual[key] !== undefined) || (key in actual)) {
|
|
@@ -88,27 +122,27 @@ export function includesProps(context: Expectations, negative: boolean, expected
|
|
|
88
122
|
}
|
|
89
123
|
|
|
90
124
|
const count = Object.keys(props).length
|
|
91
|
-
if (count === 0) return // no props? no errors!
|
|
125
|
+
if (count === 0) return context.expects // no props? no errors!
|
|
92
126
|
|
|
93
127
|
const type = count === 1 ? 'property' : 'properties'
|
|
94
|
-
throw new ExpectationError(context,
|
|
128
|
+
throw new ExpectationError(context, `to include ${count} ${type}`, {
|
|
95
129
|
diff: true,
|
|
96
130
|
value: actual,
|
|
97
131
|
props,
|
|
98
132
|
})
|
|
99
133
|
}
|
|
100
134
|
|
|
101
|
-
|
|
135
|
+
function includesValues(context: ExpectationsContext, expected: Set<any>): Expectations {
|
|
102
136
|
// we really need an _iterable_ object as actual
|
|
103
|
-
context.toBeInstanceOf(Object)
|
|
137
|
+
context.expects.toBeInstanceOf(Object)
|
|
104
138
|
if (typeof (context.value as any)[Symbol.iterator] !== 'function') {
|
|
105
|
-
throw new ExpectationError(context,
|
|
139
|
+
throw new ExpectationError(context, 'to be an iterable object', false)
|
|
106
140
|
}
|
|
107
141
|
const actual = new Set(context.value as Iterable<any>)
|
|
108
142
|
|
|
109
143
|
// iterate through all the values and see what we can find
|
|
110
144
|
const values: Diff[] = []
|
|
111
|
-
if (negative) {
|
|
145
|
+
if (context.negative) {
|
|
112
146
|
for (const exp of expected) {
|
|
113
147
|
for (const act of actual) {
|
|
114
148
|
const result = diff(act, exp)
|
|
@@ -136,25 +170,25 @@ export function includesValues(context: Expectations, negative: boolean, expecte
|
|
|
136
170
|
}
|
|
137
171
|
|
|
138
172
|
const count = values.length
|
|
139
|
-
if (count === 0) return // no values? no errors!
|
|
173
|
+
if (count === 0) return context.expects // no values? no errors!
|
|
140
174
|
|
|
141
175
|
const type = count === 1 ? 'value' : 'values'
|
|
142
|
-
throw new ExpectationError(context,
|
|
176
|
+
throw new ExpectationError(context, `to include ${count} ${type}`, {
|
|
143
177
|
diff: true,
|
|
144
178
|
value: context.value,
|
|
145
179
|
values,
|
|
146
180
|
})
|
|
147
181
|
}
|
|
148
182
|
|
|
149
|
-
|
|
150
|
-
context.toBeInstanceOf(Map)
|
|
183
|
+
function includesMappings(context: ExpectationsContext, expected: Map<any, any>): Expectations {
|
|
184
|
+
context.expects.toBeInstanceOf(Map)
|
|
151
185
|
const actual: Map<any, any> = context.value as any
|
|
152
186
|
|
|
153
187
|
// Get expected key set and process...
|
|
154
188
|
const keys = new Set(expected.keys())
|
|
155
189
|
const mappings: [ string, Diff ][] = []
|
|
156
190
|
|
|
157
|
-
if (negative) {
|
|
191
|
+
if (context.negative) {
|
|
158
192
|
// only consider keys... if they exist, fail!
|
|
159
193
|
for (const key of keys) {
|
|
160
194
|
if (actual.has(key)) {
|
|
@@ -173,10 +207,10 @@ export function includesMappings(context: Expectations, negative: boolean, expec
|
|
|
173
207
|
}
|
|
174
208
|
|
|
175
209
|
const count = mappings.length
|
|
176
|
-
if (count === 0) return // no mappings? no errors!
|
|
210
|
+
if (count === 0) return context.expects // no mappings? no errors!
|
|
177
211
|
|
|
178
212
|
const type = count === 1 ? 'mapping' : 'mappings'
|
|
179
|
-
throw new ExpectationError(context,
|
|
213
|
+
throw new ExpectationError(context, `to include ${count} ${type}`, {
|
|
180
214
|
diff: true,
|
|
181
215
|
value: context.value,
|
|
182
216
|
mappings,
|
|
@@ -1,45 +1,106 @@
|
|
|
1
|
-
import { ExpectationError,
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
1
|
+
import { ExpectationError, assertContextType } from './types'
|
|
2
|
+
|
|
3
|
+
import type { Expectations } from './expect'
|
|
4
|
+
import type {
|
|
5
|
+
AssertionFunction,
|
|
6
|
+
Constructor,
|
|
7
|
+
ExpectationsContext,
|
|
8
|
+
JoinExpectations,
|
|
9
|
+
} from './types'
|
|
10
|
+
|
|
11
|
+
/* === TO THROW ============================================================= */
|
|
12
|
+
|
|
13
|
+
/** Expects the value to be a `function` throwing _anything_. */
|
|
14
|
+
function toThrow<T>(this: T): JoinExpectations<T, Function>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Expects the value to be a `function` throwing, and asserts the
|
|
18
|
+
* thrown value with the specified callback.
|
|
19
|
+
*/
|
|
20
|
+
function toThrow<T>(this: T, assert: AssertionFunction): JoinExpectations<T, Function>
|
|
21
|
+
|
|
22
|
+
/* Overloaded function implementation */
|
|
23
|
+
function toThrow(
|
|
24
|
+
this: ExpectationsContext,
|
|
25
|
+
assert?: AssertionFunction,
|
|
26
|
+
): Expectations {
|
|
27
|
+
assertContextType(this, 'function')
|
|
28
|
+
|
|
29
|
+
let thrown: boolean
|
|
30
|
+
let error: unknown
|
|
31
|
+
try {
|
|
32
|
+
this.value()
|
|
33
|
+
thrown = false
|
|
34
|
+
error = undefined
|
|
35
|
+
} catch (caught) {
|
|
36
|
+
thrown = true
|
|
37
|
+
error = caught
|
|
30
38
|
}
|
|
31
|
-
}
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
...args:
|
|
38
|
-
| []
|
|
39
|
-
| [ message: StringMatcher ]
|
|
40
|
-
| [ constructor: Constructor<Error> ]
|
|
41
|
-
| [ constructor: Constructor<Error>, message: StringMatcher ]
|
|
42
|
-
): void {
|
|
43
|
-
context.negated(negative).toThrow((assert) => assert.toBeError(...args))
|
|
40
|
+
if (thrown === this.negative) {
|
|
41
|
+
throw new ExpectationError(this, 'to throw')
|
|
42
|
+
} else if (thrown && assert) {
|
|
43
|
+
assert(this.forValue(error))
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
return this.expects
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* === TO THROW ERROR ======================================================= */
|
|
50
|
+
|
|
51
|
+
/** Expects the value to be a `function` throwing an {@link Error}. */
|
|
52
|
+
function toThrowError<T>(this: T): JoinExpectations<T, Function>
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Expects the value to be a `function` throwing an {@link Error} with the
|
|
56
|
+
* specified _message_.
|
|
57
|
+
*/
|
|
58
|
+
function toThrowError<T>(this: T, message: string): JoinExpectations<T, Function>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Expects the value to be a `function` throwing an {@link Error} with its
|
|
62
|
+
* _message_ matching the specified {@link RegExp}.
|
|
63
|
+
*/
|
|
64
|
+
function toThrowError<T>(this: T, expession: RegExp): JoinExpectations<T, Function>
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Expects the value to be a `function` throwing an {@link Error} of the
|
|
68
|
+
* specified _type_.
|
|
69
|
+
*/
|
|
70
|
+
function toThrowError<T>(this: T, constructor: Constructor<Error>): JoinExpectations<T, Function>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Expects the value to be a `function` throwing an {@link Error} of the
|
|
74
|
+
* specified _type_ with the specified _message_.
|
|
75
|
+
*/
|
|
76
|
+
function toThrowError<T>(this: T, constructor: Constructor<Error>, message: string): JoinExpectations<T, Function>
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Expects the value to be a `function` throwing an {@link Error} of the
|
|
80
|
+
* specified _type_ with its _message_ matching the specified {@link RegExp}.
|
|
81
|
+
*/
|
|
82
|
+
function toThrowError<T>(this: T, constructor: Constructor<Error>, expression: RegExp): JoinExpectations<T, Function>
|
|
83
|
+
|
|
84
|
+
/* Overloaded function implementation */
|
|
85
|
+
function toThrowError(
|
|
86
|
+
this: ExpectationsContext,
|
|
87
|
+
...args:
|
|
88
|
+
| []
|
|
89
|
+
| [ string ]
|
|
90
|
+
| [ RegExp ]
|
|
91
|
+
| [ Constructor<Error> ]
|
|
92
|
+
| [ Constructor<Error>, string ]
|
|
93
|
+
| [ Constructor<Error>, RegExp ]
|
|
94
|
+
): Expectations {
|
|
95
|
+
return this.negated.toThrow((assert) =>
|
|
96
|
+
// @ts-ignore // can't reconcile the types with overloads...
|
|
97
|
+
assert.toBeError(...args))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* === EXPORTS ============================================================== */
|
|
101
|
+
|
|
102
|
+
/* coverage ignore next */
|
|
103
|
+
export {
|
|
104
|
+
toThrow,
|
|
105
|
+
toThrowError,
|
|
45
106
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ExpectationError, stringifyValue } from './types'
|
|
2
|
+
|
|
3
|
+
import type { Expectations } from './expect'
|
|
4
|
+
import type { ExpectationsContext } from './types'
|
|
5
|
+
|
|
6
|
+
/* Expects the value to be _defined_ (that is not `null` nor `undefined`). */
|
|
7
|
+
function toBeDefined<T>(this: T): T
|
|
8
|
+
function toBeDefined(this: ExpectationsContext): Expectations {
|
|
9
|
+
return check(this, 'to be defined', (value) => (value !== null) && (value !== undefined))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Expects the value strictly equal to `false`. */
|
|
13
|
+
function toBeFalse(): Expectations<false>
|
|
14
|
+
function toBeFalse(this: ExpectationsContext): Expectations {
|
|
15
|
+
return check(this, `to be ${stringifyValue(false)}`, (value) => value === false)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Expects the value to be _falsy_ (zero, empty string, `false`, ...). */
|
|
19
|
+
function toBeFalsy<T>(this: T): T
|
|
20
|
+
function toBeFalsy(this: ExpectationsContext): Expectations {
|
|
21
|
+
return check(this, 'to be falsy', (value) => ! value)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Expects the value to be `NaN`. */
|
|
25
|
+
function toBeNaN(): Expectations<number>
|
|
26
|
+
function toBeNaN(this: ExpectationsContext): Expectations {
|
|
27
|
+
return check(this, `to be ${stringifyValue(NaN)}`, (value) => (typeof value === 'number') && isNaN(value))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* Expects the value to strictly equal to `-Infinity` (negative infinity). */
|
|
31
|
+
function toBeNegativeInfinity(): Expectations<number>
|
|
32
|
+
function toBeNegativeInfinity(this: ExpectationsContext): Expectations {
|
|
33
|
+
return check(this, `to equal ${stringifyValue(Number.NEGATIVE_INFINITY)}`, (value) => value === Number.NEGATIVE_INFINITY)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Expects the value to strictly equal to `null`. */
|
|
37
|
+
function toBeNull(): Expectations<null>
|
|
38
|
+
function toBeNull(this: ExpectationsContext): Expectations {
|
|
39
|
+
return check(this, `to be ${stringifyValue(null)}`, (value) => value === null)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Expects the value to strictly equal to `null` or `undefined`. */
|
|
43
|
+
function toBeNullable(): Expectations<null | undefined>
|
|
44
|
+
function toBeNullable(this: ExpectationsContext): Expectations {
|
|
45
|
+
return check(
|
|
46
|
+
this,
|
|
47
|
+
`to be ${stringifyValue(null)} or ${stringifyValue(undefined)}`,
|
|
48
|
+
(value) => ((value === null) || (value === undefined)))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Expects the value to strictly equal to `+Infinity` (positive infinity). */
|
|
52
|
+
function toBePositiveInfinity(): Expectations<number>
|
|
53
|
+
function toBePositiveInfinity(this: ExpectationsContext): Expectations {
|
|
54
|
+
return check(this, `to equal ${stringifyValue(Number.POSITIVE_INFINITY)}`, (value) => value === Number.POSITIVE_INFINITY)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Expects the value to strictly equal to `true`. */
|
|
58
|
+
function toBeTrue(): Expectations<true>
|
|
59
|
+
function toBeTrue(this: ExpectationsContext): Expectations {
|
|
60
|
+
return check(this, `to be ${stringifyValue(true)}`, (value) => value === true)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Expects the value to be _falsy_ (non-zero, non-empty string, `true`, ...). */
|
|
64
|
+
function toBeTruthy<T>(this: T): T
|
|
65
|
+
function toBeTruthy(this:ExpectationsContext): Expectations {
|
|
66
|
+
return check(this, 'to be truthy', (value) => !! value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Expects the value to strictly equal to `undefined`. */
|
|
70
|
+
function toBeUndefined(): Expectations<undefined>
|
|
71
|
+
function toBeUndefined(this: ExpectationsContext): Expectations {
|
|
72
|
+
return check(this, `to be ${stringifyValue(undefined)}`, (value) => value === undefined)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* === EXPORTS ============================================================== */
|
|
76
|
+
|
|
77
|
+
/* coverage ignore next */
|
|
78
|
+
export {
|
|
79
|
+
toBeDefined,
|
|
80
|
+
toBeFalse,
|
|
81
|
+
toBeFalsy,
|
|
82
|
+
toBeNaN,
|
|
83
|
+
toBeNegativeInfinity,
|
|
84
|
+
toBeNull,
|
|
85
|
+
toBeNullable,
|
|
86
|
+
toBePositiveInfinity,
|
|
87
|
+
toBeTrue,
|
|
88
|
+
toBeTruthy,
|
|
89
|
+
toBeUndefined,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* ========================================================================== *
|
|
93
|
+
* INTERNALS *
|
|
94
|
+
* ========================================================================== */
|
|
95
|
+
|
|
96
|
+
function check(
|
|
97
|
+
context: ExpectationsContext,
|
|
98
|
+
details: string,
|
|
99
|
+
cb: (value: unknown) => boolean,
|
|
100
|
+
): Expectations {
|
|
101
|
+
const match = cb(context.value)
|
|
102
|
+
if (match === context.negative) {
|
|
103
|
+
throw new ExpectationError(context, details)
|
|
104
|
+
} else {
|
|
105
|
+
return context.expects
|
|
106
|
+
}
|
|
107
|
+
}
|
package/src/expectation/types.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Diff } from './diff'
|
|
2
|
-
import type { Expectations,
|
|
2
|
+
import type { Expectations, Matchers, ExpectationFunctions } from './expect'
|
|
3
|
+
|
|
4
|
+
/* ========================================================================== *
|
|
5
|
+
* INTERNAL TYPES FOR EXPECTATIONS *
|
|
6
|
+
* ========================================================================== */
|
|
3
7
|
|
|
4
8
|
/** A type identifying any constructor */
|
|
5
9
|
export type Constructor<T = any> = new (...args: any[]) => T
|
|
@@ -14,11 +18,6 @@ export type NonArrayObject<T = any> = {
|
|
|
14
18
|
[c: number]: never
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
/** A type identifying the parameter of `string.match(...)` */
|
|
18
|
-
export type StringMatcher = string | RegExp | {
|
|
19
|
-
[Symbol.match](string: string): RegExpMatchArray | null
|
|
20
|
-
}
|
|
21
|
-
|
|
22
21
|
/** Mappings for our _expanded_ {@link typeOf} implementation */
|
|
23
22
|
export type TypeMappings = {
|
|
24
23
|
// standard types, from "typeof"
|
|
@@ -45,6 +44,45 @@ export type TypeMappings = {
|
|
|
45
44
|
/** Values returned by our own _expanded_ `{@link typeOf}` */
|
|
46
45
|
export type TypeName = keyof TypeMappings
|
|
47
46
|
|
|
47
|
+
/** Join the asserted type of an {@link Expectations} with another type */
|
|
48
|
+
export type JoinExpectations<E, T2> =
|
|
49
|
+
E extends Expectations<infer T1> ? Expectations<T1 & T2> : Expectations<T2>
|
|
50
|
+
|
|
51
|
+
/** An assertion function, for sub-expectations and value introspection */
|
|
52
|
+
export type AssertionFunction<T = unknown> = (assert: Expectations<T>) => void | Expectations
|
|
53
|
+
|
|
54
|
+
/** Get the type asserted by an {@link AssertionFunction} */
|
|
55
|
+
export type AssertedType<F extends AssertionFunction<any>, R = ReturnType<F>> =
|
|
56
|
+
R extends Expectations<infer T> ? T : unknown
|
|
57
|
+
|
|
58
|
+
/** Internal context used by expectations functions to operate */
|
|
59
|
+
export interface ExpectationsContext<T = unknown> {
|
|
60
|
+
/** The value being expected */
|
|
61
|
+
readonly value: T,
|
|
62
|
+
/** Whether this is a negative or positive expectation */
|
|
63
|
+
readonly negative: boolean,
|
|
64
|
+
/** The optional parent of this instance, when constructed for a property */
|
|
65
|
+
readonly parent?: ExpectationsParent
|
|
66
|
+
/** The current _positive_ {@link Expectations} for the value */
|
|
67
|
+
readonly expects: Expectations<T>
|
|
68
|
+
/**
|
|
69
|
+
* If _negative_, the _negative_ {@link ExpectationFunctions} for the value,
|
|
70
|
+
* otherwise the _positive_ ones (basically, follow the `not` of `expect`).
|
|
71
|
+
*/
|
|
72
|
+
readonly negated: ExpectationFunctions<T>
|
|
73
|
+
|
|
74
|
+
/** Create an {@link Expectation} instance for the specified value */
|
|
75
|
+
forValue<V>(value: V): Expectations<V>,
|
|
76
|
+
/** Create an {@link Expectation} instance for a property of this value */
|
|
77
|
+
forProperty(prop: string | number | symbol): Expectations
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Parent expectations context (for properties) */
|
|
81
|
+
export interface ExpectationsParent {
|
|
82
|
+
context: ExpectationsContext,
|
|
83
|
+
prop: string | number | symbol,
|
|
84
|
+
}
|
|
85
|
+
|
|
48
86
|
/* ========================================================================== *
|
|
49
87
|
* TYPE INSPECTION, GUARD, AND ASSERTION *
|
|
50
88
|
* ========================================================================== */
|
|
@@ -81,24 +119,14 @@ export function typeOf(value: unknown): TypeName {
|
|
|
81
119
|
return 'object'
|
|
82
120
|
}
|
|
83
121
|
|
|
84
|
-
/** Determines if the specified `value` is of the specified _expanded_ `type` */
|
|
85
|
-
export function isType<T extends keyof TypeMappings>(
|
|
86
|
-
context: Expectations,
|
|
87
|
-
type: T,
|
|
88
|
-
): context is Expectations<TypeMappings[T]> {
|
|
89
|
-
return typeOf(context.value) === type
|
|
90
|
-
}
|
|
91
|
-
|
|
92
122
|
/** Asserts that the specified `value` is of the specified _expanded_ `type` */
|
|
93
|
-
export function
|
|
94
|
-
context:
|
|
123
|
+
export function assertContextType<T extends keyof TypeMappings>(
|
|
124
|
+
context: ExpectationsContext,
|
|
95
125
|
type: T,
|
|
96
|
-
): asserts context is
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (typeOf(value) === type) return
|
|
126
|
+
): asserts context is ExpectationsContext<TypeMappings[T]> {
|
|
127
|
+
if (typeOf(context.value) === type) return
|
|
100
128
|
|
|
101
|
-
throw new ExpectationError(context,
|
|
129
|
+
throw new ExpectationError(context, `to be ${prefixType(type)}`, false)
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
/* ========================================================================== *
|
|
@@ -215,7 +243,7 @@ export function prefixType(type: TypeName): string {
|
|
|
215
243
|
|
|
216
244
|
export const matcherMarker = Symbol.for('plugjs:expect5:types:ExpectationsMatcher')
|
|
217
245
|
|
|
218
|
-
export function isMatcher(what: any): what is
|
|
246
|
+
export function isMatcher(what: any): what is Matchers {
|
|
219
247
|
return what && what[matcherMarker] === matcherMarker
|
|
220
248
|
}
|
|
221
249
|
|
|
@@ -226,12 +254,41 @@ export function isMatcher(what: any): what is ExpectationsMatcher {
|
|
|
226
254
|
export class ExpectationError extends Error {
|
|
227
255
|
diff?: Diff | undefined
|
|
228
256
|
|
|
257
|
+
/** Create an {@link ExpectationError} from a context and details message */
|
|
258
|
+
constructor(context: ExpectationsContext, details: string)
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create an {@link ExpectationError} from a context and details message,
|
|
262
|
+
* including an optional {@link Diff}
|
|
263
|
+
*/
|
|
264
|
+
constructor(context: ExpectationsContext, details: string, diff?: Diff)
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Create an {@link ExpectationError} from a context and details message,
|
|
268
|
+
* optionally forcing _negation_ to be as specified.
|
|
269
|
+
*/
|
|
270
|
+
constructor(context: ExpectationsContext, details: string, forcedNegative?: boolean)
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Create an {@link ExpectationError} from a context and details message,
|
|
274
|
+
* including an optional {@link Diff} and forcing _negation_ to be as
|
|
275
|
+
* specified.
|
|
276
|
+
*/
|
|
277
|
+
constructor(context: ExpectationsContext, details: string, diff?: Diff, forcedNegative?: boolean)
|
|
278
|
+
|
|
279
|
+
/* Overloaded constructor implementation */
|
|
229
280
|
constructor(
|
|
230
|
-
context:
|
|
231
|
-
negative: boolean,
|
|
281
|
+
context: ExpectationsContext,
|
|
232
282
|
details: string,
|
|
233
|
-
|
|
283
|
+
diffOrForcedNegative?: Diff | boolean,
|
|
284
|
+
maybeForcedNegative?: boolean,
|
|
234
285
|
) {
|
|
286
|
+
const diff = typeof diffOrForcedNegative === 'object' ? diffOrForcedNegative : null
|
|
287
|
+
const negative =
|
|
288
|
+
typeof diffOrForcedNegative === 'boolean' ? diffOrForcedNegative :
|
|
289
|
+
typeof maybeForcedNegative === 'boolean' ? maybeForcedNegative :
|
|
290
|
+
context.negative
|
|
291
|
+
|
|
235
292
|
const { value } = context
|
|
236
293
|
const not = negative ? ' not' : ''
|
|
237
294
|
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,8 @@ export {
|
|
|
16
16
|
export { skip } from './execution/executable'
|
|
17
17
|
export { expect } from './expectation/expect'
|
|
18
18
|
|
|
19
|
+
export type { Expectations } from './expectation/expect'
|
|
20
|
+
|
|
19
21
|
/* ========================================================================== *
|
|
20
22
|
* EXPORTED OPTIONS TYPE AND PLUG DEFINITION *
|
|
21
23
|
* ========================================================================== */
|