@travetto/test 4.1.1 → 5.0.0-rc.1
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/LICENSE +1 -1
- package/package.json +8 -8
- package/src/assert/check.ts +10 -34
- package/src/assert/util.ts +20 -17
- package/src/consumer/error.ts +7 -4
- package/src/consumer/types/tap.ts +8 -10
- package/src/consumer/types/xunit.ts +2 -2
- package/src/execute/console.ts +9 -7
- package/src/execute/executor.ts +14 -14
- package/src/execute/phase.ts +2 -2
- package/src/execute/promise.ts +38 -61
- package/src/execute/runner.ts +7 -5
- package/src/execute/util.ts +1 -1
- package/src/execute/watcher.ts +2 -3
- package/src/fixture.ts +1 -1
- package/src/registry/suite.ts +2 -2
- package/src/worker/child.ts +16 -11
- package/src/worker/standard.ts +50 -66
- package/support/cli.test.ts +1 -1
- package/support/cli.test_child.ts +2 -2
package/LICENSE
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/test",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-rc.1",
|
|
4
4
|
"description": "Declarative test framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"unit-testing",
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"directory": "module/test"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/base": "^
|
|
31
|
-
"@travetto/registry": "^
|
|
32
|
-
"@travetto/terminal": "^
|
|
33
|
-
"@travetto/worker": "^
|
|
34
|
-
"
|
|
30
|
+
"@travetto/base": "^5.0.0-rc.1",
|
|
31
|
+
"@travetto/registry": "^5.0.0-rc.1",
|
|
32
|
+
"@travetto/terminal": "^5.0.0-rc.0",
|
|
33
|
+
"@travetto/worker": "^5.0.0-rc.0",
|
|
34
|
+
"yaml": "^2.4.5"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^
|
|
38
|
-
"@travetto/transformer": "^
|
|
37
|
+
"@travetto/cli": "^5.0.0-rc.1",
|
|
38
|
+
"@travetto/transformer": "^5.0.0-rc.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/transformer": {
|
package/src/assert/check.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
3
|
import { RuntimeIndex } from '@travetto/manifest';
|
|
4
|
-
import {
|
|
4
|
+
import { AppError, ClassInstance, Class } from '@travetto/base';
|
|
5
5
|
|
|
6
6
|
import { ThrowableError, TestConfig, Assertion } from '../model/test';
|
|
7
7
|
import { AssertCapture, CaptureAssert } from './capture';
|
|
8
8
|
import { AssertUtil } from './util';
|
|
9
9
|
import { ASSERT_FN_OPERATOR, OP_MAPPING } from './types';
|
|
10
10
|
|
|
11
|
-
declare module 'assert' {
|
|
12
|
-
interface AssertionError {
|
|
13
|
-
toJSON(): Record<string, unknown>;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
type StringFields<T> = {
|
|
18
12
|
[K in Extract<keyof T, string>]:
|
|
19
13
|
(T[K] extends string ? K : never)
|
|
@@ -30,6 +24,7 @@ export class AssertCheck {
|
|
|
30
24
|
* @param args The arguments passed in
|
|
31
25
|
*/
|
|
32
26
|
static check(assertion: CaptureAssert, positive: boolean, ...args: unknown[]): void {
|
|
27
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
33
28
|
assertion.file = RuntimeIndex.getSourceFile(assertion.file);
|
|
34
29
|
|
|
35
30
|
let fn = assertion.operator;
|
|
@@ -46,40 +41,31 @@ export class AssertCheck {
|
|
|
46
41
|
// Check fn to call
|
|
47
42
|
if (fn === 'fail') {
|
|
48
43
|
if (args.length > 1) {
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
50
44
|
[assertion.actual, assertion.expected, assertion.message, assertion.operator] = args as [unknown, unknown, string, string];
|
|
51
45
|
} else {
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
53
46
|
[assertion.message] = args as [string];
|
|
54
47
|
}
|
|
55
48
|
} else if (/throw|reject/i.test(fn)) {
|
|
56
49
|
assertion.operator = fn;
|
|
57
50
|
if (typeof args[1] !== 'string') {
|
|
58
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
59
51
|
[, assertion.expected, assertion.message] = args as [unknown, unknown, string];
|
|
60
52
|
} else {
|
|
61
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
62
53
|
[, assertion.message] = args as [unknown, string];
|
|
63
54
|
}
|
|
64
55
|
} else if (fn === 'ok' || fn === 'assert') {
|
|
65
56
|
fn = assertion.operator = 'ok';
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
67
57
|
[assertion.actual, assertion.message] = args as [unknown, string];
|
|
68
58
|
assertion.expected = { toClean: (): string => positive ? 'truthy' : 'falsy' };
|
|
69
59
|
common.state = 'should be';
|
|
70
60
|
} else if (fn === 'includes') {
|
|
71
61
|
assertion.operator = fn;
|
|
72
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
73
62
|
[assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
|
|
74
63
|
} else if (fn === 'instanceof') {
|
|
75
64
|
assertion.operator = fn;
|
|
76
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
77
65
|
[assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
|
|
78
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
79
66
|
assertion.actual = (assertion.actual as ClassInstance)?.constructor;
|
|
80
67
|
} else { // Handle unknown
|
|
81
68
|
assertion.operator = fn ?? '';
|
|
82
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
83
69
|
[assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
|
|
84
70
|
}
|
|
85
71
|
|
|
@@ -93,36 +79,24 @@ export class AssertCheck {
|
|
|
93
79
|
assertion.expected = AssertUtil.cleanValue(assertion.expected);
|
|
94
80
|
}
|
|
95
81
|
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
97
82
|
const [actual, expected, message] = args as [unknown, unknown, string];
|
|
98
83
|
|
|
99
84
|
// Actually run the assertion
|
|
100
85
|
switch (fn) {
|
|
101
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
102
86
|
case 'includes': assertFn((actual as unknown[]).includes(expected), message); break;
|
|
103
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
104
87
|
case 'test': assertFn((expected as RegExp).test(actual as string), message); break;
|
|
105
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
106
88
|
case 'instanceof': assertFn(actual instanceof (expected as Class), message); break;
|
|
107
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
108
89
|
case 'in': assertFn((actual as string) in (expected as object), message); break;
|
|
109
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
110
90
|
case 'lessThan': assertFn((actual as number) < (expected as number), message); break;
|
|
111
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
112
91
|
case 'lessThanEqual': assertFn((actual as number) <= (expected as number), message); break;
|
|
113
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
114
92
|
case 'greaterThan': assertFn((actual as number) > (expected as number), message); break;
|
|
115
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
116
93
|
case 'greaterThanEqual': assertFn((actual as number) >= (expected as number), message); break;
|
|
117
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
118
94
|
case 'ok': assertFn(...args as [unknown, string]); break;
|
|
119
95
|
default:
|
|
120
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
121
96
|
if (fn && assert[fn as keyof typeof assert]) { // Assert call
|
|
122
97
|
if (/not/i.test(fn)) {
|
|
123
98
|
common.state = 'should not';
|
|
124
99
|
}
|
|
125
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
126
100
|
assert[fn as 'ok'].apply(null, args as [boolean, string | undefined]);
|
|
127
101
|
}
|
|
128
102
|
}
|
|
@@ -144,6 +118,7 @@ export class AssertCheck {
|
|
|
144
118
|
}
|
|
145
119
|
throw err;
|
|
146
120
|
}
|
|
121
|
+
/* eslint-enable @typescript-eslint/consistent-type-assertions */
|
|
147
122
|
}
|
|
148
123
|
|
|
149
124
|
/**
|
|
@@ -205,14 +180,15 @@ export class AssertCheck {
|
|
|
205
180
|
shouldThrow: ThrowableError | undefined,
|
|
206
181
|
assertion: CaptureAssert
|
|
207
182
|
): void {
|
|
208
|
-
if (
|
|
183
|
+
if (!(err instanceof Error)) {
|
|
209
184
|
err = new Error(`${err}`);
|
|
210
185
|
}
|
|
211
186
|
if (!(err instanceof Error)) {
|
|
212
187
|
throw err;
|
|
213
188
|
}
|
|
214
189
|
if (positive) {
|
|
215
|
-
missed = new AppError('Error thrown, but expected no errors'
|
|
190
|
+
missed = new AppError('Error thrown, but expected no errors');
|
|
191
|
+
missed.stack = err.stack;
|
|
216
192
|
}
|
|
217
193
|
|
|
218
194
|
const resolvedErr = (missed && err) ?? this.checkError(shouldThrow, err);
|
|
@@ -244,8 +220,8 @@ export class AssertCheck {
|
|
|
244
220
|
try {
|
|
245
221
|
action();
|
|
246
222
|
if (!positive) {
|
|
247
|
-
if (
|
|
248
|
-
shouldThrow = shouldThrow
|
|
223
|
+
if (typeof shouldThrow === 'function') {
|
|
224
|
+
shouldThrow = shouldThrow.name;
|
|
249
225
|
}
|
|
250
226
|
throw (missed = new AppError(`No error thrown, but expected ${shouldThrow ?? 'an error'}`));
|
|
251
227
|
}
|
|
@@ -282,8 +258,8 @@ export class AssertCheck {
|
|
|
282
258
|
await action();
|
|
283
259
|
}
|
|
284
260
|
if (!positive) {
|
|
285
|
-
if (
|
|
286
|
-
shouldThrow = shouldThrow
|
|
261
|
+
if (typeof shouldThrow === 'function') {
|
|
262
|
+
shouldThrow = shouldThrow.name;
|
|
287
263
|
}
|
|
288
264
|
throw (missed = new AppError(`No error thrown, but expected ${shouldThrow ?? 'an error'} `));
|
|
289
265
|
}
|
package/src/assert/util.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { RuntimeContext } from '@travetto/base';
|
|
4
|
+
import { path } from '@travetto/manifest';
|
|
5
5
|
|
|
6
6
|
import { TestConfig, Assertion, TestResult } from '../model/test';
|
|
7
7
|
import { SuiteConfig } from '../model/suite';
|
|
@@ -19,29 +19,32 @@ export class AssertUtil {
|
|
|
19
19
|
* Clean a value for displaying in the output
|
|
20
20
|
*/
|
|
21
21
|
static cleanValue(val: unknown): unknown {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
switch (typeof val) {
|
|
23
|
+
case 'object': {
|
|
24
|
+
if (isCleanable(val)) {
|
|
25
|
+
return val.toClean();
|
|
26
|
+
} else if (val === null || val.constructor === Object || Array.isArray(val) || val instanceof Date) {
|
|
27
|
+
return JSON.stringify(val);
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'undefined': case 'string': case 'number': case 'bigint': case 'boolean': return JSON.stringify(val);
|
|
32
|
+
case 'function': {
|
|
33
|
+
if (val.Ⲑid || !val.constructor) {
|
|
34
|
+
return val.name;
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
36
37
|
}
|
|
37
38
|
}
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
40
|
+
return util.inspect(val, false, 1).replace(/\n/g, ' ');
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/**
|
|
41
44
|
* Determine file location for a given error and the stack trace
|
|
42
45
|
*/
|
|
43
46
|
static getPositionOfError(err: Error, filename: string): { file: string, line: number } {
|
|
44
|
-
const cwd =
|
|
47
|
+
const cwd = RuntimeContext.mainSourcePath;
|
|
45
48
|
const lines = path.toPosix(err.stack ?? new Error().stack!)
|
|
46
49
|
.split('\n')
|
|
47
50
|
// Exclude node_modules, target self
|
package/src/consumer/error.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AppError, TypedObject } from '@travetto/base';
|
|
2
2
|
|
|
3
3
|
import { TestEvent, } from '../model/event';
|
|
4
4
|
|
|
@@ -25,7 +25,7 @@ export class ErrorUtil {
|
|
|
25
25
|
error[k] = e[k];
|
|
26
26
|
}
|
|
27
27
|
error.name = e.name;
|
|
28
|
-
if (
|
|
28
|
+
if (e instanceof AppError) {
|
|
29
29
|
Object.assign(error, e.toJSON());
|
|
30
30
|
}
|
|
31
31
|
error.message ||= e.message;
|
|
@@ -44,8 +44,11 @@ export class ErrorUtil {
|
|
|
44
44
|
if (isSerialized(e)) {
|
|
45
45
|
const err = new Error();
|
|
46
46
|
|
|
47
|
-
for (const k of TypedObject.keys
|
|
48
|
-
|
|
47
|
+
for (const k of TypedObject.keys(e)) {
|
|
48
|
+
if (k === '$') {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
err[k] = e[k]!;
|
|
49
52
|
}
|
|
50
53
|
err.message = e.message;
|
|
51
54
|
err.stack = e.stack;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { RuntimeIndex } from '@travetto/manifest';
|
|
2
1
|
import { Terminal } from '@travetto/terminal';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { AppError, TimeUtil, RuntimeContext } from '@travetto/base';
|
|
3
|
+
import { stringify } from 'yaml';
|
|
5
4
|
|
|
6
5
|
import { TestEvent } from '../../model/event';
|
|
7
6
|
import { SuitesSummary, TestConsumer } from '../types';
|
|
@@ -44,7 +43,7 @@ export class TapEmitter implements TestConsumer {
|
|
|
44
43
|
*/
|
|
45
44
|
logMeta(obj: Record<string, unknown>): void {
|
|
46
45
|
const lineLength = this.#terminal.width - 5;
|
|
47
|
-
let body =
|
|
46
|
+
let body = stringify(obj, { lineWidth: lineLength });
|
|
48
47
|
body = body.split('\n').map(x => ` ${x}`).join('\n');
|
|
49
48
|
this.log(`---\n${this.#enhancer.objectInspect(body)}\n...`);
|
|
50
49
|
}
|
|
@@ -71,7 +70,7 @@ export class TapEmitter implements TestConsumer {
|
|
|
71
70
|
this.#enhancer.assertNumber(++subCount),
|
|
72
71
|
'-',
|
|
73
72
|
this.#enhancer.assertDescription(text),
|
|
74
|
-
`${this.#enhancer.assertFile(asrt.file.replace(
|
|
73
|
+
`${this.#enhancer.assertFile(asrt.file.replace(RuntimeContext.mainSourcePath, '.'))}:${this.#enhancer.assertLine(asrt.line)}`
|
|
75
74
|
].join(' ');
|
|
76
75
|
|
|
77
76
|
if (asrt.error) {
|
|
@@ -101,9 +100,9 @@ export class TapEmitter implements TestConsumer {
|
|
|
101
100
|
|
|
102
101
|
// Handle error
|
|
103
102
|
if (test.status === 'failed') {
|
|
104
|
-
if (test.error
|
|
103
|
+
if (test.error && test.error.name !== 'AssertionError') {
|
|
105
104
|
const err = ErrorUtil.deserializeError(test.error);
|
|
106
|
-
this.logMeta({ error:
|
|
105
|
+
this.logMeta({ error: err instanceof AppError ? err.toJSON() : err });
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
|
|
@@ -127,8 +126,7 @@ export class TapEmitter implements TestConsumer {
|
|
|
127
126
|
if (summary.errors.length) {
|
|
128
127
|
this.log('---\n');
|
|
129
128
|
for (const err of summary.errors) {
|
|
130
|
-
|
|
131
|
-
this.log(this.#enhancer.failure(ObjectUtil.hasToJSON(err) ? err.toJSON() as string : `${err}`));
|
|
129
|
+
this.log(this.#enhancer.failure(err instanceof AppError ? JSON.stringify(err.toJSON(), null, 2) : `${err}`));
|
|
132
130
|
}
|
|
133
131
|
}
|
|
134
132
|
|
|
@@ -141,7 +139,7 @@ export class TapEmitter implements TestConsumer {
|
|
|
141
139
|
`${this.#enhancer.total(summary.failed)}`,
|
|
142
140
|
'skipped',
|
|
143
141
|
this.#enhancer.total(summary.skipped),
|
|
144
|
-
`# (Total Test Time: ${TimeUtil.
|
|
142
|
+
`# (Total Test Time: ${TimeUtil.asClock(summary.duration)}, Total Run Time: ${TimeUtil.asClock(Date.now() - this.#start)})`
|
|
145
143
|
].join(' '));
|
|
146
144
|
}
|
|
147
145
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Writable } from 'node:stream';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { stringify } from 'yaml';
|
|
4
4
|
|
|
5
5
|
import { TestEvent } from '../../model/event';
|
|
6
6
|
import { SuitesSummary, TestConsumer } from '../types';
|
|
@@ -33,7 +33,7 @@ export class XunitEmitter implements TestConsumer {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
if (Object.keys(obj).length) {
|
|
36
|
-
let body =
|
|
36
|
+
let body = stringify(obj);
|
|
37
37
|
body = body.split('\n').map(x => ` ${x}`).join('\n');
|
|
38
38
|
return `<![CDATA[\n${body}\n]]>`;
|
|
39
39
|
} else {
|
package/src/execute/console.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
|
|
3
|
-
import { ConsoleEvent, ConsoleManager } from '@travetto/base';
|
|
3
|
+
import { ConsoleEvent, ConsoleListener, ConsoleManager } from '@travetto/base';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Console capturer. Hooks into the Console manager, and collects the
|
|
7
7
|
* output into a map for test results
|
|
8
8
|
*/
|
|
9
|
-
export class ConsoleCapture {
|
|
9
|
+
export class ConsoleCapture implements ConsoleListener {
|
|
10
|
+
static #listener: ConsoleListener = ConsoleManager.get();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
out: Record<string, string[]>;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
start(): this {
|
|
14
15
|
this.out = {};
|
|
15
16
|
ConsoleManager.set(this);
|
|
17
|
+
return this;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
log({ level, args }: ConsoleEvent): void {
|
|
19
21
|
(this.out[level] = this.out[level] ?? []).push(
|
|
20
22
|
args
|
|
21
23
|
.map((x => typeof x === 'string' ? x : util.inspect(x, false, 5)))
|
|
@@ -23,10 +25,10 @@ export class ConsoleCapture {
|
|
|
23
25
|
);
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
end(): Record<string, string> {
|
|
27
29
|
const ret = this.out ?? {};
|
|
28
30
|
this.out = {};
|
|
29
|
-
ConsoleManager.
|
|
31
|
+
ConsoleManager.set(ConsoleCapture.#listener);
|
|
30
32
|
return Object.fromEntries(Object.entries(ret).map(([k, v]) => [k, v.join('\n')]));
|
|
31
33
|
}
|
|
32
34
|
}
|
package/src/execute/executor.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AssertionError } from 'node:assert';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import { Env,
|
|
4
|
+
import { RuntimeIndex } from '@travetto/manifest';
|
|
5
|
+
import { Env, TimeUtil, RuntimeContext } from '@travetto/base';
|
|
5
6
|
import { Barrier, ExecutionError } from '@travetto/worker';
|
|
6
7
|
|
|
7
8
|
import { SuiteRegistry } from '../registry/suite';
|
|
@@ -12,10 +13,10 @@ import { AssertCheck } from '../assert/check';
|
|
|
12
13
|
import { AssertCapture } from '../assert/capture';
|
|
13
14
|
import { ConsoleCapture } from './console';
|
|
14
15
|
import { TestPhaseManager } from './phase';
|
|
15
|
-
import {
|
|
16
|
+
import { PromiseCapturer } from './promise';
|
|
16
17
|
import { AssertUtil } from '../assert/util';
|
|
17
18
|
|
|
18
|
-
const TEST_TIMEOUT = Env.TRV_TEST_TIMEOUT.
|
|
19
|
+
const TEST_TIMEOUT = TimeUtil.fromValue(Env.TRV_TEST_TIMEOUT.val) ?? 5000;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Support execution of the tests
|
|
@@ -29,22 +30,21 @@ export class TestExecutor {
|
|
|
29
30
|
*/
|
|
30
31
|
static async #executeTestMethod(test: TestConfig): Promise<Error | undefined> {
|
|
31
32
|
const suite = SuiteRegistry.get(test.class);
|
|
32
|
-
const promCleanup = Util.resolvablePromise();
|
|
33
33
|
|
|
34
34
|
// Ensure all the criteria below are satisfied before moving forward
|
|
35
35
|
const barrier = new Barrier(test.timeout || TEST_TIMEOUT, true)
|
|
36
|
-
.add(promCleanup, true) // If not timeout or unhandled, ensure all promises are cleaned up
|
|
37
36
|
.add(async () => {
|
|
38
37
|
const env = process.env;
|
|
38
|
+
process.env = { ...env }; // Created an isolated environment
|
|
39
|
+
const pCap = new PromiseCapturer();
|
|
39
40
|
|
|
40
41
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
await pCap.run(() =>
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
44
|
+
(suite.instance as Record<string, Function>)[test.methodName]()
|
|
45
|
+
);
|
|
45
46
|
} finally {
|
|
46
47
|
process.env = env; // Restore
|
|
47
|
-
PromiseCapture.stop().then(() => Util.queueMacroTask().then(promCleanup.resolve), promCleanup.reject);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -71,7 +71,7 @@ export class TestExecutor {
|
|
|
71
71
|
const classId = RuntimeIndex.getId(file, name);
|
|
72
72
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
73
73
|
const suite = { class: { name }, classId, duration: 0, lineStart: 1, lineEnd: 1, file, } as SuiteConfig & SuiteResult;
|
|
74
|
-
err.message = err.message.replaceAll(
|
|
74
|
+
err.message = err.message.replaceAll(RuntimeContext.mainSourcePath, '.');
|
|
75
75
|
const res = AssertUtil.generateSuiteError(suite, 'require', err);
|
|
76
76
|
consumer.onEvent({ type: 'suite', phase: 'before', suite });
|
|
77
77
|
consumer.onEvent({ type: 'test', phase: 'before', test: res.testConfig });
|
|
@@ -137,7 +137,7 @@ export class TestExecutor {
|
|
|
137
137
|
})
|
|
138
138
|
);
|
|
139
139
|
|
|
140
|
-
ConsoleCapture.start(); // Capture all output from transpiled code
|
|
140
|
+
const consoleCapture = new ConsoleCapture().start(); // Capture all output from transpiled code
|
|
141
141
|
|
|
142
142
|
// Run method and get result
|
|
143
143
|
let error = await this.#executeTestMethod(test);
|
|
@@ -158,7 +158,7 @@ export class TestExecutor {
|
|
|
158
158
|
|
|
159
159
|
Object.assign(result, {
|
|
160
160
|
status: error ? 'failed' : 'passed',
|
|
161
|
-
output:
|
|
161
|
+
output: consoleCapture.end(),
|
|
162
162
|
assertions: getAssertions(),
|
|
163
163
|
duration: Date.now() - startTime,
|
|
164
164
|
...(error ? { error } : {})
|
package/src/execute/phase.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Barrier } from '@travetto/worker';
|
|
2
|
-
import { Env } from '@travetto/base';
|
|
2
|
+
import { Env, TimeUtil } from '@travetto/base';
|
|
3
3
|
|
|
4
4
|
import { TestConsumer } from '../consumer/types';
|
|
5
5
|
import { SuiteConfig, SuiteResult } from '../model/suite';
|
|
@@ -8,7 +8,7 @@ import { TestResult } from '../model/test';
|
|
|
8
8
|
|
|
9
9
|
class TestBreakout extends Error { }
|
|
10
10
|
|
|
11
|
-
const TEST_PHASE_TIMEOUT = Env.TRV_TEST_PHASE_TIMEOUT.
|
|
11
|
+
const TEST_PHASE_TIMEOUT = TimeUtil.fromValue(Env.TRV_TEST_PHASE_TIMEOUT.val) ?? 15000;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Test Phase Execution Manager.
|
package/src/execute/promise.ts
CHANGED
|
@@ -1,72 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const og = Promise;
|
|
4
|
-
|
|
5
|
-
declare global {
|
|
6
|
-
interface Promise<T> {
|
|
7
|
-
status: 'ok' | 'error';
|
|
8
|
-
}
|
|
9
|
-
}
|
|
1
|
+
import { createHook, executionAsyncId } from 'node:async_hooks';
|
|
10
2
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*/
|
|
14
|
-
function Wrapped(this: Promise<unknown>, ex: (res: (v: unknown) => unknown, rej?: (err: unknown) => unknown) => void): Promise<unknown> {
|
|
15
|
-
const prom = new og(ex);
|
|
16
|
-
this.then = prom.then.bind(prom);
|
|
17
|
-
this.catch = prom.catch.bind(prom);
|
|
18
|
-
this.finally = prom.finally.bind(prom);
|
|
19
|
-
this.then(() => prom.status = 'ok',
|
|
20
|
-
() => prom.status = 'error');
|
|
21
|
-
|
|
22
|
-
if (PromiseCapture.pending) {
|
|
23
|
-
PromiseCapture.pending.push(prom);
|
|
24
|
-
}
|
|
25
|
-
return this;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
Wrapped.allSettled = Promise.allSettled.bind(Promise);
|
|
29
|
-
Wrapped.race = Promise.race.bind(Promise);
|
|
30
|
-
Wrapped.all = Promise.all.bind(Promise);
|
|
31
|
-
Wrapped.resolve = Promise.resolve.bind(Promise);
|
|
32
|
-
Wrapped.reject = Promise.reject.bind(Promise);
|
|
3
|
+
import { ExecutionError } from '@travetto/worker';
|
|
4
|
+
import { Util } from '@travetto/base';
|
|
33
5
|
|
|
34
6
|
/**
|
|
35
7
|
* Promise watcher, to catch any unfinished promises
|
|
36
8
|
*/
|
|
37
|
-
export class
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
global.Promise = Wrapped as unknown as typeof Promise;
|
|
9
|
+
export class PromiseCapturer {
|
|
10
|
+
#pending = new Map<number, Promise<unknown>>();
|
|
11
|
+
#id: number = 0;
|
|
12
|
+
|
|
13
|
+
#init(id: number, type: string, triggerId: number, resource: unknown): void {
|
|
14
|
+
if (this.#id && type === 'PROMISE' && triggerId === this.#id) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
16
|
+
this.#pending.set(id, resource as Promise<unknown>);
|
|
17
|
+
}
|
|
47
18
|
}
|
|
48
19
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*/
|
|
52
|
-
static async resolvePending(pending: Promise<unknown>[]): Promise<void> {
|
|
53
|
-
if (pending.length) {
|
|
54
|
-
let final: Error | undefined;
|
|
55
|
-
console.debug('Resolving', { pending: this.pending.length });
|
|
56
|
-
await Promise.all(pending).catch(err => final = err);
|
|
57
|
-
|
|
58
|
-
// If any return in an error, make that the final result
|
|
59
|
-
throw new ExecutionError(`Pending promises: ${pending.length}`, final?.stack);
|
|
60
|
-
}
|
|
20
|
+
#promiseResolve(asyncId: number): void {
|
|
21
|
+
this.#pending.delete(asyncId);
|
|
61
22
|
}
|
|
62
23
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
24
|
+
async run(op: () => Promise<unknown> | unknown): Promise<unknown> {
|
|
25
|
+
const hook = createHook({
|
|
26
|
+
init: (...args) => this.#init(...args),
|
|
27
|
+
promiseResolve: (id) => this.#promiseResolve(id)
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
hook.enable();
|
|
31
|
+
|
|
32
|
+
await Util.queueMacroTask();
|
|
33
|
+
this.#id = executionAsyncId();
|
|
34
|
+
try {
|
|
35
|
+
const res = await op();
|
|
36
|
+
let i = 5; // Wait upto 5 macro tasks before continuing
|
|
37
|
+
while (this.#pending.size) {
|
|
38
|
+
await Util.queueMacroTask();
|
|
39
|
+
i -= 1;
|
|
40
|
+
if (i === 0) {
|
|
41
|
+
throw new ExecutionError(`Pending promises: ${this.#pending.size}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return res;
|
|
45
|
+
} finally {
|
|
46
|
+
hook.disable();
|
|
47
|
+
}
|
|
71
48
|
}
|
|
72
49
|
}
|
package/src/execute/runner.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { path as mp, RuntimeIndex } from '@travetto/manifest';
|
|
4
|
+
import { TimeUtil, RuntimeContext } from '@travetto/base';
|
|
3
5
|
import { WorkPool } from '@travetto/worker';
|
|
4
6
|
|
|
5
7
|
import { buildStandardTestManager } from '../worker/standard';
|
|
@@ -21,7 +23,7 @@ export class Runner {
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
get patterns(): RegExp[] {
|
|
24
|
-
return this.#state.args.map(x => new RegExp(
|
|
26
|
+
return this.#state.args.map(x => new RegExp(mp.toPosix(x)));
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -37,10 +39,10 @@ export class Runner {
|
|
|
37
39
|
const testCount = await RunnerUtil.getTestCount(this.#state.args);
|
|
38
40
|
await consumer.onStart({ testCount });
|
|
39
41
|
await WorkPool.run(
|
|
40
|
-
(
|
|
42
|
+
buildStandardTestManager.bind(null, consumer),
|
|
41
43
|
files,
|
|
42
44
|
{
|
|
43
|
-
idleTimeoutMillis: TimeUtil.
|
|
45
|
+
idleTimeoutMillis: TimeUtil.asMillis(10, 's'),
|
|
44
46
|
min: 1,
|
|
45
47
|
max: this.#state.concurrency,
|
|
46
48
|
});
|
package/src/execute/util.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { createReadStream } from 'node:fs';
|
|
|
3
3
|
import readline from 'node:readline';
|
|
4
4
|
|
|
5
5
|
import { Env, ExecUtil, ShutdownManager, Util } from '@travetto/base';
|
|
6
|
-
import { IndexedFile, RuntimeIndex } from '@travetto/manifest';
|
|
6
|
+
import { type IndexedFile, RuntimeIndex } from '@travetto/manifest';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Simple Test Utilities
|
package/src/execute/watcher.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { RootRegistry, MethodSource } from '@travetto/registry';
|
|
2
2
|
import { WorkPool, WorkQueue } from '@travetto/worker';
|
|
3
3
|
import { RuntimeIndex } from '@travetto/manifest';
|
|
4
|
-
import { ObjectUtil } from '@travetto/base';
|
|
5
4
|
|
|
6
5
|
import { SuiteRegistry } from '../registry/suite';
|
|
7
6
|
import { buildStandardTestManager } from '../worker/standard';
|
|
@@ -12,7 +11,7 @@ import { RunnerUtil } from './util';
|
|
|
12
11
|
import { TestEvent } from '../model/event';
|
|
13
12
|
|
|
14
13
|
function isRunEvent(ev: unknown): ev is RunEvent {
|
|
15
|
-
return
|
|
14
|
+
return typeof ev === 'object' && !!ev && 'type' in ev && typeof ev.type === 'string' && ev.type === 'run-test';
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
export type TestWatchEvent =
|
|
@@ -88,7 +87,7 @@ export class TestWatcher {
|
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
await WorkPool.run(
|
|
91
|
-
(
|
|
90
|
+
buildStandardTestManager.bind(null, consumer),
|
|
92
91
|
itr,
|
|
93
92
|
{
|
|
94
93
|
idleTimeoutMillis: 120000,
|
package/src/fixture.ts
CHANGED
|
@@ -2,6 +2,6 @@ import { FileLoader } from '@travetto/base';
|
|
|
2
2
|
|
|
3
3
|
export class TestFixtures extends FileLoader {
|
|
4
4
|
constructor(modules: string[] = []) {
|
|
5
|
-
super(['@#test/fixtures', ...['@', ...modules.flat()].map(x => `${x}#support/fixtures`)]);
|
|
5
|
+
super(['@#test/fixtures', ...['@', ...modules.flat(), '@@'].map(x => `${x}#support/fixtures`)]);
|
|
6
6
|
}
|
|
7
7
|
}
|
package/src/registry/suite.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Class, ConcreteClass } from '@travetto/base';
|
|
2
|
-
import { RuntimeIndex, RuntimeContext } from '@travetto/manifest';
|
|
1
|
+
import { Class, ConcreteClass, RuntimeContext } from '@travetto/base';
|
|
3
2
|
import { MetadataRegistry } from '@travetto/registry';
|
|
3
|
+
import { RuntimeIndex } from '@travetto/manifest';
|
|
4
4
|
|
|
5
5
|
import { SuiteConfig } from '../model/suite';
|
|
6
6
|
import { TestConfig } from '../model/test';
|
package/src/worker/child.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
2
|
|
|
3
|
-
import { RuntimeContext } from '@travetto/
|
|
4
|
-
import { ConsoleManager, Env, TimeUtil, Util } from '@travetto/base';
|
|
3
|
+
import { ConsoleManager, Env, Util, RuntimeContext } from '@travetto/base';
|
|
5
4
|
import { ChildCommChannel } from '@travetto/worker';
|
|
6
5
|
|
|
7
6
|
import { ErrorUtil } from '../consumer/error';
|
|
@@ -15,6 +14,8 @@ import { Events, RunEvent } from './types';
|
|
|
15
14
|
*/
|
|
16
15
|
export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
17
16
|
|
|
17
|
+
#done = Util.resolvablePromise();
|
|
18
|
+
|
|
18
19
|
async #exec(op: () => Promise<unknown>, type: string): Promise<void> {
|
|
19
20
|
try {
|
|
20
21
|
await op();
|
|
@@ -36,9 +37,9 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
36
37
|
const file = RuntimeContext.toolPath(`test-worker.${process.pid}.log`);
|
|
37
38
|
const stdout = createWriteStream(file, { flags: 'a' });
|
|
38
39
|
const c = new console.Console({ stdout, inspectOptions: { depth: 4, colors: false } });
|
|
39
|
-
ConsoleManager.set({
|
|
40
|
+
ConsoleManager.set({ log: (ev) => c[ev.level](process.pid, ...ev.args) });
|
|
40
41
|
} else {
|
|
41
|
-
ConsoleManager.set({
|
|
42
|
+
ConsoleManager.set({ log: () => { } });
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
RunnerUtil.registerCleanup('worker');
|
|
@@ -49,7 +50,7 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
49
50
|
// Let parent know the child is ready for handling commands
|
|
50
51
|
this.send(Events.READY);
|
|
51
52
|
|
|
52
|
-
await
|
|
53
|
+
await this.#done.promise;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
/**
|
|
@@ -80,11 +81,15 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
80
81
|
|
|
81
82
|
console.debug('Running', { file: event.file });
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
try {
|
|
85
|
+
await new Runner({
|
|
86
|
+
format: 'exec',
|
|
87
|
+
mode: 'single',
|
|
88
|
+
args: [event.file!, event.class!, event.method!],
|
|
89
|
+
concurrency: 1
|
|
90
|
+
}).run();
|
|
91
|
+
} finally {
|
|
92
|
+
this.#done.resolve();
|
|
93
|
+
}
|
|
89
94
|
}
|
|
90
95
|
}
|
package/src/worker/standard.ts
CHANGED
|
@@ -2,88 +2,72 @@ import { fork } from 'node:child_process';
|
|
|
2
2
|
|
|
3
3
|
import { RuntimeIndex } from '@travetto/manifest';
|
|
4
4
|
import { Env } from '@travetto/base';
|
|
5
|
-
import { ParentCommChannel
|
|
5
|
+
import { ParentCommChannel } from '@travetto/worker';
|
|
6
6
|
|
|
7
7
|
import { Events, RunEvent } from './types';
|
|
8
8
|
import { TestConsumer } from '../consumer/types';
|
|
9
9
|
import { ErrorUtil } from '../consumer/error';
|
|
10
10
|
import { TestEvent } from '../model/event';
|
|
11
11
|
|
|
12
|
-
let i = 0;
|
|
13
|
-
|
|
14
|
-
function buildEvent(ev: string): RunEvent {
|
|
15
|
-
if (ev.includes('#')) {
|
|
16
|
-
const [file, cls, method] = ev.split('#');
|
|
17
|
-
return { file, class: cls, method };
|
|
18
|
-
} else {
|
|
19
|
-
return { file: ev };
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
12
|
/**
|
|
24
13
|
* Produce a handler for the child worker
|
|
25
14
|
*/
|
|
26
|
-
export function buildStandardTestManager(consumer: TestConsumer):
|
|
27
|
-
|
|
28
|
-
* Spawn a child
|
|
29
|
-
*/
|
|
30
|
-
return {
|
|
31
|
-
id: i += 1,
|
|
32
|
-
active: true,
|
|
33
|
-
async destroy(): Promise<void> { },
|
|
34
|
-
async execute(file: string): Promise<void> {
|
|
35
|
-
|
|
36
|
-
process.send?.({ type: 'log', message: `Worker Executing ${file}` });
|
|
37
|
-
|
|
38
|
-
const event = buildEvent(file);
|
|
15
|
+
export async function buildStandardTestManager(consumer: TestConsumer, file: string): Promise<void> {
|
|
16
|
+
process.send?.({ type: 'log', message: `Worker Executing ${file}` });
|
|
39
17
|
|
|
40
|
-
|
|
41
|
-
|
|
18
|
+
let event: RunEvent;
|
|
19
|
+
if (file.includes('#')) {
|
|
20
|
+
const [f, cls, method] = file.split('#');
|
|
21
|
+
event = { file: f, class: cls, method };
|
|
22
|
+
} else {
|
|
23
|
+
event = { file };
|
|
24
|
+
}
|
|
42
25
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
26
|
+
const { module } = RuntimeIndex.getEntry(event.file!)!;
|
|
27
|
+
const cwd = RuntimeIndex.getModule(module)!.sourcePath;
|
|
28
|
+
|
|
29
|
+
const channel = new ParentCommChannel<TestEvent & { error?: Error }>(
|
|
30
|
+
fork(
|
|
31
|
+
RuntimeIndex.resolveFileImport('@travetto/cli/support/entry.trv'), ['test:child'],
|
|
32
|
+
{
|
|
33
|
+
cwd,
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
...Env.TRV_MANIFEST.export(RuntimeIndex.getModule(module)!.outputPath),
|
|
37
|
+
...Env.TRV_QUIET.export(true)
|
|
38
|
+
},
|
|
39
|
+
stdio: ['ignore', 'ignore', 2, 'ipc']
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
);
|
|
57
43
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
44
|
+
await channel.once(Events.READY); // Wait for the child to be ready
|
|
45
|
+
await channel.send(Events.INIT); // Initialize
|
|
46
|
+
await channel.once(Events.INIT_COMPLETE); // Wait for complete
|
|
61
47
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
48
|
+
channel.on('*', async ev => {
|
|
49
|
+
try {
|
|
50
|
+
await consumer.onEvent(ev); // Connect the consumer with the event stream from the child
|
|
51
|
+
} catch {
|
|
52
|
+
// Do nothing
|
|
53
|
+
}
|
|
54
|
+
});
|
|
69
55
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
// Listen for child to complete
|
|
57
|
+
const complete = channel.once(Events.RUN_COMPLETE);
|
|
58
|
+
// Start test
|
|
59
|
+
channel.send(Events.RUN, event);
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
|
|
61
|
+
// Wait for complete
|
|
62
|
+
const { error } = await complete;
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
// Kill on complete
|
|
65
|
+
await channel.destroy();
|
|
80
66
|
|
|
81
|
-
|
|
67
|
+
process.send?.({ type: 'log', message: `Worker Finished ${file}` });
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
};
|
|
69
|
+
// If we received an error, throw it
|
|
70
|
+
if (error) {
|
|
71
|
+
throw ErrorUtil.deserializeError(error);
|
|
72
|
+
}
|
|
89
73
|
}
|
package/support/cli.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
3
4
|
|
|
4
|
-
import { path } from '@travetto/manifest';
|
|
5
5
|
import { Env } from '@travetto/base';
|
|
6
6
|
import { CliCommandShape, CliCommand, CliValidationError } from '@travetto/cli';
|
|
7
7
|
import { WorkPool } from '@travetto/worker';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
|
|
3
|
-
import { Env
|
|
3
|
+
import { Env } from '@travetto/base';
|
|
4
4
|
import { CliCommand } from '@travetto/cli';
|
|
5
5
|
|
|
6
6
|
/** Test child worker target */
|
|
@@ -17,7 +17,7 @@ export class TestChildWorkerCommand {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
async main(): Promise<void> {
|
|
20
|
-
|
|
20
|
+
process.once('disconnect', () => process.exit());
|
|
21
21
|
const { TestChildWorker } = await import('../src/worker/child');
|
|
22
22
|
return new TestChildWorker().activate();
|
|
23
23
|
}
|