@plugjs/expect5 0.4.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/README.md +7 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.mjs +96 -0
- package/dist/cli.mjs.map +6 -0
- package/dist/execution/executable.cjs +299 -0
- package/dist/execution/executable.cjs.map +6 -0
- package/dist/execution/executable.d.ts +87 -0
- package/dist/execution/executable.mjs +260 -0
- package/dist/execution/executable.mjs.map +6 -0
- package/dist/execution/executor.cjs +125 -0
- package/dist/execution/executor.cjs.map +6 -0
- package/dist/execution/executor.d.ts +35 -0
- package/dist/execution/executor.mjs +90 -0
- package/dist/execution/executor.mjs.map +6 -0
- package/dist/execution/setup.cjs +127 -0
- package/dist/execution/setup.cjs.map +6 -0
- package/dist/execution/setup.d.ts +31 -0
- package/dist/execution/setup.mjs +87 -0
- package/dist/execution/setup.mjs.map +6 -0
- package/dist/expectation/basic.cjs +216 -0
- package/dist/expectation/basic.cjs.map +6 -0
- package/dist/expectation/basic.d.ts +47 -0
- package/dist/expectation/basic.mjs +177 -0
- package/dist/expectation/basic.mjs.map +6 -0
- package/dist/expectation/diff.cjs +253 -0
- package/dist/expectation/diff.cjs.map +6 -0
- package/dist/expectation/diff.d.ts +27 -0
- package/dist/expectation/diff.mjs +228 -0
- package/dist/expectation/diff.mjs.map +6 -0
- package/dist/expectation/expect.cjs +211 -0
- package/dist/expectation/expect.cjs.map +6 -0
- package/dist/expectation/expect.d.ts +140 -0
- package/dist/expectation/expect.mjs +219 -0
- package/dist/expectation/expect.mjs.map +6 -0
- package/dist/expectation/include.cjs +187 -0
- package/dist/expectation/include.cjs.map +6 -0
- package/dist/expectation/include.d.ts +10 -0
- package/dist/expectation/include.mjs +158 -0
- package/dist/expectation/include.mjs.map +6 -0
- package/dist/expectation/print.cjs +281 -0
- package/dist/expectation/print.cjs.map +6 -0
- package/dist/expectation/print.d.ts +4 -0
- package/dist/expectation/print.mjs +256 -0
- package/dist/expectation/print.mjs.map +6 -0
- package/dist/expectation/throwing.cjs +58 -0
- package/dist/expectation/throwing.cjs.map +6 -0
- package/dist/expectation/throwing.d.ts +8 -0
- package/dist/expectation/throwing.mjs +32 -0
- package/dist/expectation/throwing.mjs.map +6 -0
- package/dist/expectation/types.cjs +212 -0
- package/dist/expectation/types.cjs.map +6 -0
- package/dist/expectation/types.d.ts +57 -0
- package/dist/expectation/types.mjs +178 -0
- package/dist/expectation/types.mjs.map +6 -0
- package/dist/expectation/void.cjs +111 -0
- package/dist/expectation/void.cjs.map +6 -0
- package/dist/expectation/void.d.ts +39 -0
- package/dist/expectation/void.mjs +77 -0
- package/dist/expectation/void.mjs.map +6 -0
- package/dist/globals.cjs +2 -0
- package/dist/globals.cjs.map +6 -0
- package/dist/globals.d.ts +23 -0
- package/dist/globals.mjs +1 -0
- package/dist/globals.mjs.map +6 -0
- package/dist/index.cjs +66 -0
- package/dist/index.cjs.map +6 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.mjs +41 -0
- package/dist/index.mjs.map +6 -0
- package/dist/test.cjs +229 -0
- package/dist/test.cjs.map +6 -0
- package/dist/test.d.ts +9 -0
- package/dist/test.mjs +194 -0
- package/dist/test.mjs.map +6 -0
- package/package.json +57 -0
- package/src/cli.mts +122 -0
- package/src/execution/executable.ts +364 -0
- package/src/execution/executor.ts +146 -0
- package/src/execution/setup.ts +108 -0
- package/src/expectation/basic.ts +209 -0
- package/src/expectation/diff.ts +445 -0
- package/src/expectation/expect.ts +401 -0
- package/src/expectation/include.ts +184 -0
- package/src/expectation/print.ts +386 -0
- package/src/expectation/throwing.ts +45 -0
- package/src/expectation/types.ts +263 -0
- package/src/expectation/void.ts +80 -0
- package/src/globals.ts +30 -0
- package/src/index.ts +54 -0
- package/src/test.ts +239 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ExpectationError, stringifyValue } from './types'
|
|
2
|
+
|
|
3
|
+
import type { Expectation, Expectations } from './expect'
|
|
4
|
+
|
|
5
|
+
/** A simple {@link Expectation} performing a basic true/false check */
|
|
6
|
+
abstract class VoidExpectation implements Expectation {
|
|
7
|
+
constructor(
|
|
8
|
+
private _details: string,
|
|
9
|
+
private _check: (value: unknown) => boolean,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
expect(context: Expectations, negative: boolean): void {
|
|
13
|
+
const check = this._check(context.value)
|
|
14
|
+
if (check === negative) {
|
|
15
|
+
throw new ExpectationError(context, negative, this._details)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* ========================================================================== */
|
|
21
|
+
|
|
22
|
+
export class ToBeDefined extends VoidExpectation {
|
|
23
|
+
constructor() {
|
|
24
|
+
super('to be defined', (value) => (value !== null) && (value !== undefined))
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ToBeFalse extends VoidExpectation {
|
|
29
|
+
constructor() {
|
|
30
|
+
super(`to be ${stringifyValue(false)}`, (value) => value === false)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ToBeFalsy extends VoidExpectation {
|
|
35
|
+
constructor() {
|
|
36
|
+
super('to be falsy', (value) => ! value)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class ToBeNaN extends VoidExpectation {
|
|
41
|
+
constructor() {
|
|
42
|
+
super(`to be ${stringifyValue(NaN)}`, (value) => (typeof value === 'number') && isNaN(value))
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ToBeNegativeInfinity extends VoidExpectation {
|
|
47
|
+
constructor() {
|
|
48
|
+
super(`to equal ${stringifyValue(Number.NEGATIVE_INFINITY)}`, (value) => value === Number.NEGATIVE_INFINITY)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ToBeNull extends VoidExpectation {
|
|
53
|
+
constructor() {
|
|
54
|
+
super(`to be ${stringifyValue(null)}`, (value) => value === null)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export class ToBePositiveInfinity extends VoidExpectation {
|
|
59
|
+
constructor() {
|
|
60
|
+
super(`to equal ${stringifyValue(Number.POSITIVE_INFINITY)}`, (value) => value === Number.POSITIVE_INFINITY)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class ToBeTrue extends VoidExpectation {
|
|
65
|
+
constructor() {
|
|
66
|
+
super(`to be ${stringifyValue(true)}`, (value) => value === true)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class ToBeTruthy extends VoidExpectation {
|
|
71
|
+
constructor() {
|
|
72
|
+
super('to be truthy', (value) => !! value)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class ToBeUndefined extends VoidExpectation {
|
|
77
|
+
constructor() {
|
|
78
|
+
super(`to be ${stringifyValue(undefined)}`, (value) => value === undefined)
|
|
79
|
+
}
|
|
80
|
+
}
|
package/src/globals.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as setup from './execution/setup'
|
|
2
|
+
import type { skip as SkipFunction } from './execution/executable'
|
|
3
|
+
import type { expect as ExpectFunction } from './expectation/expect'
|
|
4
|
+
import type { logging } from '@plugjs/plug'
|
|
5
|
+
|
|
6
|
+
declare global {
|
|
7
|
+
const describe: setup.SuiteFunction
|
|
8
|
+
const fdescribe: setup.SuiteSetup
|
|
9
|
+
const xdescribe: setup.SuiteSetup
|
|
10
|
+
|
|
11
|
+
const it: setup.SpecFunction
|
|
12
|
+
const fit: setup.SpecSetup
|
|
13
|
+
const xit: setup.SpecSetup
|
|
14
|
+
|
|
15
|
+
const afterAll: setup.HookFunction
|
|
16
|
+
const afterEach: setup.HookFunction
|
|
17
|
+
const beforeAll: setup.HookFunction
|
|
18
|
+
const beforeEach: setup.HookFunction
|
|
19
|
+
|
|
20
|
+
const xafterAll: setup.HookSetup
|
|
21
|
+
const xafterEach: setup.HookSetup
|
|
22
|
+
const xbeforeAll: setup.HookSetup
|
|
23
|
+
const xbeforeEach: setup.HookSetup
|
|
24
|
+
|
|
25
|
+
const skip: typeof SkipFunction
|
|
26
|
+
|
|
27
|
+
const expect: typeof ExpectFunction
|
|
28
|
+
|
|
29
|
+
const log: logging.LogFunction
|
|
30
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { installForking } from '@plugjs/plug/fork'
|
|
2
|
+
import { requireResolve } from '@plugjs/plug/paths'
|
|
3
|
+
|
|
4
|
+
import type { ForkOptions } from '@plugjs/plug/fork'
|
|
5
|
+
|
|
6
|
+
/* ========================================================================== *
|
|
7
|
+
* EXPORTED VARIABLES (for when globals is false) *
|
|
8
|
+
* ========================================================================== */
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
it, fit, xit,
|
|
12
|
+
describe, fdescribe, xdescribe,
|
|
13
|
+
afterAll, afterEach, xafterAll, xafterEach,
|
|
14
|
+
beforeAll, beforeEach, xbeforeAll, xbeforeEach,
|
|
15
|
+
} from './execution/setup'
|
|
16
|
+
export { skip } from './execution/executable'
|
|
17
|
+
export { expect } from './expectation/expect'
|
|
18
|
+
|
|
19
|
+
/* ========================================================================== *
|
|
20
|
+
* EXPORTED OPTIONS TYPE AND PLUG DEFINITION *
|
|
21
|
+
* ========================================================================== */
|
|
22
|
+
|
|
23
|
+
/** Options to construct our {@link Jasmine} plug. */
|
|
24
|
+
export interface TestOptions extends ForkOptions {
|
|
25
|
+
/** Report up to the specified amount of failures (default: `+Infinity`) */
|
|
26
|
+
maxFailures?: number,
|
|
27
|
+
/**
|
|
28
|
+
* Specify whether the variables (`describe`, `it`, `expect`, ...) will be
|
|
29
|
+
* exposed as _global_ variables to tests or not (default: `true`)
|
|
30
|
+
*/
|
|
31
|
+
globals?: boolean,
|
|
32
|
+
/**
|
|
33
|
+
* Print differences between expected and actual values from generic errors
|
|
34
|
+
* (e.g. `AssertionError` or _Chai_ expectations) (default: `true`)
|
|
35
|
+
*/
|
|
36
|
+
genericErrorDiffs?: boolean,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare module '@plugjs/plug' {
|
|
40
|
+
export interface Pipe {
|
|
41
|
+
/**
|
|
42
|
+
* Run tests.
|
|
43
|
+
*
|
|
44
|
+
* @param options Optional {@link TestOptions | options}.
|
|
45
|
+
*/
|
|
46
|
+
test(options?: TestOptions): Promise<undefined>
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* ========================================================================== *
|
|
51
|
+
* INSTALL FORKING PLUG *
|
|
52
|
+
* ========================================================================== */
|
|
53
|
+
|
|
54
|
+
installForking('test', requireResolve(__fileurl, './test'), 'Test')
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Reference ourselves, so that the constructor's parameters are correct
|
|
2
|
+
/// <reference path="./index.ts"/>
|
|
3
|
+
|
|
4
|
+
import { AssertionError } from 'node:assert'
|
|
5
|
+
|
|
6
|
+
import { BuildFailure } from '@plugjs/plug'
|
|
7
|
+
import { $blu, $grn, $gry, $ms, $red, $wht, $ylw, log, ERROR, NOTICE, WARN } from '@plugjs/plug/logging'
|
|
8
|
+
import { assert } from '@plugjs/plug/asserts'
|
|
9
|
+
|
|
10
|
+
import { skip, Suite } from './execution/executable'
|
|
11
|
+
import { runSuite } from './execution/executor'
|
|
12
|
+
import { expect } from './expectation/expect'
|
|
13
|
+
import { ExpectationError, stringifyObjectType } from './expectation/types'
|
|
14
|
+
import { printDiff } from './expectation/print'
|
|
15
|
+
import { diff } from './expectation/diff'
|
|
16
|
+
import * as setup from './execution/setup'
|
|
17
|
+
|
|
18
|
+
import type { Files } from '@plugjs/plug/files'
|
|
19
|
+
import type { Logger } from '@plugjs/plug/logging'
|
|
20
|
+
import type { Context, PipeParameters, Plug } from '@plugjs/plug/pipe'
|
|
21
|
+
import type { TestOptions } from './index'
|
|
22
|
+
|
|
23
|
+
const _pending = '\u22EF' // middle ellipsis
|
|
24
|
+
const _success = '\u2714' // heavy check mark
|
|
25
|
+
const _failure = '\u2718' // heavy ballot x
|
|
26
|
+
const _details = '\u2192' // rightwards arrow
|
|
27
|
+
|
|
28
|
+
/** Writes some info about the current {@link Files} being passed around. */
|
|
29
|
+
export class Test implements Plug<void> {
|
|
30
|
+
constructor(...args: PipeParameters<'test'>)
|
|
31
|
+
constructor(private readonly _options: TestOptions = {}) {}
|
|
32
|
+
|
|
33
|
+
async pipe(files: Files, context: Context): Promise<void> {
|
|
34
|
+
assert(files.length, 'No files available for running tests')
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
globals = true,
|
|
38
|
+
genericErrorDiffs = true,
|
|
39
|
+
maxFailures = Number.POSITIVE_INFINITY,
|
|
40
|
+
} = this._options
|
|
41
|
+
|
|
42
|
+
// Inject globals if we were told to do so...
|
|
43
|
+
if (globals) {
|
|
44
|
+
const anyGlobal = globalThis as any
|
|
45
|
+
|
|
46
|
+
anyGlobal['describe'] = setup.describe
|
|
47
|
+
anyGlobal['fdescribe'] = setup.fdescribe
|
|
48
|
+
anyGlobal['xdescribe'] = setup.xdescribe
|
|
49
|
+
anyGlobal['it'] = setup.it
|
|
50
|
+
anyGlobal['fit'] = setup.fit
|
|
51
|
+
anyGlobal['xit'] = setup.xit
|
|
52
|
+
anyGlobal['afterAll'] = setup.afterAll
|
|
53
|
+
anyGlobal['afterEach'] = setup.afterEach
|
|
54
|
+
anyGlobal['beforeAll'] = setup.beforeAll
|
|
55
|
+
anyGlobal['beforeEach'] = setup.beforeEach
|
|
56
|
+
anyGlobal['xafterAll'] = setup.xafterAll
|
|
57
|
+
anyGlobal['xafterEach'] = setup.xafterEach
|
|
58
|
+
anyGlobal['xbeforeAll'] = setup.xbeforeAll
|
|
59
|
+
anyGlobal['xbeforeEach'] = setup.xbeforeEach
|
|
60
|
+
anyGlobal['skip'] = skip
|
|
61
|
+
anyGlobal['expect'] = expect
|
|
62
|
+
anyGlobal['log'] = log
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Create our _root_ Suite
|
|
66
|
+
const suite = new Suite(undefined, '', async () => {
|
|
67
|
+
for (const file of files.absolutePaths()) await import(file)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Setup our suite counts
|
|
71
|
+
await suite.setup()
|
|
72
|
+
|
|
73
|
+
const snum = suite.specs
|
|
74
|
+
const fnum = files.length
|
|
75
|
+
const smsg = snum === 1 ? 'spec' : 'specs'
|
|
76
|
+
const fmsg = fnum === 1 ? 'file' : 'files'
|
|
77
|
+
|
|
78
|
+
assert(snum, 'No specs configured by test files')
|
|
79
|
+
|
|
80
|
+
// Run our suite and setup listeners
|
|
81
|
+
const execution = runSuite(suite)
|
|
82
|
+
|
|
83
|
+
execution.on('suite:start', (current) => {
|
|
84
|
+
if (current.parent === suite) {
|
|
85
|
+
if (suite.flag !== 'only') context.log.notice('')
|
|
86
|
+
context.log.enter(NOTICE, `${$wht(current.name)}`)
|
|
87
|
+
context.log.notice('')
|
|
88
|
+
} else if (current.parent) {
|
|
89
|
+
context.log.enter(NOTICE, `${$blu(_details)} ${$wht(current.name)}`)
|
|
90
|
+
} else {
|
|
91
|
+
context.log.notice(`Running ${$ylw(snum)} ${smsg} from ${$ylw(fnum)} ${fmsg}`)
|
|
92
|
+
if (suite.flag === 'only') context.log.notice('')
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
execution.on('suite:done', (current) => {
|
|
97
|
+
if (current.parent) context.log.leave()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
execution.on('spec:start', (spec) => {
|
|
101
|
+
context.log.enter(NOTICE, `${$blu(_pending)} ${spec.name}`)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
execution.on('spec:skip', (spec, ms) => {
|
|
105
|
+
if (suite.flag === 'only') return context.log.leave()
|
|
106
|
+
context.log.leave(WARN, `${$ylw(_pending)} ${spec.name} ${$ms(ms)} ${$gry('[')}${$ylw('skipped')}${$gry(']')}`)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
execution.on('spec:pass', (spec, ms) => {
|
|
110
|
+
context.log.leave(NOTICE, `${$grn(_success)} ${spec.name} ${$ms(ms)}`)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
execution.on('spec:fail', (spec, ms, { number }) => {
|
|
114
|
+
context.log.leave(ERROR,
|
|
115
|
+
`${$red(_failure)} ${spec.name} ${$ms(ms)} ` +
|
|
116
|
+
`${$gry('[')}${$red('failed')}${$gry('|')}${$red(`${number}`)}${$gry(']')}`)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
execution.on('hook:fail', (hook, ms, { number }) => {
|
|
120
|
+
context.log.error(`${$red(_failure)} Hook "${hook.name}" ${$ms(ms)} ` +
|
|
121
|
+
`${$gry('[')}${$red('failed')}${$gry('|')}${$red(`${number}`)}${$gry(']')}`)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Await execution
|
|
125
|
+
const { failed, passed, skipped, failures, time } = await execution.result
|
|
126
|
+
|
|
127
|
+
// Dump all (or a limited number of) failures
|
|
128
|
+
const limit = Math.min(failures.length, maxFailures)
|
|
129
|
+
for (let i = 0; i < limit; i ++) {
|
|
130
|
+
if (i === 0) context.log.error('')
|
|
131
|
+
const { source, error, number } = failures[i]!
|
|
132
|
+
|
|
133
|
+
const names: string[] = [ '' ]
|
|
134
|
+
for (let p = source.parent; p?.parent; p = p.parent) {
|
|
135
|
+
if (p) names.unshift(p.name)
|
|
136
|
+
}
|
|
137
|
+
const details = names.join(` ${$gry(_details)} `) + $wht(source.name)
|
|
138
|
+
|
|
139
|
+
context.log.enter(ERROR, `${$gry('[')}${$red(number)}${$gry(']:')} ${details}`)
|
|
140
|
+
dumpError(context.log, error, genericErrorDiffs)
|
|
141
|
+
context.log.leave()
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Epilogue
|
|
145
|
+
const summary: string[] = [ `${passed} ${$gry('passed')}` ]
|
|
146
|
+
if (skipped) summary.push(`${skipped} ${$gry('skipped')}`)
|
|
147
|
+
if (failed) summary.push(`${failed} ${$gry('failed')}`)
|
|
148
|
+
if (failures.length) summary.push(`${failures.length} ${$gry('total failures')}`)
|
|
149
|
+
|
|
150
|
+
const epilogue = ` ${$gry('(')}${summary.join($gry(', '))}${$gry(')')}`
|
|
151
|
+
const message = `Ran ${$ylw(snum)} ${smsg} from ${$ylw(fnum)} ${fmsg}${epilogue} ${$ms(time)}`
|
|
152
|
+
|
|
153
|
+
if (failures.length) {
|
|
154
|
+
context.log.error(message)
|
|
155
|
+
throw new BuildFailure()
|
|
156
|
+
} else if (suite.flag === 'only') {
|
|
157
|
+
context.log.error('')
|
|
158
|
+
context.log.error(message)
|
|
159
|
+
throw new BuildFailure('Suite running in focus ("only") mode')
|
|
160
|
+
} else if (skipped) {
|
|
161
|
+
context.log.warn('')
|
|
162
|
+
context.log.warn(message)
|
|
163
|
+
} else {
|
|
164
|
+
context.log.notice('')
|
|
165
|
+
context.log.notice(message)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ========================================================================== *
|
|
171
|
+
* ERROR REPORTING *
|
|
172
|
+
* ========================================================================== */
|
|
173
|
+
|
|
174
|
+
function dumpError(log: Logger, error: any, genericErrorDiffs: boolean): void {
|
|
175
|
+
// First and foremost, our own expectation errors
|
|
176
|
+
if (error instanceof ExpectationError) {
|
|
177
|
+
log.enter(ERROR, `${$gry('Expectation Error:')} ${$red(error.message)}`)
|
|
178
|
+
try {
|
|
179
|
+
dumpStack(log, error)
|
|
180
|
+
if (error.diff) printDiff(log, error.diff)
|
|
181
|
+
} finally {
|
|
182
|
+
log.error('')
|
|
183
|
+
log.leave()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Assertion errors are another kind of exception we support
|
|
187
|
+
} else if (error instanceof AssertionError) {
|
|
188
|
+
const [ message = 'Unknown Error', ...lines ] = error.message.split('\n')
|
|
189
|
+
log.enter(ERROR, `${$gry('Assertion Error:')} ${$red(message)}`)
|
|
190
|
+
try {
|
|
191
|
+
dumpStack(log, error)
|
|
192
|
+
|
|
193
|
+
// If we print diffs from generic errors, we take over
|
|
194
|
+
if (genericErrorDiffs) {
|
|
195
|
+
// if this is a generated message ignore all extra lines
|
|
196
|
+
if (! error.generatedMessage) for (const line of lines) log.error(' ', line)
|
|
197
|
+
printDiff(log, diff(error.actual, error.expected))
|
|
198
|
+
} else {
|
|
199
|
+
// trim initial empty lines
|
|
200
|
+
while (lines.length && (! lines[0])) lines.shift()
|
|
201
|
+
for (const line of lines) log.error(' ', line)
|
|
202
|
+
}
|
|
203
|
+
} finally {
|
|
204
|
+
log.error('')
|
|
205
|
+
log.leave()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Any other error also gets printed somewhat nicely
|
|
209
|
+
} else if (error instanceof Error) {
|
|
210
|
+
const message = error.message || 'Unknown Error'
|
|
211
|
+
const type = stringifyObjectType(error)
|
|
212
|
+
log.enter(ERROR, `${$gry(type)}: ${$red(message)}`)
|
|
213
|
+
try {
|
|
214
|
+
dumpStack(log, error)
|
|
215
|
+
|
|
216
|
+
// if there are "actual" or "expected" properties on the error, diff!
|
|
217
|
+
if (genericErrorDiffs && (('actual' in error) || ('expected' in error))) {
|
|
218
|
+
printDiff(log, diff((error as any).actual, (error as any).expected))
|
|
219
|
+
}
|
|
220
|
+
} finally {
|
|
221
|
+
log.error('')
|
|
222
|
+
log.leave()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Anthing else just gets dumped out...
|
|
226
|
+
} else /* coverage ignore next */ {
|
|
227
|
+
// This should never happen, as executor converts evertything to errors...
|
|
228
|
+
log.error($gry('Uknown error:'), error)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function dumpStack(log: Logger, error: Error): void {
|
|
233
|
+
if (! error.stack) return log.error('<no stack trace>')
|
|
234
|
+
error.stack
|
|
235
|
+
.split('\n')
|
|
236
|
+
.filter((line) => line.match(/^\s+at\s+/))
|
|
237
|
+
.map((line) => line.trim())
|
|
238
|
+
.forEach((line) => log.error(line))
|
|
239
|
+
}
|