@travetto/test 4.0.0-rc.0 → 4.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/package.json +8 -8
- package/src/assert/check.ts +16 -4
- package/src/consumer/error.ts +2 -2
- package/src/execute/executor.ts +6 -4
- package/src/execute/util.ts +7 -8
- package/src/execute/watcher.ts +8 -1
- package/src/worker/child.ts +3 -2
- package/src/worker/standard.ts +6 -4
- package/support/bin/types.ts +1 -1
- package/support/cli.test.ts +0 -2
- package/support/cli.test_child.ts +2 -2
- package/support/cli.test_watch.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/test",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.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": "^4.0.0-rc.
|
|
31
|
-
"@travetto/registry": "^4.0.0-rc.
|
|
32
|
-
"@travetto/terminal": "^4.0.0-rc.
|
|
33
|
-
"@travetto/worker": "^4.0.0-rc.
|
|
34
|
-
"@travetto/yaml": "^4.0.0-rc.
|
|
30
|
+
"@travetto/base": "^4.0.0-rc.1",
|
|
31
|
+
"@travetto/registry": "^4.0.0-rc.1",
|
|
32
|
+
"@travetto/terminal": "^4.0.0-rc.1",
|
|
33
|
+
"@travetto/worker": "^4.0.0-rc.1",
|
|
34
|
+
"@travetto/yaml": "^4.0.0-rc.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^4.0.0-rc.
|
|
38
|
-
"@travetto/transformer": "^4.0.0-rc.
|
|
37
|
+
"@travetto/cli": "^4.0.0-rc.1",
|
|
38
|
+
"@travetto/transformer": "^4.0.0-rc.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/transformer": {
|
package/src/assert/check.ts
CHANGED
|
@@ -115,7 +115,7 @@ export class AssertCheck {
|
|
|
115
115
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
116
116
|
case 'greaterThanEqual': assertFn((actual as number) >= (expected as number), message); break;
|
|
117
117
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
118
|
-
case 'ok': assertFn
|
|
118
|
+
case 'ok': assertFn(...args as [unknown, string]); break; // eslint-disable-line prefer-spread
|
|
119
119
|
default:
|
|
120
120
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
121
121
|
if (fn && assert[fn as keyof typeof assert]) { // Assert call
|
|
@@ -161,11 +161,19 @@ export class AssertCheck {
|
|
|
161
161
|
|
|
162
162
|
// If a string, check if error exists, and then see if the string is included in the message
|
|
163
163
|
if (typeof shouldThrow === 'string' && (!err || !(err instanceof Error ? err.message : err).includes(shouldThrow))) {
|
|
164
|
-
return new assert.AssertionError({
|
|
164
|
+
return new assert.AssertionError({
|
|
165
|
+
message: `Expected error containing text '${shouldThrow}', but got ${actual}`,
|
|
166
|
+
actual,
|
|
167
|
+
expected: shouldThrow
|
|
168
|
+
});
|
|
165
169
|
}
|
|
166
170
|
// If a regexp, check if error exists, and then test the error message against the regex
|
|
167
171
|
if (shouldThrow instanceof RegExp && (!err || !shouldThrow.test(typeof err === 'string' ? err : err.message))) {
|
|
168
|
-
return new assert.AssertionError({
|
|
172
|
+
return new assert.AssertionError({
|
|
173
|
+
message: `Expected error with message matching '${shouldThrow.source}', but got ${actual}`,
|
|
174
|
+
actual,
|
|
175
|
+
expected: shouldThrow.source
|
|
176
|
+
});
|
|
169
177
|
}
|
|
170
178
|
// If passing in a constructor
|
|
171
179
|
} else if (shouldThrow === Error ||
|
|
@@ -173,7 +181,11 @@ export class AssertCheck {
|
|
|
173
181
|
Object.getPrototypeOf(shouldThrow) !== Object.getPrototypeOf(Function)
|
|
174
182
|
) { // if not simple function, treat as class
|
|
175
183
|
if (!err || !(err instanceof shouldThrow)) {
|
|
176
|
-
return new assert.AssertionError({
|
|
184
|
+
return new assert.AssertionError({
|
|
185
|
+
message: `Expected to throw ${shouldThrow.name}, but got ${err ?? 'nothing'}`,
|
|
186
|
+
actual: (err ?? 'nothing'),
|
|
187
|
+
expected: shouldThrow.name
|
|
188
|
+
});
|
|
177
189
|
}
|
|
178
190
|
} else {
|
|
179
191
|
// Else treat as a simple function to build an error or not
|
package/src/consumer/error.ts
CHANGED
|
@@ -28,8 +28,8 @@ export class ErrorUtil {
|
|
|
28
28
|
if (ObjectUtil.hasToJSON(e)) {
|
|
29
29
|
Object.assign(error, e.toJSON());
|
|
30
30
|
}
|
|
31
|
-
error.message
|
|
32
|
-
error.stack ??= e.stack;
|
|
31
|
+
error.message ||= e.message;
|
|
32
|
+
error.stack ??= e.stack?.replace(/.*\[ERR_ASSERTION\]:\s*/, '');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
return error;
|
package/src/execute/executor.ts
CHANGED
|
@@ -140,13 +140,15 @@ export class TestExecutor {
|
|
|
140
140
|
// Run method and get result
|
|
141
141
|
let error = await this.#executeTestMethod(test);
|
|
142
142
|
|
|
143
|
-
if (error) {
|
|
143
|
+
if (!error) {
|
|
144
|
+
error = AssertCheck.checkError(test.shouldThrow, error); // Rewrite error
|
|
145
|
+
} else {
|
|
144
146
|
if (error instanceof AssertionError) {
|
|
145
|
-
// Pass
|
|
147
|
+
// Pass, do nothing
|
|
146
148
|
} else if (error instanceof ExecutionError) { // Errors that are not expected
|
|
147
149
|
AssertCheck.checkUnhandled(test, error);
|
|
148
|
-
} else if (test.shouldThrow) {
|
|
149
|
-
error = AssertCheck.checkError(test.shouldThrow
|
|
150
|
+
} else if (test.shouldThrow) {
|
|
151
|
+
error = AssertCheck.checkError(test.shouldThrow, error); // Rewrite error
|
|
150
152
|
} else if (error instanceof Error) {
|
|
151
153
|
AssertCheck.checkUnhandled(test, error);
|
|
152
154
|
}
|
package/src/execute/util.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
1
2
|
import { createReadStream } from 'node:fs';
|
|
2
3
|
import readline from 'node:readline';
|
|
3
4
|
import timers from 'node:timers/promises';
|
|
@@ -43,7 +44,7 @@ export class RunnerUtil {
|
|
|
43
44
|
folder: f => f === 'test',
|
|
44
45
|
file: f => f.role === 'test'
|
|
45
46
|
})
|
|
46
|
-
.filter(f => globs?.some(g => g.test(f.
|
|
47
|
+
.filter(f => globs?.some(g => g.test(f.sourceFile)) ?? true);
|
|
47
48
|
|
|
48
49
|
const validFiles = files
|
|
49
50
|
.map(f => this.isTestFile(f.sourceFile).then(valid => ({ file: f, valid })));
|
|
@@ -59,14 +60,12 @@ export class RunnerUtil {
|
|
|
59
60
|
* @returns
|
|
60
61
|
*/
|
|
61
62
|
static async getTestCount(patterns: string[]): Promise<number> {
|
|
62
|
-
const
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
63
|
+
const countRes = await ExecUtil.getResult(
|
|
64
|
+
spawn('npx', ['trv', 'test:count', ...patterns], {
|
|
65
|
+
env: { ...process.env, ...Env.FORCE_COLOR.export(0), ...Env.NO_COLOR.export(true) }
|
|
66
|
+
}),
|
|
67
|
+
{ catch: true }
|
|
68
68
|
);
|
|
69
|
-
const countRes = await proc.result;
|
|
70
69
|
if (!countRes.valid) {
|
|
71
70
|
throw new Error(countRes.stderr);
|
|
72
71
|
}
|
package/src/execute/watcher.ts
CHANGED
|
@@ -9,11 +9,18 @@ import { TestConsumerRegistry } from '../consumer/registry';
|
|
|
9
9
|
import { CumulativeSummaryConsumer } from '../consumer/types/cumulative';
|
|
10
10
|
import { RunEvent } from '../worker/types';
|
|
11
11
|
import { RunnerUtil } from './util';
|
|
12
|
+
import { TestEvent } from '../model/event';
|
|
12
13
|
|
|
13
14
|
function isRunEvent(ev: unknown): ev is RunEvent {
|
|
14
15
|
return ObjectUtil.isPlainObject(ev) && 'type' in ev && typeof ev.type === 'string' && ev.type === 'run-test';
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
export type TestWatchEvent =
|
|
19
|
+
TestEvent |
|
|
20
|
+
{ type: 'removeTest', method: string, file: string, classId: string } |
|
|
21
|
+
{ type: 'ready' } |
|
|
22
|
+
{ type: 'log', message: string };
|
|
23
|
+
|
|
17
24
|
/**
|
|
18
25
|
* Test Watcher.
|
|
19
26
|
*
|
|
@@ -71,7 +78,7 @@ export class TestWatcher {
|
|
|
71
78
|
}
|
|
72
79
|
});
|
|
73
80
|
|
|
74
|
-
process.send?.('ready');
|
|
81
|
+
process.send?.({ type: 'ready' });
|
|
75
82
|
|
|
76
83
|
if (runAllOnStart) {
|
|
77
84
|
for (const test of await RunnerUtil.getTestFiles()) {
|
package/src/worker/child.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createWriteStream } from 'node:fs';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { RuntimeContext } from '@travetto/manifest';
|
|
5
5
|
import { ConsoleManager, Env, TimeUtil } from '@travetto/base';
|
|
6
6
|
import { ChildCommChannel } from '@travetto/worker';
|
|
7
7
|
|
|
@@ -34,7 +34,8 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
34
34
|
*/
|
|
35
35
|
async activate(): Promise<void> {
|
|
36
36
|
if (/\b@travetto[/]test\b/.test(Env.DEBUG.val ?? '')) {
|
|
37
|
-
const
|
|
37
|
+
const file = RuntimeContext.toolPath(`test-worker.${process.pid}.log`);
|
|
38
|
+
const stdout = createWriteStream(file, { flags: 'a' });
|
|
38
39
|
const c = new console.Console({ stdout, inspectOptions: { depth: 4, colors: false } });
|
|
39
40
|
ConsoleManager.set({ onLog: (ev) => c[ev.level](process.pid, ...ev.args) });
|
|
40
41
|
} else {
|
package/src/worker/standard.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { fork } from 'node:child_process';
|
|
2
|
+
|
|
1
3
|
import { RuntimeIndex } from '@travetto/manifest';
|
|
2
|
-
import { Env
|
|
4
|
+
import { Env } from '@travetto/base';
|
|
3
5
|
import { ParentCommChannel, Worker } from '@travetto/worker';
|
|
4
6
|
|
|
5
7
|
import { Events, RunEvent } from './types';
|
|
@@ -39,12 +41,12 @@ export function buildStandardTestManager(consumer: TestConsumer): Worker<string>
|
|
|
39
41
|
const cwd = RuntimeIndex.getModule(module)!.sourcePath;
|
|
40
42
|
|
|
41
43
|
const channel = new ParentCommChannel<TestEvent & { error?: Error }>(
|
|
42
|
-
|
|
43
|
-
RuntimeIndex.resolveFileImport('@travetto/cli/support/entry.trv'),
|
|
44
|
-
['test:child'],
|
|
44
|
+
fork(
|
|
45
|
+
RuntimeIndex.resolveFileImport('@travetto/cli/support/entry.trv'), ['test:child'],
|
|
45
46
|
{
|
|
46
47
|
cwd,
|
|
47
48
|
env: {
|
|
49
|
+
...process.env,
|
|
48
50
|
...Env.TRV_MANIFEST.export(RuntimeIndex.getModule(module)!.outputPath),
|
|
49
51
|
...Env.TRV_QUIET.export(true)
|
|
50
52
|
},
|
package/support/bin/types.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type TestFormat = 'tap' | 'tap-streamed' | 'xunit' | 'event' | 'exec';
|
|
1
|
+
export type TestFormat = 'tap' | 'tap-streamed' | 'xunit' | 'event' | 'exec' | 'json';
|
|
2
2
|
export type TestMode = 'single' | 'standard';
|
package/support/cli.test.ts
CHANGED
|
@@ -45,8 +45,6 @@ export class TestCommand implements CliCommandShape {
|
|
|
45
45
|
|
|
46
46
|
if (mode === 'single' && !await this.isFirstFile(first)) {
|
|
47
47
|
return { message: 'You must specify a proper test file to run in single mode', source: 'arg' };
|
|
48
|
-
} else if (!/test\//.test(first)) {
|
|
49
|
-
return { message: 'Only files in the test/ folder are permitted to be run', source: 'arg' };
|
|
50
48
|
}
|
|
51
49
|
}
|
|
52
50
|
|
|
@@ -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.js');
|
|
22
22
|
return new TestChildWorker().activate();
|
|
23
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Env
|
|
1
|
+
import { Env } from '@travetto/base';
|
|
2
2
|
import { CliCommand, CliUtil } from '@travetto/cli';
|
|
3
3
|
|
|
4
4
|
import { TestFormat } from './bin/types';
|
|
@@ -22,7 +22,7 @@ export class TestWatcherCommand {
|
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
process.once('disconnect', () => process.exit());
|
|
26
26
|
|
|
27
27
|
try {
|
|
28
28
|
const { TestWatcher } = await import('../src/execute/watcher.js');
|