@travetto/test 3.0.0-rc.4 → 3.0.0-rc.6
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 +19 -18
- package/{index.ts → __index__.ts} +2 -0
- package/package.json +19 -12
- package/src/assert/capture.ts +1 -0
- package/src/assert/check.ts +13 -7
- package/src/assert/util.ts +12 -12
- package/src/consumer/enhancer.ts +16 -24
- package/src/consumer/registry.ts +5 -2
- package/src/consumer/types/{index.ts → all.ts} +1 -1
- package/src/consumer/types/cumulative.ts +11 -6
- package/src/consumer/types/runnable.ts +8 -12
- package/src/consumer/types/tap-streamed.ts +92 -0
- package/src/consumer/types/tap.ts +25 -21
- package/src/consumer/types.ts +2 -2
- package/src/consumer/util.ts +1 -1
- package/src/decorator/suite.ts +3 -2
- package/src/decorator/test.ts +11 -2
- package/src/execute/console.ts +4 -3
- package/src/execute/executor.ts +26 -40
- package/src/execute/phase.ts +2 -2
- package/src/execute/runner.ts +15 -30
- package/src/execute/{types.d.ts → types.ts} +4 -4
- package/src/execute/util.ts +8 -8
- package/src/execute/watcher.ts +43 -20
- package/src/fixture.ts +10 -0
- package/src/model/common.ts +4 -0
- package/src/registry/suite.ts +10 -7
- package/src/worker/child.ts +6 -51
- package/src/worker/standard.ts +39 -18
- package/src/worker/types.ts +0 -1
- package/{bin/lib → support/bin}/run.ts +8 -9
- package/support/cli.test.ts +76 -0
- package/support/main.test-child.ts +32 -0
- package/support/main.test-direct.ts +15 -0
- package/support/main.test-watch.ts +8 -0
- package/support/transformer.annotate.ts +4 -5
- package/support/transformer.assert.ts +22 -20
- package/bin/cli-test.ts +0 -121
- package/bin/test-child.ts +0 -38
- package/bin/test-direct.ts +0 -23
- package/bin/test-watch.ts +0 -25
- package/src/consumer/types/tap-summary.ts +0 -78
- package/src/worker/isolated.ts +0 -19
- package/support/phase.reset.ts +0 -12
package/src/registry/suite.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Class, ConcreteClass } from '@travetto/base';
|
|
2
|
+
import { RootIndex } from '@travetto/manifest';
|
|
2
3
|
import { MetadataRegistry } from '@travetto/registry';
|
|
3
4
|
|
|
4
5
|
import { SuiteConfig } from '../model/suite';
|
|
@@ -13,14 +14,15 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
13
14
|
* Find all valid tests (ignoring abstract)
|
|
14
15
|
*/
|
|
15
16
|
getValidClasses(): Class[] {
|
|
16
|
-
return this.getClasses().filter(c => !c
|
|
17
|
+
return this.getClasses().filter(c => !RootIndex.getFunctionMetadata(c)?.abstract);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
createPending(cls: Class): Partial<SuiteConfig> {
|
|
20
21
|
return {
|
|
21
22
|
class: cls,
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
module: RootIndex.manifest.mainModule,
|
|
24
|
+
classId: cls.Ⲑid,
|
|
25
|
+
file: RootIndex.getFunctionMetadata(cls)!.source,
|
|
24
26
|
tests: [],
|
|
25
27
|
beforeAll: [],
|
|
26
28
|
beforeEach: [],
|
|
@@ -32,7 +34,8 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
32
34
|
override createPendingField(cls: Class, fn: Function): Partial<TestConfig> {
|
|
33
35
|
return {
|
|
34
36
|
class: cls,
|
|
35
|
-
|
|
37
|
+
module: RootIndex.manifest.mainModule,
|
|
38
|
+
file: RootIndex.getFunctionMetadata(cls)!.source,
|
|
36
39
|
methodName: fn.name
|
|
37
40
|
};
|
|
38
41
|
}
|
|
@@ -52,7 +55,7 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
52
55
|
onInstallFinalize<T>(cls: Class<T>): SuiteConfig {
|
|
53
56
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
54
57
|
const config = this.getOrCreatePending(cls) as SuiteConfig;
|
|
55
|
-
const tests = [...this.pendingFields.get(cls
|
|
58
|
+
const tests = [...this.pendingFields.get(cls.Ⲑid)!.values()];
|
|
56
59
|
|
|
57
60
|
const parent = this.getParentClass(cls);
|
|
58
61
|
|
|
@@ -88,7 +91,7 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
88
91
|
getRunParams(file: string, clsName?: string, method?: string): { suites: SuiteConfig[] } | { suite: SuiteConfig, test?: TestConfig } {
|
|
89
92
|
if (clsName && /^\d+$/.test(clsName)) { // If we only have a line number
|
|
90
93
|
const line = parseInt(clsName, 10);
|
|
91
|
-
const suites = this.getValidClasses().filter(
|
|
94
|
+
const suites = this.getValidClasses().filter(cls => RootIndex.getFunctionMetadata(cls)!.source === file).map(x => this.get(x)).filter(x => !x.skip);
|
|
92
95
|
const suite = suites.find(x => x.lines && (line >= x.lines.start && line <= x.lines.end));
|
|
93
96
|
|
|
94
97
|
if (suite) {
|
|
@@ -110,7 +113,7 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
110
113
|
} else {
|
|
111
114
|
const suites = this.getValidClasses()
|
|
112
115
|
.map(x => this.get(x))
|
|
113
|
-
.filter(x => !x.class
|
|
116
|
+
.filter(x => !RootIndex.getFunctionMetadata(x.class)?.abstract); // Do not run abstract suites
|
|
114
117
|
return { suites };
|
|
115
118
|
}
|
|
116
119
|
}
|
package/src/worker/child.ts
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SourceIndex } from '@travetto/boot/src/internal/source';
|
|
3
|
-
import { ErrorUtil } from '@travetto/base/src/internal/error';
|
|
4
|
-
import { PhaseManager, ShutdownManager } from '@travetto/base';
|
|
1
|
+
import { ErrorUtil, TimeUtil } from '@travetto/base';
|
|
5
2
|
import { ChildCommChannel } from '@travetto/worker';
|
|
6
3
|
|
|
4
|
+
import { RunnerUtil } from '../execute/util';
|
|
5
|
+
import { Runner } from '../execute/runner';
|
|
7
6
|
import { Events, RunEvent } from './types';
|
|
8
7
|
|
|
9
|
-
const FIXED_MODULES = new Set([
|
|
10
|
-
// 'cache', 'openapi',
|
|
11
|
-
// 'registry'
|
|
12
|
-
'boot', 'base', 'cli',
|
|
13
|
-
'compiler', 'transformer',
|
|
14
|
-
'yaml', 'worker', 'command',
|
|
15
|
-
'log', 'jwt', 'image',
|
|
16
|
-
'test',
|
|
17
|
-
].map(x => `@travetto/${x}`));
|
|
18
|
-
|
|
19
8
|
/**
|
|
20
9
|
* Child Worker for the Test Runner. Receives events as commands
|
|
21
10
|
* to run specific tests
|
|
22
11
|
*/
|
|
23
12
|
export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
24
13
|
|
|
25
|
-
#runs = 0;
|
|
26
|
-
|
|
27
14
|
async #exec(op: () => Promise<unknown>, type: string): Promise<void> {
|
|
28
15
|
try {
|
|
29
16
|
await op();
|
|
@@ -41,7 +28,6 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
41
28
|
* Start the worker
|
|
42
29
|
*/
|
|
43
30
|
async activate(): Promise<void> {
|
|
44
|
-
const { RunnerUtil } = await import('../execute/util');
|
|
45
31
|
RunnerUtil.registerCleanup('worker');
|
|
46
32
|
|
|
47
33
|
// Listen for inbound requests
|
|
@@ -49,6 +35,8 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
49
35
|
|
|
50
36
|
// Let parent know the child is ready for handling commands
|
|
51
37
|
this.send(Events.READY);
|
|
38
|
+
|
|
39
|
+
await TimeUtil.wait('10m');
|
|
52
40
|
}
|
|
53
41
|
|
|
54
42
|
/**
|
|
@@ -60,6 +48,7 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
60
48
|
if (event.type === Events.INIT) { // On request to init, start initialization
|
|
61
49
|
await this.#exec(() => this.onInitCommand(), Events.INIT_COMPLETE);
|
|
62
50
|
} else if (event.type === Events.RUN) { // On request to run, start running
|
|
51
|
+
console.log!(process.stdout.isTTY && process.stdout.getColorDepth());
|
|
63
52
|
await this.#exec(() => this.onRunCommand(event), Events.RUN_COMPLETE);
|
|
64
53
|
}
|
|
65
54
|
|
|
@@ -71,46 +60,12 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
71
60
|
*/
|
|
72
61
|
async onInitCommand(): Promise<void> { }
|
|
73
62
|
|
|
74
|
-
/**
|
|
75
|
-
* Reset the state to prepare for the next run
|
|
76
|
-
*/
|
|
77
|
-
async resetForRun(): Promise<void> {
|
|
78
|
-
// Clear require cache of all data loaded minus base framework pieces
|
|
79
|
-
console.debug('Resetting', { fileCount: Object.keys(require.cache).length });
|
|
80
|
-
|
|
81
|
-
// Reload registries, test and root
|
|
82
|
-
await PhaseManager.run('reset');
|
|
83
|
-
await ShutdownManager.executeAsync(-1);
|
|
84
|
-
|
|
85
|
-
for (const { file } of SourceIndex.find({
|
|
86
|
-
paths: SourceIndex.getPaths().filter(x => !FIXED_MODULES.has(x)),
|
|
87
|
-
includeIndex: true
|
|
88
|
-
})) {
|
|
89
|
-
if (!/support\/(transformer|phase)[.]/.test(file)) {
|
|
90
|
-
const worked = ModuleManager.unload(file);
|
|
91
|
-
if (worked) {
|
|
92
|
-
console.debug('Unloading', { pid: process.pid, file });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
63
|
/**
|
|
99
64
|
* Run a specific test/suite
|
|
100
65
|
*/
|
|
101
66
|
async onRunCommand(event: RunEvent): Promise<void> {
|
|
102
|
-
this.#runs += 1;
|
|
103
67
|
console.debug('Run');
|
|
104
68
|
|
|
105
|
-
if (this.#runs > 1) {
|
|
106
|
-
await this.resetForRun();
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Run all remaining initializations as needed for tests
|
|
110
|
-
await PhaseManager.run('init', '*', ['@trv:registry/init']);
|
|
111
|
-
|
|
112
|
-
const { Runner } = await import('../execute/runner');
|
|
113
|
-
|
|
114
69
|
console.debug('Running', { file: event.file });
|
|
115
70
|
|
|
116
71
|
await new Runner({
|
package/src/worker/standard.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { RootIndex } from '@travetto/manifest';
|
|
2
|
+
import { ExecUtil, ErrorUtil } from '@travetto/base';
|
|
3
|
+
import { ParentCommChannel, Worker } from '@travetto/worker';
|
|
4
4
|
|
|
5
5
|
import { Events, RunEvent } from './types';
|
|
6
6
|
import { TestConsumer } from '../consumer/types';
|
|
7
7
|
import { TestEvent } from '../model/event';
|
|
8
8
|
|
|
9
|
+
let i = 0;
|
|
10
|
+
|
|
11
|
+
function buildEvent(ev: string): RunEvent {
|
|
12
|
+
if (ev.includes('#')) {
|
|
13
|
+
const [file, cls, method] = ev.split('#');
|
|
14
|
+
return { file, class: cls, method };
|
|
15
|
+
} else {
|
|
16
|
+
return { file: ev };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
/**
|
|
10
21
|
* Produce a handler for the child worker
|
|
11
22
|
*/
|
|
@@ -13,17 +24,28 @@ export function buildStandardTestManager(consumer: TestConsumer): () => Worker<s
|
|
|
13
24
|
/**
|
|
14
25
|
* Spawn a child
|
|
15
26
|
*/
|
|
16
|
-
return () =>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
return () => ({
|
|
28
|
+
id: i += 1,
|
|
29
|
+
active: true,
|
|
30
|
+
async destroy(): Promise<void> { },
|
|
31
|
+
async execute(file: string): Promise<void> {
|
|
32
|
+
const event = buildEvent(file);
|
|
33
|
+
|
|
34
|
+
const { module } = RootIndex.getEntry(event.file!)!;
|
|
35
|
+
const cwd = RootIndex.getModule(module)!.source;
|
|
36
|
+
|
|
37
|
+
const channel = new ParentCommChannel<TestEvent & { error?: Error }>(
|
|
38
|
+
ExecUtil.fork(RootIndex.resolveFileImport('@travetto/test/support/main.test-child.ts'), [], {
|
|
39
|
+
cwd,
|
|
40
|
+
env: { TRV_MANIFEST: module },
|
|
41
|
+
stdio: [0, 'ignore', 2, 'ipc']
|
|
42
|
+
})
|
|
43
|
+
);
|
|
44
|
+
|
|
24
45
|
await channel.once(Events.READY); // Wait for the child to be ready
|
|
25
46
|
await channel.send(Events.INIT); // Initialize
|
|
26
47
|
await channel.once(Events.INIT_COMPLETE); // Wait for complete
|
|
48
|
+
|
|
27
49
|
channel.on('*', async ev => {
|
|
28
50
|
try {
|
|
29
51
|
await consumer.onEvent(ev); // Connect the consumer with the event stream from the child
|
|
@@ -31,23 +53,22 @@ export function buildStandardTestManager(consumer: TestConsumer): () => Worker<s
|
|
|
31
53
|
// Do nothing
|
|
32
54
|
}
|
|
33
55
|
});
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Send child command to run tests
|
|
37
|
-
*/
|
|
38
|
-
async (channel: ParentCommChannel<TestEvent & { error?: Error }>, event: string | RunEvent): Promise<void> => {
|
|
56
|
+
|
|
39
57
|
// Listen for child to complete
|
|
40
58
|
const complete = channel.once(Events.RUN_COMPLETE);
|
|
41
59
|
// Start test
|
|
42
|
-
event = typeof event === 'string' ? { file: event } : event;
|
|
43
60
|
channel.send(Events.RUN, event);
|
|
44
61
|
|
|
45
62
|
// Wait for complete
|
|
46
63
|
const { error } = await complete;
|
|
47
64
|
|
|
65
|
+
// Kill on complete
|
|
66
|
+
await channel.destroy();
|
|
67
|
+
|
|
48
68
|
// If we received an error, throw it
|
|
49
69
|
if (error) {
|
|
50
70
|
throw ErrorUtil.deserializeError(error);
|
|
51
71
|
}
|
|
52
|
-
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
53
74
|
}
|
package/src/worker/types.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { ShutdownManager, TimeUtil } from '@travetto/base';
|
|
2
|
+
|
|
3
|
+
import { RunnerUtil } from '../../src/execute/util';
|
|
4
|
+
import { Runner } from '../../src/execute/runner';
|
|
5
|
+
|
|
1
6
|
import type { RunState } from '../../src/execute/types';
|
|
2
7
|
|
|
3
8
|
declare global {
|
|
@@ -14,23 +19,17 @@ declare global {
|
|
|
14
19
|
* @param opts
|
|
15
20
|
*/
|
|
16
21
|
export async function runTests(opts: RunState): Promise<void> {
|
|
17
|
-
const { PhaseManager, Util } = await import('@travetto/base');
|
|
18
|
-
await PhaseManager.run('init', '*', ['@trv:registry/init']); // Delay registry
|
|
19
|
-
|
|
20
|
-
const { RunnerUtil } = await import('../../src/execute/util');
|
|
21
|
-
const { Runner } = await import('../../src/execute/runner');
|
|
22
|
-
|
|
23
22
|
RunnerUtil.registerCleanup('runner');
|
|
24
23
|
|
|
25
24
|
if (process.env.TRV_TEST_DELAY) {
|
|
26
|
-
await
|
|
25
|
+
await TimeUtil.wait(process.env.TRV_TEST_DELAY);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
try {
|
|
30
29
|
const res = await new Runner(opts).run();
|
|
31
|
-
|
|
30
|
+
return ShutdownManager.exit(res ? 0 : 1);
|
|
32
31
|
} catch (err) {
|
|
33
32
|
console.error('Test Worker Failed', { error: err });
|
|
34
|
-
|
|
33
|
+
return ShutdownManager.exit(1);
|
|
35
34
|
}
|
|
36
35
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
|
|
4
|
+
import { path, RootIndex } from '@travetto/manifest';
|
|
5
|
+
import { GlobalEnvConfig } from '@travetto/base';
|
|
6
|
+
import { CliCommand, OptionConfig } from '@travetto/cli';
|
|
7
|
+
import { WorkPool } from '@travetto/worker';
|
|
8
|
+
|
|
9
|
+
import type { RunState } from '../src/execute/types';
|
|
10
|
+
|
|
11
|
+
const modes = ['single', 'standard'] as const;
|
|
12
|
+
|
|
13
|
+
type Options = {
|
|
14
|
+
format: OptionConfig<string>;
|
|
15
|
+
concurrency: OptionConfig<number>;
|
|
16
|
+
mode: OptionConfig<(typeof modes)[number]>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Launch test framework and execute tests
|
|
21
|
+
*/
|
|
22
|
+
export class TestCommand extends CliCommand<Options> {
|
|
23
|
+
name = 'test';
|
|
24
|
+
_types: string[];
|
|
25
|
+
|
|
26
|
+
getTypes(): string[] {
|
|
27
|
+
if (!this._types) {
|
|
28
|
+
this._types = RootIndex
|
|
29
|
+
.findSrc({ filter: /consumer\/types\/.*/, profiles: ['test'] })
|
|
30
|
+
.map(x => readFileSync(`${x.output}`, 'utf8').match(/Consumable.?[(]'([^']+)/)?.[1])
|
|
31
|
+
.filter((x?: string): x is string => !!x);
|
|
32
|
+
}
|
|
33
|
+
return this._types;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getOptions(): Options {
|
|
37
|
+
return {
|
|
38
|
+
format: this.choiceOption({ desc: 'Output format for test results', def: 'tap', choices: this.getTypes() }),
|
|
39
|
+
concurrency: this.intOption({ desc: 'Number of tests to run concurrently', lower: 1, upper: 32, def: WorkPool.DEFAULT_SIZE }),
|
|
40
|
+
mode: this.choiceOption({ desc: 'Test run mode', def: 'standard', choices: [...modes] })
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
envInit(): GlobalEnvConfig {
|
|
45
|
+
return { test: true };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getArgs(): string {
|
|
49
|
+
return '[regexes...]';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async action(regexes: string[]): Promise<void> {
|
|
53
|
+
const { runTests } = await import('./bin/run.js');
|
|
54
|
+
|
|
55
|
+
const [first] = regexes;
|
|
56
|
+
|
|
57
|
+
const isFile = await fs.stat(path.resolve(first ?? '')).then(x => x.isFile(), () => false);
|
|
58
|
+
|
|
59
|
+
const state: RunState = {
|
|
60
|
+
args: !first ? ['test/.*'] : regexes,
|
|
61
|
+
mode: isFile && regexes.length === 1 ? 'single' : this.cmd.mode,
|
|
62
|
+
concurrency: this.cmd.concurrency,
|
|
63
|
+
format: this.cmd.format
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (state.mode === 'single') {
|
|
67
|
+
if (!isFile) {
|
|
68
|
+
return this.showHelp('You must specify a proper test file to run in single mode');
|
|
69
|
+
} else if (!/test\//.test(first)) {
|
|
70
|
+
return this.showHelp('Only files in the test/ folder are permitted to be run');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await runTests(state);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
|
|
3
|
+
import { ConsoleManager, defineGlobalEnv } from '@travetto/base';
|
|
4
|
+
import { path } from '@travetto/manifest';
|
|
5
|
+
|
|
6
|
+
import { TestChildWorker } from '../src/worker/child';
|
|
7
|
+
|
|
8
|
+
export async function customLogs(): Promise<void> {
|
|
9
|
+
if (/\b@travetto[/]test\b/.test(process.env.DEBUG ?? '')) {
|
|
10
|
+
const handle = await fs.open(path.resolve(`.trv-test-worker.${process.pid}.log`), 'a');
|
|
11
|
+
const stdout = handle.createWriteStream();
|
|
12
|
+
|
|
13
|
+
const c = new console.Console({
|
|
14
|
+
stdout,
|
|
15
|
+
inspectOptions: { depth: 4 },
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
ConsoleManager.set({
|
|
19
|
+
onLog: (ev) => c[ev.level](process.pid, ...ev.args)
|
|
20
|
+
});
|
|
21
|
+
} else {
|
|
22
|
+
ConsoleManager.set({ onLog: () => { } });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function main(): Promise<void> {
|
|
27
|
+
defineGlobalEnv({ test: true });
|
|
28
|
+
ConsoleManager.setDebugFromEnv();
|
|
29
|
+
|
|
30
|
+
await customLogs();
|
|
31
|
+
return new TestChildWorker().activate();
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ConsoleManager, defineGlobalEnv } from '@travetto/base';
|
|
2
|
+
import { runTests } from './bin/run';
|
|
3
|
+
|
|
4
|
+
// Direct entry point
|
|
5
|
+
export function main(...args: string[]): Promise<void> {
|
|
6
|
+
defineGlobalEnv({ test: true });
|
|
7
|
+
ConsoleManager.setDebugFromEnv();
|
|
8
|
+
|
|
9
|
+
return runTests({
|
|
10
|
+
args,
|
|
11
|
+
format: process.env.TRV_TEST_FORMAT ?? 'tap',
|
|
12
|
+
mode: 'single',
|
|
13
|
+
concurrency: 1
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ConsoleManager, defineGlobalEnv } from '@travetto/base';
|
|
2
|
+
import { TestWatcher } from '../src/execute/watcher';
|
|
3
|
+
|
|
4
|
+
export async function main(format: string = 'tap', runAllOnStart: string = 'true'): Promise<void> {
|
|
5
|
+
defineGlobalEnv({ test: true, dynamic: true });
|
|
6
|
+
ConsoleManager.setDebugFromEnv();
|
|
7
|
+
await TestWatcher.watch(format, runAllOnStart !== 'false');
|
|
8
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
TransformerState, DecoratorMeta, OnMethod, OnClass, CoreUtil, DecoratorUtil
|
|
4
|
+
TransformerState, DecoratorMeta, OnMethod, OnClass, CoreUtil, DecoratorUtil
|
|
5
5
|
} from '@travetto/transformer';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
*/
|
|
10
10
|
export class AnnotationTransformer {
|
|
11
11
|
|
|
12
|
-
static [TransformerId] = '@trv:test';
|
|
13
|
-
|
|
14
12
|
/**
|
|
15
13
|
* Build source annotation, indicating line ranges
|
|
16
14
|
* @param state
|
|
@@ -29,8 +27,9 @@ export class AnnotationTransformer {
|
|
|
29
27
|
[
|
|
30
28
|
...(expression.arguments ?? []),
|
|
31
29
|
state.fromLiteral({
|
|
30
|
+
ident: `@${DecoratorUtil.getDecoratorIdent(dec).text}()`,
|
|
32
31
|
lines: {
|
|
33
|
-
...CoreUtil.getRangeOf(state.source,
|
|
32
|
+
...CoreUtil.getRangeOf(state.source, node),
|
|
34
33
|
codeStart: CoreUtil.getRangeOf(state.source, n?.body?.statements[0])?.start
|
|
35
34
|
}
|
|
36
35
|
})
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { TransformerState, OnCall, DeclarationUtil, CoreUtil, TransformerId } from '@travetto/transformer';
|
|
3
|
+
import { TransformerState, OnCall, DeclarationUtil, CoreUtil, OnMethod, AfterMethod } from '@travetto/transformer';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Which types are candidates for deep literal checking
|
|
@@ -49,8 +48,8 @@ const METHODS: Record<string, Function[]> = {
|
|
|
49
48
|
|
|
50
49
|
const OP_TOKEN_TO_NAME = new Map<number, keyof typeof OPTOKEN_ASSERT>();
|
|
51
50
|
|
|
52
|
-
const AssertⲐ = Symbol.for('@
|
|
53
|
-
const IsTestⲐ = Symbol.for('@
|
|
51
|
+
const AssertⲐ = Symbol.for('@travetto/test:assert');
|
|
52
|
+
const IsTestⲐ = Symbol.for('@travetto/test:valid');
|
|
54
53
|
|
|
55
54
|
/**
|
|
56
55
|
* Assert transformation state
|
|
@@ -59,9 +58,9 @@ interface AssertState {
|
|
|
59
58
|
[AssertⲐ]?: {
|
|
60
59
|
assert: ts.Identifier;
|
|
61
60
|
hasAssertCall?: boolean;
|
|
62
|
-
assertCheck: ts.
|
|
63
|
-
checkThrow: ts.
|
|
64
|
-
checkThrowAsync: ts.
|
|
61
|
+
assertCheck: ts.Expression;
|
|
62
|
+
checkThrow: ts.Expression;
|
|
63
|
+
checkThrowAsync: ts.Expression;
|
|
65
64
|
};
|
|
66
65
|
[IsTestⲐ]?: boolean;
|
|
67
66
|
}
|
|
@@ -91,8 +90,6 @@ interface Command {
|
|
|
91
90
|
*/
|
|
92
91
|
export class AssertTransformer {
|
|
93
92
|
|
|
94
|
-
static [TransformerId] = '@trv:test';
|
|
95
|
-
|
|
96
93
|
/**
|
|
97
94
|
* Resolves optoken to syntax kind. Relies on `ts`
|
|
98
95
|
*/
|
|
@@ -162,7 +159,7 @@ export class AssertTransformer {
|
|
|
162
159
|
cmd.args = cmd.args.filter(x => x !== undefined && x !== null);
|
|
163
160
|
const check = state.factory.createCallExpression(state[AssertⲐ]!.assertCheck, undefined, state.factory.createNodeArray([
|
|
164
161
|
state.fromLiteral({
|
|
165
|
-
file: state.
|
|
162
|
+
file: state.getFilenameIdentifier(),
|
|
166
163
|
line: state.fromLiteral(ts.getLineAndCharacterOfPosition(state.source, node.getStart()).line + 1),
|
|
167
164
|
text: state.fromLiteral(firstText),
|
|
168
165
|
operator: state.fromLiteral(cmd.fn)
|
|
@@ -187,7 +184,7 @@ export class AssertTransformer {
|
|
|
187
184
|
undefined,
|
|
188
185
|
state.factory.createNodeArray([
|
|
189
186
|
state.fromLiteral({
|
|
190
|
-
file: state.
|
|
187
|
+
file: state.getFilenameIdentifier(),
|
|
191
188
|
line: state.fromLiteral(ts.getLineAndCharacterOfPosition(state.source, node.getStart()).line + 1),
|
|
192
189
|
text: state.fromLiteral(`${key} ${firstText}`),
|
|
193
190
|
operator: state.fromLiteral(`${key}`)
|
|
@@ -270,18 +267,23 @@ export class AssertTransformer {
|
|
|
270
267
|
}
|
|
271
268
|
}
|
|
272
269
|
|
|
270
|
+
@OnMethod('AssertCheck')
|
|
271
|
+
static onAssertCheck(state: TransformerState & AssertState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
272
|
+
state[IsTestⲐ] = true;
|
|
273
|
+
return node;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@AfterMethod('AssertCheck')
|
|
277
|
+
static afterAssertCheck(state: TransformerState & AssertState, node: ts.MethodDeclaration): ts.MethodDeclaration {
|
|
278
|
+
state[IsTestⲐ] = false;
|
|
279
|
+
return node;
|
|
280
|
+
}
|
|
281
|
+
|
|
273
282
|
/**
|
|
274
283
|
* Listen for all call expression
|
|
275
284
|
*/
|
|
276
285
|
@OnCall()
|
|
277
286
|
static onAssertCall(state: TransformerState & AssertState, node: ts.CallExpression): ts.CallExpression {
|
|
278
|
-
// If not in test mode, see if file is valid
|
|
279
|
-
if (state[IsTestⲐ] === undefined) {
|
|
280
|
-
const name = PathUtil.toUnix(state.source.fileName);
|
|
281
|
-
// Only apply to test files, allowing for inheriting from module test files as well
|
|
282
|
-
state[IsTestⲐ] = /\/test(-(support|isolated))?\//.test(name) && !name.includes('/test/src/');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
287
|
// Only check in test mode
|
|
286
288
|
if (!state[IsTestⲐ]) {
|
|
287
289
|
return node;
|
|
@@ -290,7 +292,7 @@ export class AssertTransformer {
|
|
|
290
292
|
const exp = node.expression;
|
|
291
293
|
|
|
292
294
|
// Determine if calling assert directly
|
|
293
|
-
if (ts.isIdentifier(exp) && exp.getText() === ASSERT_CMD) { // Straight assert
|
|
295
|
+
if (ts.isIdentifier(exp) && exp.getSourceFile() && exp.getText() === ASSERT_CMD) { // Straight assert
|
|
294
296
|
const cmd = this.getCommand(state, node.arguments);
|
|
295
297
|
if (cmd) {
|
|
296
298
|
node = this.doAssert(state, node, cmd);
|