@travetto/test 7.0.0-rc.0 → 7.0.0-rc.2
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 +6 -6
- package/package.json +7 -7
- package/src/assert/check.ts +46 -46
- package/src/assert/util.ts +30 -30
- package/src/consumer/registry-index.ts +4 -4
- package/src/consumer/types/cumulative.ts +10 -10
- package/src/consumer/types/delegating.ts +17 -17
- package/src/consumer/types/runnable.ts +3 -3
- package/src/consumer/types/summarizer.ts +10 -10
- package/src/consumer/types/tap-summary.ts +20 -20
- package/src/consumer/types/tap.ts +15 -15
- package/src/consumer/types/xunit.ts +15 -15
- package/src/decorator/suite.ts +2 -2
- package/src/decorator/test.ts +6 -4
- package/src/execute/barrier.ts +8 -8
- package/src/execute/console.ts +1 -1
- package/src/execute/executor.ts +11 -11
- package/src/execute/phase.ts +6 -6
- package/src/execute/runner.ts +4 -4
- package/src/execute/util.ts +11 -11
- package/src/execute/watcher.ts +16 -17
- package/src/fixture.ts +2 -2
- package/src/model/suite.ts +1 -1
- package/src/model/test.ts +1 -1
- package/src/registry/registry-adapter.ts +20 -20
- package/src/registry/registry-index.ts +16 -15
- package/src/worker/child.ts +10 -10
- package/src/worker/standard.ts +3 -3
- package/support/bin/run.ts +6 -6
- package/support/cli.test.ts +2 -2
- package/support/cli.test_digest.ts +5 -5
- package/support/cli.test_direct.ts +1 -1
- package/support/cli.test_watch.ts +2 -2
- package/support/transformer.assert.ts +12 -12
|
@@ -5,8 +5,8 @@ import { SuiteConfig } from '../model/suite.ts';
|
|
|
5
5
|
import { TestConfig, TestRun } from '../model/test.ts';
|
|
6
6
|
import { SuiteRegistryAdapter } from './registry-adapter.ts';
|
|
7
7
|
|
|
8
|
-
const sortedTests = (
|
|
9
|
-
Object.values(
|
|
8
|
+
const sortedTests = (config: SuiteConfig): TestConfig[] =>
|
|
9
|
+
Object.values(config.tests).toSorted((a, b) => a.lineStart - b.lineStart);
|
|
10
10
|
|
|
11
11
|
type SuiteTests = { suite: SuiteConfig, tests: TestConfig[] };
|
|
12
12
|
|
|
@@ -51,7 +51,7 @@ export class SuiteRegistryIndex implements RegistryIndex {
|
|
|
51
51
|
* Find all valid tests (ignoring abstract)
|
|
52
52
|
*/
|
|
53
53
|
getValidClasses(): Class[] {
|
|
54
|
-
return this.store.getClasses().filter(
|
|
54
|
+
return this.store.getClasses().filter(cls => !describeFunction(cls).abstract);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
getConfig(cls: Class): SuiteConfig {
|
|
@@ -70,27 +70,28 @@ export class SuiteRegistryIndex implements RegistryIndex {
|
|
|
70
70
|
const line = parseInt(clsId, 10);
|
|
71
71
|
const suites = this.getValidClasses()
|
|
72
72
|
.filter(cls => Runtime.getImport(cls) === imp)
|
|
73
|
-
.map(
|
|
74
|
-
|
|
73
|
+
.map(cls => this.getConfig(cls))
|
|
74
|
+
.filter(config => !config.skip);
|
|
75
|
+
const suite = suites.find(config => line >= config.lineStart && line <= config.lineEnd);
|
|
75
76
|
|
|
76
77
|
if (suite) {
|
|
77
78
|
const tests = sortedTests(suite);
|
|
78
|
-
const test = tests.find(
|
|
79
|
+
const test = tests.find(config => line >= config.lineStart && line <= config.lineEnd);
|
|
79
80
|
return test ? [{ suite, tests: [test] }] : [{ suite, tests }];
|
|
80
81
|
} else {
|
|
81
|
-
return suites.map(
|
|
82
|
+
return suites.map(config => ({ suite: config, tests: sortedTests(config) }));
|
|
82
83
|
}
|
|
83
84
|
} else { // Else lookup directly
|
|
84
85
|
if (methodNames.length) {
|
|
85
|
-
const cls = this.getValidClasses().find(
|
|
86
|
+
const cls = this.getValidClasses().find(type => type.Ⲑid === clsId);
|
|
86
87
|
if (!cls) {
|
|
87
88
|
throw new AppError('Unable to find suite for class ID', { details: { classId: clsId } });
|
|
88
89
|
}
|
|
89
90
|
const suite = this.getConfig(cls);
|
|
90
|
-
const tests = sortedTests(suite).filter(
|
|
91
|
+
const tests = sortedTests(suite).filter(config => methodNames.includes(config.methodName));
|
|
91
92
|
return [{ suite, tests }];
|
|
92
93
|
} else if (clsId) {
|
|
93
|
-
const cls = this.getValidClasses().find(
|
|
94
|
+
const cls = this.getValidClasses().find(type => type.Ⲑid === clsId)!;
|
|
94
95
|
if (!cls) {
|
|
95
96
|
throw new AppError('Unable to find suite for class ID', { details: { classId: clsId } });
|
|
96
97
|
}
|
|
@@ -98,9 +99,9 @@ export class SuiteRegistryIndex implements RegistryIndex {
|
|
|
98
99
|
return suite ? [{ suite, tests: sortedTests(suite) }] : [];
|
|
99
100
|
} else {
|
|
100
101
|
const suites = this.getValidClasses()
|
|
101
|
-
.map(
|
|
102
|
-
.filter(
|
|
103
|
-
return suites.map(
|
|
102
|
+
.map(type => this.getConfig(type))
|
|
103
|
+
.filter(config => !describeFunction(config.class).abstract); // Do not run abstract suites
|
|
104
|
+
return suites.map(config => ({ suite: config, tests: sortedTests(config) }));
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -110,8 +111,8 @@ export class SuiteRegistryIndex implements RegistryIndex {
|
|
|
110
111
|
*/
|
|
111
112
|
getTestConfig(cls: Class, method: Function): TestConfig | undefined {
|
|
112
113
|
if (this.store.has(cls)) {
|
|
113
|
-
const
|
|
114
|
-
return Object.values(
|
|
114
|
+
const config = this.getConfig(cls);
|
|
115
|
+
return Object.values(config.tests).find(item => item.methodName === method.name);
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
}
|
package/src/worker/child.ts
CHANGED
|
@@ -16,16 +16,16 @@ export class TestChildWorker extends IpcChannel<TestRun> {
|
|
|
16
16
|
|
|
17
17
|
#done = Promise.withResolvers<void>();
|
|
18
18
|
|
|
19
|
-
async #exec(
|
|
19
|
+
async #exec(operation: () => Promise<unknown>, type: string): Promise<void> {
|
|
20
20
|
try {
|
|
21
|
-
await
|
|
21
|
+
await operation();
|
|
22
22
|
this.send(type); // Respond
|
|
23
|
-
} catch (
|
|
24
|
-
if (!(
|
|
25
|
-
throw
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (!(error instanceof Error)) {
|
|
25
|
+
throw error;
|
|
26
26
|
}
|
|
27
27
|
// Mark as errored out
|
|
28
|
-
this.send(type, JSON.parse(Util.serializeToJSON({ error
|
|
28
|
+
this.send(type, JSON.parse(Util.serializeToJSON({ error })));
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -33,11 +33,11 @@ export class TestChildWorker extends IpcChannel<TestRun> {
|
|
|
33
33
|
* Start the worker
|
|
34
34
|
*/
|
|
35
35
|
async activate(): Promise<void> {
|
|
36
|
-
if (/\b@travetto[/]test\b/.test(Env.DEBUG.
|
|
36
|
+
if (/\b@travetto[/]test\b/.test(Env.DEBUG.value ?? '')) {
|
|
37
37
|
const file = Runtime.toolPath(`test-worker.${process.pid}.log`);
|
|
38
38
|
const stdout = createWriteStream(file, { flags: 'a' });
|
|
39
|
-
const
|
|
40
|
-
ConsoleManager.set({ log: (
|
|
39
|
+
const cons = new console.Console({ stdout, inspectOptions: { depth: 4, colors: false } });
|
|
40
|
+
ConsoleManager.set({ log: (event) => cons[event.level](process.pid, ...event.args) });
|
|
41
41
|
} else {
|
|
42
42
|
ConsoleManager.set({ log: () => { } });
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ export class TestChildWorker extends IpcChannel<TestRun> {
|
|
|
45
45
|
RunnerUtil.registerCleanup('worker');
|
|
46
46
|
|
|
47
47
|
// Listen for inbound requests
|
|
48
|
-
this.on('*',
|
|
48
|
+
this.on('*', event => this.onCommand(event));
|
|
49
49
|
|
|
50
50
|
// Let parent know the child is ready for handling commands
|
|
51
51
|
this.send(Events.READY);
|
package/src/worker/standard.ts
CHANGED
|
@@ -41,9 +41,9 @@ export async function buildStandardTestManager(consumer: TestConsumerShape, run:
|
|
|
41
41
|
await channel.send(Events.INIT); // Initialize
|
|
42
42
|
await channel.once(Events.INIT_COMPLETE); // Wait for complete
|
|
43
43
|
|
|
44
|
-
channel.on('*', async
|
|
44
|
+
channel.on('*', async event => {
|
|
45
45
|
try {
|
|
46
|
-
await consumer.onEvent(Util.deserializeFromJson(JSON.stringify(
|
|
46
|
+
await consumer.onEvent(Util.deserializeFromJson(JSON.stringify(event))); // Connect the consumer with the event stream from the child
|
|
47
47
|
} catch {
|
|
48
48
|
// Do nothing
|
|
49
49
|
}
|
|
@@ -55,7 +55,7 @@ export async function buildStandardTestManager(consumer: TestConsumerShape, run:
|
|
|
55
55
|
channel.send(Events.RUN, run);
|
|
56
56
|
|
|
57
57
|
// Wait for complete
|
|
58
|
-
const result = await complete.then(
|
|
58
|
+
const result = await complete.then(event => Util.deserializeFromJson<typeof event>(JSON.stringify(event)));
|
|
59
59
|
|
|
60
60
|
// Kill on complete
|
|
61
61
|
await channel.destroy();
|
package/support/bin/run.ts
CHANGED
|
@@ -6,19 +6,19 @@ import type { RunState } from '../../src/execute/types.ts';
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Run tests given the input state
|
|
9
|
-
* @param
|
|
9
|
+
* @param state
|
|
10
10
|
*/
|
|
11
|
-
export async function runTests(
|
|
11
|
+
export async function runTests(state: RunState): Promise<void> {
|
|
12
12
|
const { RunnerUtil } = await import('../../src/execute/util.ts');
|
|
13
13
|
const { Runner } = await import('../../src/execute/runner.ts');
|
|
14
14
|
|
|
15
15
|
RunnerUtil.registerCleanup('runner');
|
|
16
16
|
|
|
17
17
|
try {
|
|
18
|
-
const
|
|
19
|
-
process.exitCode =
|
|
20
|
-
} catch (
|
|
21
|
-
console.error('Test Worker Failed', { error
|
|
18
|
+
const result = await new Runner(state).run();
|
|
19
|
+
process.exitCode = result ? 0 : 1;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error('Test Worker Failed', { error });
|
|
22
22
|
process.exitCode = 1;
|
|
23
23
|
}
|
|
24
24
|
}
|
package/support/cli.test.ts
CHANGED
|
@@ -45,7 +45,7 @@ export class TestCommand implements CliCommandShape {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
isFirstFile(first: string): Promise<boolean> {
|
|
48
|
-
return fs.stat(path.resolve(first ?? '')).then(
|
|
48
|
+
return fs.stat(path.resolve(first ?? '')).then(stat => stat.isFile(), () => false);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async resolvedMode(first: string, rest: string[]): Promise<string> {
|
|
@@ -70,7 +70,7 @@ export class TestCommand implements CliCommandShape {
|
|
|
70
70
|
|
|
71
71
|
const isFirst = await this.isFirstFile(first);
|
|
72
72
|
const isSingle = this.mode === 'single' || (isFirst && globs.length === 0);
|
|
73
|
-
const options = Object.fromEntries((this.formatOptions ?? [])?.map(
|
|
73
|
+
const options = Object.fromEntries((this.formatOptions ?? [])?.map(option => [...option.split(':'), true]));
|
|
74
74
|
|
|
75
75
|
return runTests({
|
|
76
76
|
concurrency: this.concurrency,
|
|
@@ -22,8 +22,8 @@ export class TestDigestCommand {
|
|
|
22
22
|
for await (const imp of await RunnerUtil.getTestImports(globs)) {
|
|
23
23
|
try {
|
|
24
24
|
await Runtime.importFrom(imp);
|
|
25
|
-
} catch (
|
|
26
|
-
console.error('Failed to import', imp,
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Failed to import', imp, error);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -31,9 +31,9 @@ export class TestDigestCommand {
|
|
|
31
31
|
|
|
32
32
|
const suites = SuiteRegistryIndex.getClasses();
|
|
33
33
|
const all = suites
|
|
34
|
-
.map(
|
|
35
|
-
.filter(
|
|
36
|
-
.flatMap(
|
|
34
|
+
.map(cls => SuiteRegistryIndex.getConfig(cls))
|
|
35
|
+
.filter(config => !describeFunction(config.class).abstract)
|
|
36
|
+
.flatMap(config => Object.values(config.tests))
|
|
37
37
|
.toSorted((a, b) => {
|
|
38
38
|
const classComp = a.classId.localeCompare(b.classId);
|
|
39
39
|
return classComp !== 0 ? classComp : a.methodName.localeCompare(b.methodName);
|
|
@@ -31,7 +31,7 @@ export class TestDirectCommand {
|
|
|
31
31
|
|
|
32
32
|
main(importOrFile: string, clsId?: string, methodsNames: string[] = []): Promise<void> {
|
|
33
33
|
|
|
34
|
-
const options = Object.fromEntries((this.formatOptions ?? [])?.map(
|
|
34
|
+
const options = Object.fromEntries((this.formatOptions ?? [])?.map(option => [...option.split(':'), true]));
|
|
35
35
|
|
|
36
36
|
return runTests({
|
|
37
37
|
consumer: this.format,
|
|
@@ -29,8 +29,8 @@ export class TestWatcherCommand {
|
|
|
29
29
|
try {
|
|
30
30
|
const { TestWatcher } = await import('../src/execute/watcher.ts');
|
|
31
31
|
await TestWatcher.watch(this.format, this.mode === 'all');
|
|
32
|
-
} catch (
|
|
33
|
-
console.error(
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(error);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -96,10 +96,10 @@ export class AssertTransformer {
|
|
|
96
96
|
static lookupOpToken(key: number): string | undefined {
|
|
97
97
|
if (OP_TOKEN_TO_NAME.size === 0) {
|
|
98
98
|
Object.keys(ts.SyntaxKind)
|
|
99
|
-
.filter(
|
|
100
|
-
.filter((
|
|
101
|
-
.forEach(
|
|
102
|
-
OP_TOKEN_TO_NAME.set(ts.SyntaxKind[
|
|
99
|
+
.filter(kind => !/^\d+$/.test(kind))
|
|
100
|
+
.filter((kind): kind is keyof typeof OPTOKEN_ASSERT => !/^(Last|First)/.test(kind))
|
|
101
|
+
.forEach(kind =>
|
|
102
|
+
OP_TOKEN_TO_NAME.set(ts.SyntaxKind[kind], kind));
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const name = OP_TOKEN_TO_NAME.get(key)!;
|
|
@@ -123,10 +123,10 @@ export class AssertTransformer {
|
|
|
123
123
|
|
|
124
124
|
// If looking at an identifier, see if it's in a diff file or if its const
|
|
125
125
|
if (!found && ts.isIdentifier(node)) {
|
|
126
|
-
found = !!state.getDeclarations(node).find(
|
|
126
|
+
found = !!state.getDeclarations(node).find(declaration =>
|
|
127
127
|
// In a separate file or is const
|
|
128
|
-
|
|
129
|
-
DeclarationUtil.isConstantDeclaration(
|
|
128
|
+
declaration.getSourceFile().fileName !== state.source.fileName ||
|
|
129
|
+
DeclarationUtil.isConstantDeclaration(declaration));
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
return found;
|
|
@@ -137,7 +137,7 @@ export class AssertTransformer {
|
|
|
137
137
|
*/
|
|
138
138
|
static initState(state: TransformerState & AssertState): void {
|
|
139
139
|
if (!state[AssertSymbol]) {
|
|
140
|
-
const asrt = state.importFile('@travetto/test/src/assert/check.ts').
|
|
140
|
+
const asrt = state.importFile('@travetto/test/src/assert/check.ts').identifier;
|
|
141
141
|
state[AssertSymbol] = {
|
|
142
142
|
assert: asrt,
|
|
143
143
|
assertCheck: CoreUtil.createAccess(state.factory, asrt, ASSERT_UTIL, 'check'),
|
|
@@ -156,7 +156,7 @@ export class AssertTransformer {
|
|
|
156
156
|
const first = CoreUtil.firstArgument(node);
|
|
157
157
|
const firstText = first?.getText() ?? node.getText();
|
|
158
158
|
|
|
159
|
-
cmd.args = cmd.args.filter(
|
|
159
|
+
cmd.args = cmd.args.filter(arg => arg !== undefined && arg !== null);
|
|
160
160
|
const check = state.factory.createCallExpression(state[AssertSymbol]!.assertCheck, undefined, state.factory.createNodeArray([
|
|
161
161
|
state.fromLiteral({
|
|
162
162
|
module: state.getModuleIdentifier(),
|
|
@@ -236,7 +236,7 @@ export class AssertTransformer {
|
|
|
236
236
|
const matched = METHODS[key.text!];
|
|
237
237
|
if (matched) {
|
|
238
238
|
const resolved = state.resolveType(root);
|
|
239
|
-
if (resolved.key === 'literal' && matched.find(
|
|
239
|
+
if (resolved.key === 'literal' && matched.find(type => resolved.ctor === type)) { // Ensure method is against real type
|
|
240
240
|
switch (key.text) {
|
|
241
241
|
case 'includes': return { fn: key.text, args: [comp.expression.expression, comp.arguments[0], ...args.slice(1)] };
|
|
242
242
|
case 'test': return { fn: key.text, args: [comp.arguments[0], comp.expression.expression, ...args.slice(1)] };
|
|
@@ -298,9 +298,9 @@ export class AssertTransformer {
|
|
|
298
298
|
}
|
|
299
299
|
// If calling `assert.*`
|
|
300
300
|
} else if (ts.isPropertyAccessExpression(exp) && ts.isIdentifier(exp.expression)) { // Assert method call
|
|
301
|
-
const
|
|
301
|
+
const identifier = exp.expression;
|
|
302
302
|
const fn = exp.name.escapedText.toString();
|
|
303
|
-
if (
|
|
303
|
+
if (identifier.escapedText === ASSERT_CMD) {
|
|
304
304
|
// Look for reject/throw
|
|
305
305
|
if (fn === 'fail') {
|
|
306
306
|
node = this.doAssert(state, node, { fn: 'fail', args: node.arguments.slice() });
|