@yamf/test 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/assert.js ADDED
@@ -0,0 +1,504 @@
1
+ import {
2
+ AssertionFailure,
3
+ AssertionFailureDetail,
4
+ MultiAssertionFailure,
5
+ } from './assertion-errors.js'
6
+
7
+ // TODO @yamf/core
8
+ import { Logger } from '../../core/src/index.js'
9
+ const logger = new Logger()
10
+
11
+
12
+ function isAsyncOrPromise(arg) {
13
+ return arg instanceof Promise || arg?.constructor?.name === 'AsyncFunction'
14
+ }
15
+
16
+ // used to check what mode to run in
17
+ function scanAllArgumentsForAsyncOrPromise(args, assertFns) {
18
+ if (!Array.isArray(args)) {
19
+ if (isAsyncOrPromise(args)) return true
20
+ } else {
21
+ for (let arg of args) if (isAsyncOrPromise(arg)) return true
22
+ for (let fn of assertFns) if (isAsyncOrPromise(fn)) return true
23
+ }
24
+ return false
25
+ }
26
+
27
+ // validator for sequence fns (expects a matching assertionFn length)
28
+ function validateSequenceAssertionCount(targets, assertFns) {
29
+ if (!Array.isArray(targets) || !Array.isArray(assertFns))
30
+ throw new Error('Expected sequence assertion arguments to be arrays')
31
+ if (targets.length !== assertFns.length)
32
+ throw new Error('Expected sequence assertion arguments to be the same length')
33
+ }
34
+
35
+ // for multi-assertion meta-helpers (each, sequence)
36
+ function checkFailuresAndThrowMultiAssertionFailure(assertionName, failures) {
37
+ if (failures.length === 0) return
38
+ if (failures.length > 1) {
39
+ throw new MultiAssertionFailure(assertionName, failures)
40
+ } else if (failures.length === 1) {
41
+ throw failures[0]
42
+ }
43
+ }
44
+
45
+ function distillValueFromTestArgument(val) {
46
+ try {
47
+ if (typeof val === 'function') val = val()
48
+ } catch (err) {
49
+ val = err
50
+ }
51
+ // if we find a promise, throw so we can attempt to run as async
52
+ if (val instanceof Promise) {
53
+ let err = new Error('Found Promise in Sync Fn')
54
+ err.promise = val
55
+ throw err
56
+ }
57
+
58
+ return val
59
+ }
60
+
61
+ async function distillValueFromTestArgumentAsync(val) {
62
+ try {
63
+ if (typeof val === 'function') {
64
+ val = await val()
65
+ } else if (val instanceof Promise) {
66
+ return val.then(a => val = a).catch(e => val = e)
67
+ }
68
+ } catch (err) {
69
+ val = err
70
+ }
71
+ return val
72
+ }
73
+
74
+
75
+ function getAssertionFailuresForValue(val, ...assertFns) {
76
+ let assertionFailures = []
77
+ if (!(val instanceof Error)) {
78
+ let failedAssertFns = []
79
+ assertFns.forEach(assertFn => {
80
+ try {
81
+ let isValid
82
+ if (typeof assertFn === 'function') {
83
+ isValid = assertFn(val)
84
+ } else {
85
+ isValid = val === assertFn
86
+ }
87
+ if (!isValid) {
88
+ failedAssertFns.push(assertFn)
89
+ }
90
+ } catch (assertionErr) {
91
+ failedAssertFns.push(assertionErr)
92
+ }
93
+ })
94
+ if (failedAssertFns.length > 0) {
95
+ assertionFailures.push(new AssertionFailureDetail(val, failedAssertFns))
96
+ }
97
+ } else {
98
+ assertionFailures.push(new AssertionFailureDetail(val, 'expected no error'))
99
+ }
100
+ return assertionFailures
101
+ }
102
+
103
+ async function getAssertionFailuresForValueAsync(val, ...assertFns) {
104
+ let assertionFailures = []
105
+ if (!(val instanceof Error)) {
106
+ let failedAssertFns = []
107
+ await Promise.all(assertFns.map(async assertFn => {
108
+ try {
109
+ let isValid
110
+ if (typeof assertFn === 'function') {
111
+ isValid = await assertFn(val)
112
+ } else {
113
+ isValid = val === assertFn
114
+ }
115
+ if (!isValid) {
116
+ failedAssertFns.push(assertFn)
117
+ }
118
+ } catch (assertionErr) {
119
+ failedAssertFns.push(assertionErr)
120
+ }
121
+ }))
122
+ if (failedAssertFns.length > 0) {
123
+ assertionFailures.push(new AssertionFailureDetail(val, failedAssertFns))
124
+ }
125
+ } else {
126
+ assertionFailures.push(new AssertionFailureDetail(val, 'expected no error'))
127
+ }
128
+ return assertionFailures
129
+ }
130
+
131
+ function getErrorAssertionFailuresForValue(val, ...assertFns) {
132
+ let assertionFailures = []
133
+ if (val instanceof Error) {
134
+ let failedAssertFns = []
135
+ assertFns.forEach(assertFn => {
136
+ try {
137
+ let isValid = assertFn(val)
138
+ if (!isValid) {
139
+ failedAssertFns.push(assertFn)
140
+ }
141
+ } catch (assertionErr) {
142
+ failedAssertFns.push(assertionErr)
143
+ }
144
+ })
145
+ if (failedAssertFns.length > 0) {
146
+ assertionFailures.push(new AssertionFailureDetail(val, failedAssertFns))
147
+ }
148
+ } else {
149
+ assertionFailures.push(new AssertionFailureDetail(val, 'expected an error'))
150
+ }
151
+ return assertionFailures
152
+ }
153
+
154
+ async function getErrorAssertionFailuresForValueAsync(val, ...assertFns) {
155
+ let assertionFailures = []
156
+ if (val instanceof Error) {
157
+ let failedAssertFns = []
158
+ await Promise.all(assertFns.map(async assertFn => {
159
+ try {
160
+ let isValid = await assertFn(val)
161
+ if (!isValid) {
162
+ failedAssertFns.push(assertFn)
163
+ }
164
+ } catch (assertionErr) {
165
+ failedAssertFns.push(assertionErr)
166
+ }
167
+ }))
168
+ if (failedAssertFns.length > 0) {
169
+ assertionFailures.push(new AssertionFailureDetail(val, failedAssertFns))
170
+ }
171
+ } else {
172
+ assertionFailures.push(new AssertionFailureDetail(val, 'expected an error'))
173
+ }
174
+ return assertionFailures
175
+ }
176
+
177
+ function getTransformedAssertionFailuresforValue(val, ...assertFns) {
178
+ let assertionFailures = getAssertionFailuresForValue(val, ...assertFns)
179
+ let failure = checkAndTransformErrorsToMultiAssertError(val, assertionFailures)
180
+ return failure
181
+ }
182
+
183
+ async function getTransformedAssertionFailuresforValueAsync(val, ...assertFns) {
184
+ let assertionFailures = await getAssertionFailuresForValueAsync(val, ...assertFns)
185
+ let failure = checkAndTransformErrorsToMultiAssertError(val, assertionFailures)
186
+ return failure
187
+ }
188
+
189
+ function getTransformedErrorAssertionFailuresforValue(val, ...assertFns) {
190
+ let assertionFailures = getErrorAssertionFailuresForValue(val, ...assertFns)
191
+ let failure = checkAndTransformErrorsToMultiAssertError(val, assertionFailures)
192
+ return failure
193
+ }
194
+
195
+ async function getTransformedErrorAssertionFailuresforValueAsync(val, ...assertFns) {
196
+ let assertionFailures = await getErrorAssertionFailuresForValueAsync(val, ...assertFns)
197
+ let failure = checkAndTransformErrorsToMultiAssertError(val, assertionFailures)
198
+ return failure
199
+ }
200
+
201
+ function checkAndTransformErrorsToMultiAssertError(val, assertionFailures) {
202
+ if (Array.isArray(assertionFailures) && assertionFailures.length > 0) {
203
+ let failure
204
+ if (assertionFailures.length === 1) {
205
+ failure = new AssertionFailure(val, assertionFailures[0])
206
+ } else {
207
+ failure = new AssertionFailure(val, assertionFailures)
208
+ }
209
+ return failure
210
+ }
211
+ }
212
+
213
+ // main assertion helper entry-point
214
+ function processAssertionAndChooseSyncOrAsyncPath(assertionHelper, target, ...assertFns) {
215
+ const doAsyncPath = (thrownPromise) => new Promise((resolve, reject) => assertionHelper
216
+ // use the val from the (sync) thrown promise target
217
+ .async(thrownPromise || target, ...assertFns)
218
+ .catch(reject)
219
+ .finally(resolve)
220
+ )
221
+ if (scanAllArgumentsForAsyncOrPromise(target, assertFns)) {
222
+ return doAsyncPath()
223
+ } else try {
224
+ return assertionHelper.sync(target, ...assertFns)
225
+ } catch (err) {
226
+ if (err.message?.includes('Found Promise')) {
227
+ return doAsyncPath(err.promise)
228
+ } else throw err
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Assert that a value or function result passes all assertion functions
234
+ *
235
+ * Automatically detects and handles async when needed:
236
+ * - Direct Promise: `await assert(promise, fn)`
237
+ * - Async function: `await assert(async () => await fetchData(), fn)`
238
+ * - Promise-returning function: `await assert(() => fetchData(), fn)`
239
+ * - Sync function: `assert(() => getValue(), fn)`
240
+ * - Direct value: `assert(5, fn)`
241
+ *
242
+ * @param {*|Function|Promise} valOrFn - Value, function, or Promise to test
243
+ * @param {...Function} assertFns - Assertion functions returning true/false
244
+ * @returns {void|Promise<void>} - Throws AssertError or MultiAssertError on failure
245
+ *
246
+ * @example
247
+ * // Sync usage
248
+ * assert(5, n => n > 0)
249
+ * assert(() => getValue(), n => n > 0)
250
+ *
251
+ * // Async usage (auto-detected, just add await)
252
+ * await assert(promise, v => v !== null)
253
+ * await assert(async () => await fetchData(), d => d.status === 'ok')
254
+ * await assert(() => fetchData(), d => d.status === 'ok') // Promise-returning
255
+ */
256
+
257
+ export function assert(valOrFn, ...assertFns) {
258
+ return processAssertionAndChooseSyncOrAsyncPath(assert, valOrFn, ...assertFns)
259
+ }
260
+
261
+ assert.sync = function assertSync(valOrFn, ...assertFns) {
262
+ let val = distillValueFromTestArgument(valOrFn)
263
+ let failure = getTransformedAssertionFailuresforValue(val, ...assertFns)
264
+ if (failure) throw failure
265
+ }
266
+
267
+ assert.async = async function assertAsync(valOrFn, ...assertFns) {
268
+ let val = await distillValueFromTestArgumentAsync(valOrFn)
269
+ let failure = await getTransformedAssertionFailuresforValueAsync(val, ...assertFns)
270
+ if (failure) throw failure
271
+ }
272
+
273
+ /**
274
+ * Assert that a function throws an error matching all assertion functions
275
+ *
276
+ * Automatically detects and handles async when needed:
277
+ * - Direct Error: `assertErr(error, fn)`
278
+ * - Sync throwing function: `assertErr(() => throwingFn(), fn)`
279
+ * - Async throwing function: `await assertErr(async () => await failingFn(), fn)`
280
+ * - Promise-rejecting function: `await assertErr(() => rejectingFn(), fn)`
281
+ *
282
+ * @param {Error|Function} errOrFn - Error object or function that should throw
283
+ * @param {...Function} assertFns - Assertion functions returning true/false
284
+ * @returns {void|Promise<void>} - Throws AssertError or MultiAssertError on failure
285
+ *
286
+ * @example
287
+ * // Sync usage
288
+ * assertErr(() => throwingFn(), err => err.message.includes('expected'))
289
+ * assertErr(new Error('test'), err => err.message === 'test')
290
+ *
291
+ * // Async usage (auto-detected, just add await)
292
+ * await assertErr(async () => await failingAsyncFn(), err => err.status === 400)
293
+ * await assertErr(() => rejectingPromiseFn(), err => err.message === 'rejected')
294
+ */
295
+ export function assertErr(errOrFn, ...assertFns) {
296
+ return processAssertionAndChooseSyncOrAsyncPath(assertErr, errOrFn, ...assertFns)
297
+ }
298
+
299
+ assertErr.sync = function assertErrSync(errOrFn, ...assertFns) {
300
+ let val = distillValueFromTestArgument(errOrFn)
301
+ let failure = getTransformedErrorAssertionFailuresforValue(val, ...assertFns)
302
+ if (failure) throw failure
303
+ }
304
+
305
+ assertErr.async = async function assertErrAsync(errOrFn, ...assertFns) {
306
+ let val = await distillValueFromTestArgumentAsync(errOrFn)
307
+ let failure = await getTransformedErrorAssertionFailuresforValueAsync(val, ...assertFns)
308
+ if (failure) throw failure
309
+ }
310
+
311
+
312
+ /**
313
+ * Assert that each value in an array passes all assertion functions
314
+ *
315
+ * Automatically detects and handles async when needed.
316
+ * Applies each assertion function to each value in the array.
317
+ *
318
+ * @param {Array} values - Array of values to test
319
+ * @param {...Function} assertFns - Assertion functions returning true/false
320
+ * @returns {void|Promise<void>} - Throws AssertError or MultiAssertError on failure
321
+ *
322
+ * @example
323
+ * // Sync usage
324
+ * assertEach([1, 2, 3],
325
+ * n => n > 0,
326
+ * n => n < 10
327
+ * )
328
+ *
329
+ * // Async usage (auto-detected, just add await)
330
+ * await assertEach([promise1, promise2],
331
+ * val => val !== null,
332
+ * val => val.status === 'ok'
333
+ * )
334
+ */
335
+ export function assertEach(values, ...assertFns) {
336
+ return processAssertionAndChooseSyncOrAsyncPath(assertEach, values, ...assertFns)
337
+ }
338
+
339
+ assertEach.sync = function assertEachSync(values, ...assertFns) {
340
+ let failures = []
341
+ for (let val of values) {
342
+ val = distillValueFromTestArgument(val)
343
+ let failure = getTransformedAssertionFailuresforValue(val, ...assertFns)
344
+ if (failure) failures.push(failure)
345
+ }
346
+ checkFailuresAndThrowMultiAssertionFailure('assertEachSync', failures)
347
+ }
348
+
349
+ assertEach.async = async function assertEachAsync(values, ...assertFns) {
350
+ let failures = []
351
+ for (let val of values) {
352
+ val = await distillValueFromTestArgumentAsync(val)
353
+ let failure = await getTransformedAssertionFailuresforValueAsync(val, ...assertFns)
354
+ if (failure) failures.push(failure)
355
+ }
356
+ checkFailuresAndThrowMultiAssertionFailure('assertEachAsync', failures)
357
+ }
358
+
359
+ /**
360
+ * Assert that each value in an array passes the corresponding assertion function
361
+ *
362
+ * Automatically detects and handles async when needed.
363
+ * Applies the corresponding assertion function to each value in the array.
364
+ * Errors early if the number of values and assertion functions do not match.
365
+ *
366
+ * @param {Array} values - Array of values to test
367
+ * @param {...Function} assertFns - Assertion functions returning true/false
368
+ * @returns {void|Promise<void>} - Throws AssertError or MultiAssertError on failure
369
+ *
370
+ * @example
371
+ * // Sync usage
372
+ * assertSequence([1, 2, 3],
373
+ * n => n === 1,
374
+ * n => n === 2,
375
+ * n => n === 3
376
+ * )
377
+ *
378
+ * // Async usage (auto-detected, just add await)
379
+ * await assertSequence([promise1, promise2, promise3],
380
+ * val => val !== null,
381
+ * val => val.status === 'ok',
382
+ * val => val.count > 0
383
+ * )
384
+ */
385
+ export function assertSequence(values, ...assertFns) {
386
+ validateSequenceAssertionCount(values, assertFns)
387
+ return processAssertionAndChooseSyncOrAsyncPath(assertSequence, values, ...assertFns)
388
+ }
389
+
390
+ assertSequence.sync = function assertSequenceSync(values, ...assertFns) {
391
+ let failures = []
392
+ let index = 0
393
+ for (let val of values) {
394
+ val = distillValueFromTestArgument(val)
395
+ let failure = getTransformedAssertionFailuresforValue(val, assertFns[index])
396
+ if (failure) failures.push(failure)
397
+ index++
398
+ }
399
+ checkFailuresAndThrowMultiAssertionFailure('assertSequenceSync', failures)
400
+ }
401
+
402
+ assertSequence.async = async function assertSequenceAsync(values, ...assertFns) {
403
+ let failures = []
404
+ let index = 0
405
+ for (let val of values) {
406
+ val = await distillValueFromTestArgumentAsync(val)
407
+ let failure = await getTransformedAssertionFailuresforValueAsync(val, assertFns[index])
408
+ if (failure) failures.push(failure)
409
+ index++
410
+ }
411
+ checkFailuresAndThrowMultiAssertionFailure('assertSequenceAsync', failures)
412
+ }
413
+
414
+
415
+ /**
416
+ * Assert that each error in an array passes all assertion functions
417
+ * Assert that each function in an array throws an error matching all assertion functions
418
+ *
419
+ * Automatically detects and handles async when needed.
420
+ *
421
+ * @param {Array} errsOrFns - Array of errors or functions that should throw
422
+ * @param {...Function} assertFns - Assertion functions returning true/false
423
+ * @returns {void|Promise<void>} - Throws AssertError or MultiAssertError on failure
424
+ *
425
+ * @example
426
+ * // Sync usage with error objects
427
+ * assertErrEach([new Error('test'), new Error('test2')],
428
+ * err => err instanceof Error,
429
+ * err => err.message.includes('test')
430
+ * )
431
+ *
432
+ * // Sync usage with throwing functions
433
+ * assertErrEach([
434
+ * () => { throw new Error('error1') },
435
+ * () => { throw new Error('error2') }
436
+ * ],
437
+ * err => err instanceof Error,
438
+ * err => err.message.includes('error')
439
+ * )
440
+ *
441
+ * // Async usage (auto-detected, just add await)
442
+ * await assertErrEach([
443
+ * async () => { throw new Error('async1') },
444
+ * async () => { throw new Error('async2') }
445
+ * ],
446
+ * err => err instanceof Error,
447
+ * err => err.message.includes('async')
448
+ * )
449
+ */
450
+ export function assertErrEach(errsOrFns, ...assertFns) {
451
+ return processAssertionAndChooseSyncOrAsyncPath(assertErrEach, errsOrFns, ...assertFns)
452
+ }
453
+
454
+ assertErrEach.sync = function assertErrEachSync(errsOrFns, ...assertFns) {
455
+ let failures = []
456
+ for (let errOrFn of errsOrFns) {
457
+ let val = distillValueFromTestArgument(errOrFn)
458
+ let failure = getTransformedErrorAssertionFailuresforValue(val, ...assertFns)
459
+ if (failure) failures.push(failure)
460
+ }
461
+ checkFailuresAndThrowMultiAssertionFailure('assertErrEachSync', failures)
462
+ }
463
+
464
+ assertErrEach.async = async function assertErrEachAsync(errsOrFns, ...assertFns) {
465
+ let failures = []
466
+ for (let errOrFn of errsOrFns) {
467
+ let val = await distillValueFromTestArgumentAsync(errOrFn)
468
+ let failure = await getTransformedErrorAssertionFailuresforValueAsync(val, ...assertFns)
469
+ if (failure) failures.push(failure)
470
+ }
471
+ checkFailuresAndThrowMultiAssertionFailure('assertErrEachAsync', failures)
472
+ }
473
+
474
+
475
+ /* TODO documentation comment
476
+ */
477
+ export function assertErrSequence(errsOrFns, ...assertFns) {
478
+ validateSequenceAssertionCount(errsOrFns, assertFns)
479
+ return processAssertionAndChooseSyncOrAsyncPath(assertErrSequence, errsOrFns, ...assertFns)
480
+ }
481
+
482
+ assertErrSequence.sync = function assertErrSequenceSync(errsOrFns, ...assertFns) {
483
+ let failures = []
484
+ let index = 0
485
+ for (let errOrFn of errsOrFns) {
486
+ let val = distillValueFromTestArgument(errOrFn)
487
+ let failure = getTransformedErrorAssertionFailuresforValue(val, assertFns[index])
488
+ if (failure) failures.push(failure)
489
+ index++
490
+ }
491
+ checkFailuresAndThrowMultiAssertionFailure('assertErrSequenceSync', failures)
492
+ }
493
+
494
+ assertErrSequence.async = async function assertErrSequenceAsync(errsOrFns, ...assertFns) {
495
+ let failures = []
496
+ let index = 0
497
+ for (let errOrFn of errsOrFns) {
498
+ let val = await distillValueFromTestArgumentAsync(errOrFn)
499
+ let failure = await getTransformedErrorAssertionFailuresforValueAsync(val, assertFns[index])
500
+ if (failure) failures.push(failure)
501
+ index++
502
+ }
503
+ checkFailuresAndThrowMultiAssertionFailure('assertErrSequenceAsync', failures)
504
+ }
@@ -0,0 +1,114 @@
1
+
2
+ const getType = (val) => {
3
+ if (val instanceof Error) {
4
+ return `error`
5
+ } else return typeof val
6
+ }
7
+
8
+ const getTargetString = (val, pad) => {
9
+ if (val instanceof Error) {
10
+ return `"${val.message}"`
11
+ } else if (Array.isArray(val) && val.some(v => v instanceof Error)) {
12
+ return `[${val.map(v => `\n${pad+pad}${getValOrErrString(v)}`).join()}\n${pad}]`
13
+ } else if (typeof val === 'object') {
14
+ return JSON.stringify(val)
15
+ } else {
16
+ return val.toString()
17
+ }
18
+ }
19
+
20
+ export class AssertionFailureDetail {
21
+ constructor(distilledTargetValue, failingAssertionFns, childDetails) {
22
+ this.target = distilledTargetValue
23
+ this.assertFns = (
24
+ Array.isArray(failingAssertionFns)
25
+ && failingAssertionFns.length > 1
26
+ ) ? failingAssertionFns : [failingAssertionFns]
27
+ if (!this.assertFns) this.assertFns = []
28
+ this.children = childDetails
29
+ }
30
+
31
+ toString(depth = 1) {
32
+ let pad = Array.from({ length: depth }, () => ' ').join('')
33
+ let type = getType(this.target)
34
+ let val = getTargetString(this.target, pad)
35
+ let childMessages = this.children?.map(c => {
36
+ `${pad}${c.toString(depth).split('\n').map(l => `${pad}${l}`).join('\n')}`
37
+ })
38
+ childMessages = childMessages || ''
39
+ return `${pad}for target (${type}) value = ${val}`
40
+ + this.assertFns.map(fn =>
41
+ `\n${pad}failed -> ${fn?.name || fn.toString().replace(/^\s?.+\=\>\s?/, '')}`
42
+ ).join('')
43
+ + childMessages
44
+ }
45
+
46
+ get message() {
47
+ return this.toString()
48
+ }
49
+
50
+ get assertMessage() {
51
+ // 0 for no padding, runner will print this on the same initial line
52
+ return this.toString(0)
53
+ }
54
+ }
55
+
56
+ export class AssertionFailure extends Error {
57
+ constructor(distilledTargetObject, assertionFailureDetails) {
58
+ super() // don't care about original message
59
+ this.target = distilledTargetObject
60
+ this.assertionFailureDetails = (
61
+ Array.isArray(assertionFailureDetails)
62
+ && assertionFailureDetails.length > 1
63
+ ) ? assertionFailureDetails : [assertionFailureDetails]
64
+ }
65
+
66
+ toString(depth = 1) {
67
+ let pad = Array.from({ length: depth }, () => ' ').join('')
68
+ return `${pad}AssertionFailure\n`
69
+ + this.assertionFailureDetails?.map(d =>
70
+ `${d.toString(depth+1).split('\n').map(l => `${l}`).join('\n')}`
71
+ ).join('\n')
72
+ }
73
+
74
+ get message() {
75
+ return this.toString()
76
+ }
77
+
78
+ get assertMessage() {
79
+ // 0 for no padding, runner will print this on the same initial line
80
+ return this.toString(0)
81
+ }
82
+ }
83
+
84
+
85
+ export class MultiAssertionFailure extends Error {
86
+ constructor(assertType, failures) {
87
+ super() // don't care about normal error message
88
+
89
+ if (!failures) {
90
+ failures = assertType
91
+ assertType = ''
92
+ }
93
+ this.assertType = assertType
94
+ this.failures = failures
95
+ this.name = 'MultiAssertionFailure'
96
+ }
97
+
98
+ toString() {
99
+ let typeMessage = this.assertType
100
+ ? `: "${this.assertType}" failed`
101
+ : ''
102
+
103
+ return `${this.name}${typeMessage}\n`
104
+ + this.failures.map(f => f.toString(1)).join('\n')
105
+ }
106
+
107
+ get message() {
108
+ return this.toString()
109
+ }
110
+
111
+ get assertMessage() {
112
+ return this.toString()
113
+ }
114
+ }
package/src/cli.js ADDED
@@ -0,0 +1,49 @@
1
+
2
+ /* TODO add cli options and flags
3
+
4
+ --name <name> (matching text or wildcard)
5
+ --file <file> (matching file name text or wildcard; is a test suite by default)
6
+
7
+
8
+ ---TODO determine features and flags from the following suggested list---
9
+
10
+ Essential test CLI options and flags vary significantly depending on the specific testing framework or tool being used. However, common categories of options and flags found across many testing CLIs include:
11
+ Execution Control:
12
+
13
+ Running specific tests:
14
+ --test-name <name> or similar: Executes a single test or a group of tests matching a pattern.
15
+ --grep <pattern>: Filters tests based on a regular expression pattern in their names.
16
+ --file <path>: Runs tests only from specified files.
17
+ Controlling execution flow:
18
+ --fail-fast or --first: Stops test execution immediately upon the first failure.
19
+ --bail: Similar to fail-fast, often with more granular control over how many failures are tolerated before stopping.
20
+ --retries <n>: Reruns failed tests a specified number of times.
21
+
22
+ Output and Reporting:
23
+
24
+ Verbosity levels:
25
+ --verbose or -v: Provides more detailed output during test execution.
26
+ --quiet or -q: Suppresses most output, showing only essential information like summaries.
27
+ Reporting formats:
28
+ --reporter <format>: Specifies the output format for test results (e.g., json, junit, html).
29
+ --output <file>: Redirects test output to a specified file.
30
+ Code coverage:
31
+ --coverage: Enables code coverage analysis and reporting.
32
+ --coverage-report <format>: Specifies the format for coverage reports.
33
+
34
+ Configuration and Setup:
35
+
36
+ Configuration files:
37
+ --config <file>: Specifies an alternative configuration file for the test runner.
38
+ Environment variables:
39
+ --env <key=value>: Sets environment variables for the test process.
40
+ Setup/Teardown scripts:
41
+ --pre-flight <script>: Runs a script or command before any tests execute.
42
+ --post-flight <script>: Runs a script or command after all tests have completed.
43
+
44
+ Debugging and Development:
45
+
46
+ Debugging tools:
47
+ --inspect or --debug: Enables debugging features, often allowing attachment of a debugger.
48
+ --watch: Reruns tests automatically when relevant files change.
49
+ */