@travetto/test 3.0.3 → 3.1.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/README.md +5 -5
- package/package.json +8 -8
- package/src/assert/check.ts +5 -5
- package/src/assert/util.ts +1 -1
- package/src/consumer/registry.ts +3 -4
- package/src/consumer/types/runnable.ts +3 -3
- package/src/consumer/types/tap-streamed.ts +7 -21
- package/src/consumer/types/tap.ts +2 -4
- package/src/consumer/types.ts +5 -1
- package/src/execute/executor.ts +1 -1
- package/src/execute/runner.ts +3 -2
- package/src/execute/util.ts +15 -1
- package/src/execute/watcher.ts +1 -17
- package/src/worker/child.ts +1 -7
- package/src/worker/standard.ts +3 -3
- package/support/bin/run.ts +3 -3
- package/support/bin/types.ts +2 -0
- package/support/cli.test.ts +34 -53
- package/support/cli.test_child.ts +19 -0
- package/support/cli.test_count.ts +34 -0
- package/support/cli.test_direct.ts +20 -0
- package/support/cli.test_watch.ts +34 -0
- package/support/transformer.assert.ts +4 -4
- package/support/bin/direct.ts +0 -15
package/README.md
CHANGED
|
@@ -220,13 +220,13 @@ To run the tests you can either call the [Command Line Interface](https://github
|
|
|
220
220
|
```bash
|
|
221
221
|
$ trv test --help
|
|
222
222
|
|
|
223
|
-
Usage:
|
|
223
|
+
Usage: test [options] [first:string] [regexes...:string]
|
|
224
224
|
|
|
225
225
|
Options:
|
|
226
|
-
-f, --format <
|
|
227
|
-
-c, --concurrency <
|
|
228
|
-
-m, --mode <
|
|
229
|
-
-h, --help
|
|
226
|
+
-f, --format <string> Output format for test results (default: "tap")
|
|
227
|
+
-c, --concurrency <number> Number of tests to run concurrently (default: 7)
|
|
228
|
+
-m, --mode <single|standard> Test run mode (default: "standard")
|
|
229
|
+
-h, --help display help for command
|
|
230
230
|
```
|
|
231
231
|
|
|
232
232
|
The regexes are the patterns of tests you want to run, and all tests must be found under the `test/` folder.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/test",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.1.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": "^3.0.
|
|
31
|
-
"@travetto/registry": "^3.0.
|
|
32
|
-
"@travetto/terminal": "^3.0.
|
|
33
|
-
"@travetto/worker": "^3.0.
|
|
34
|
-
"@travetto/yaml": "^3.0.
|
|
30
|
+
"@travetto/base": "^3.1.0-rc.0",
|
|
31
|
+
"@travetto/registry": "^3.1.0-rc.0",
|
|
32
|
+
"@travetto/terminal": "^3.1.0-rc.0",
|
|
33
|
+
"@travetto/worker": "^3.1.0-rc.0",
|
|
34
|
+
"@travetto/yaml": "^3.1.0-rc.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^3.0.
|
|
38
|
-
"@travetto/transformer": "^3.0.
|
|
37
|
+
"@travetto/cli": "^3.1.0-rc.1",
|
|
38
|
+
"@travetto/transformer": "^3.1.0-rc.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/transformer": {
|
package/src/assert/check.ts
CHANGED
|
@@ -70,7 +70,7 @@ export class AssertCheck {
|
|
|
70
70
|
} else if (fn === 'includes') {
|
|
71
71
|
assertion.operator = fn;
|
|
72
72
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
73
|
-
[assertion.
|
|
73
|
+
[assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
|
|
74
74
|
} else if (fn === 'instanceof') {
|
|
75
75
|
assertion.operator = fn;
|
|
76
76
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -98,6 +98,10 @@ export class AssertCheck {
|
|
|
98
98
|
|
|
99
99
|
// Actually run the assertion
|
|
100
100
|
switch (fn) {
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
102
|
+
case 'includes': assertFn((actual as unknown[]).includes(expected), message); break;
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
104
|
+
case 'test': assertFn((expected as RegExp).test(actual as string), message); break;
|
|
101
105
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
102
106
|
case 'instanceof': assertFn(actual instanceof (expected as Class), message); break;
|
|
103
107
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -120,10 +124,6 @@ export class AssertCheck {
|
|
|
120
124
|
}
|
|
121
125
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
122
126
|
assert[fn as 'ok'].apply(null, args as [boolean, string | undefined]);
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
124
|
-
} else if (expected && !!(expected as Record<string, Function>)[fn]) { // Dotted Method call (e.g. assert.rejects)
|
|
125
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
126
|
-
assertFn((expected as typeof assert)[fn as 'ok'](actual));
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
package/src/assert/util.ts
CHANGED
|
@@ -41,7 +41,7 @@ export class AssertUtil {
|
|
|
41
41
|
* Determine file location for a given error and the stack trace
|
|
42
42
|
*/
|
|
43
43
|
static getPositionOfError(err: Error, filename: string): { file: string, line: number } {
|
|
44
|
-
const cwd =
|
|
44
|
+
const cwd = RootIndex.mainModule.sourcePath;
|
|
45
45
|
const lines = path.toPosix(err.stack ?? new Error().stack!)
|
|
46
46
|
.split('\n')
|
|
47
47
|
// Exclude node_modules, target self
|
package/src/consumer/registry.ts
CHANGED
|
@@ -58,9 +58,8 @@ export const TestConsumerRegistry = new $TestConsumerRegistry();
|
|
|
58
58
|
* @param type The unique identifier for the consumer
|
|
59
59
|
* @param isDefault Is this the default consumer. Last one wins
|
|
60
60
|
*/
|
|
61
|
-
export function Consumable(type: string, isDefault = false):
|
|
62
|
-
|
|
63
|
-
return function (cls: Class<TestConsumer>) {
|
|
61
|
+
export function Consumable(type: string, isDefault = false): (cls: Class<TestConsumer>) => void {
|
|
62
|
+
return function (cls: Class<TestConsumer>): void {
|
|
64
63
|
TestConsumerRegistry.add(type, cls, isDefault);
|
|
65
|
-
}
|
|
64
|
+
};
|
|
66
65
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TestConsumer } from '../types';
|
|
1
|
+
import { TestConsumer, TestRunState } from '../types';
|
|
2
2
|
import { TestResultsSummarizer } from './summarizer';
|
|
3
3
|
import { TestConsumerRegistry } from '../registry';
|
|
4
4
|
import { TestEvent } from '../../model/event';
|
|
@@ -27,9 +27,9 @@ export class RunnableTestConsumer implements TestConsumer {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async onStart(
|
|
30
|
+
async onStart(state: TestRunState): Promise<void> {
|
|
31
31
|
for (const c of this.#consumers) {
|
|
32
|
-
await c.onStart?.(
|
|
32
|
+
await c.onStart?.(state);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { GlobalTerminal, TermStyleInput, Terminal } from '@travetto/terminal';
|
|
2
2
|
import { ManualAsyncIterator } from '@travetto/worker';
|
|
3
|
-
import { RootIndex } from '@travetto/manifest';
|
|
4
|
-
|
|
5
|
-
import { SuitesSummary, TestConsumer } from '../types';
|
|
6
|
-
import { Consumable } from '../registry';
|
|
7
3
|
|
|
8
4
|
import { TestEvent } from '../../model/event';
|
|
9
5
|
import { TestResult } from '../../model/test';
|
|
10
|
-
|
|
6
|
+
|
|
7
|
+
import { SuitesSummary, TestConsumer, TestRunState } from '../types';
|
|
8
|
+
import { Consumable } from '../registry';
|
|
9
|
+
|
|
11
10
|
import { TapEmitter } from './tap';
|
|
12
11
|
|
|
13
12
|
/**
|
|
@@ -49,25 +48,12 @@ export class TapStreamedEmitter implements TestConsumer {
|
|
|
49
48
|
this.#consumer = new TapEmitter(this.#terminal);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
async onStart(
|
|
51
|
+
async onStart(state: TestRunState): Promise<void> {
|
|
53
52
|
this.#consumer.onStart();
|
|
54
53
|
|
|
55
|
-
// Load all tests
|
|
56
|
-
for (const file of files) {
|
|
57
|
-
await import(RootIndex.getFromSource(file)!.import);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
await SuiteRegistry.init();
|
|
61
|
-
|
|
62
|
-
const suites = SuiteRegistry.getClasses();
|
|
63
|
-
const total = suites
|
|
64
|
-
.map(c => SuiteRegistry.get(c))
|
|
65
|
-
.filter(c => !RootIndex.getFunctionMetadata(c.class)?.abstract)
|
|
66
|
-
.reduce((acc, c) => acc + (c.tests?.length ?? 0), 0);
|
|
67
|
-
|
|
68
54
|
this.#progress = this.#terminal.streamToPosition(this.#results,
|
|
69
|
-
TapStreamedEmitter.makeProgressBar(this.#terminal,
|
|
70
|
-
{ position: 'bottom' }
|
|
55
|
+
TapStreamedEmitter.makeProgressBar(this.#terminal, state.testCount ?? 0),
|
|
56
|
+
{ position: 'bottom', minDelay: 100 }
|
|
71
57
|
);
|
|
72
58
|
}
|
|
73
59
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RootIndex } from '@travetto/manifest';
|
|
2
2
|
import { GlobalTerminal, Terminal } from '@travetto/terminal';
|
|
3
3
|
import { ErrorUtil, ObjectUtil, TimeUtil } from '@travetto/base';
|
|
4
4
|
import { YamlUtil } from '@travetto/yaml';
|
|
@@ -61,8 +61,6 @@ export class TapEmitter implements TestConsumer {
|
|
|
61
61
|
}
|
|
62
62
|
this.log(`# ${header}`);
|
|
63
63
|
|
|
64
|
-
const cwd = path.cwd();
|
|
65
|
-
|
|
66
64
|
// Handle each assertion
|
|
67
65
|
if (test.assertions.length) {
|
|
68
66
|
let subCount = 0;
|
|
@@ -72,7 +70,7 @@ export class TapEmitter implements TestConsumer {
|
|
|
72
70
|
this.#enhancer.assertNumber(++subCount),
|
|
73
71
|
'-',
|
|
74
72
|
this.#enhancer.assertDescription(text),
|
|
75
|
-
`${this.#enhancer.assertFile(asrt.file.replace(
|
|
73
|
+
`${this.#enhancer.assertFile(asrt.file.replace(RootIndex.mainModule.sourcePath, '.'))}:${this.#enhancer.assertLine(asrt.line)}`
|
|
76
74
|
].join(' ');
|
|
77
75
|
|
|
78
76
|
if (asrt.error) {
|
package/src/consumer/types.ts
CHANGED
|
@@ -19,6 +19,10 @@ export interface SuitesSummary extends Counts {
|
|
|
19
19
|
duration: number;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export type TestRunState = {
|
|
23
|
+
testCount?: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
22
26
|
/**
|
|
23
27
|
* A test result handler
|
|
24
28
|
*/
|
|
@@ -26,7 +30,7 @@ export interface TestConsumer {
|
|
|
26
30
|
/**
|
|
27
31
|
* Listen for start of the test run
|
|
28
32
|
*/
|
|
29
|
-
onStart?(
|
|
33
|
+
onStart?(testState: TestRunState): Promise<void> | void;
|
|
30
34
|
/**
|
|
31
35
|
* Handle individual tests events
|
|
32
36
|
*/
|
package/src/execute/executor.ts
CHANGED
|
@@ -71,7 +71,7 @@ export class TestExecutor {
|
|
|
71
71
|
const classId = RootIndex.getId(file, name);
|
|
72
72
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
73
73
|
const suite = { class: { name }, classId, duration: 0, lines: { start: 1, end: 1 }, file, } as SuiteConfig & SuiteResult;
|
|
74
|
-
err.message = err.message.
|
|
74
|
+
err.message = err.message.replaceAll(RootIndex.mainModule.sourcePath, '.');
|
|
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 });
|
package/src/execute/runner.ts
CHANGED
|
@@ -42,7 +42,8 @@ export class Runner {
|
|
|
42
42
|
max: this.#state.concurrency
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
await
|
|
45
|
+
const testCount = await RunnerUtil.getTestCount(this.#state.args);
|
|
46
|
+
await consumer.onStart({ testCount });
|
|
46
47
|
await pool.process(new IterableWorkSet(files));
|
|
47
48
|
return consumer.summarizeAsBoolean();
|
|
48
49
|
}
|
|
@@ -55,7 +56,7 @@ export class Runner {
|
|
|
55
56
|
|
|
56
57
|
const [file, ...args] = this.#state.args;
|
|
57
58
|
|
|
58
|
-
await consumer.onStart(
|
|
59
|
+
await consumer.onStart({});
|
|
59
60
|
await TestExecutor.execute(consumer, file, ...args);
|
|
60
61
|
return consumer.summarizeAsBoolean();
|
|
61
62
|
}
|
package/src/execute/util.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createReadStream } from 'fs';
|
|
2
2
|
import readline from 'readline';
|
|
3
3
|
|
|
4
|
-
import { ShutdownManager, TimeUtil } from '@travetto/base';
|
|
4
|
+
import { ExecUtil, ShutdownManager, TimeUtil } from '@travetto/base';
|
|
5
5
|
import { RootIndex } from '@travetto/manifest';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -47,4 +47,18 @@ export class RunnerUtil {
|
|
|
47
47
|
.filter(x => x.valid)
|
|
48
48
|
.map(x => x.file);
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get count of tests for a given set of patterns
|
|
53
|
+
* @param patterns
|
|
54
|
+
* @returns
|
|
55
|
+
*/
|
|
56
|
+
static async getTestCount(patterns: string[]): Promise<number> {
|
|
57
|
+
const proc = ExecUtil.spawn('npx', ['trv', 'test:count', ...patterns], { stdio: 'pipe', catchAsResult: true });
|
|
58
|
+
const countRes = await proc.result;
|
|
59
|
+
if (!countRes.valid) {
|
|
60
|
+
throw new Error(countRes.stderr);
|
|
61
|
+
}
|
|
62
|
+
return countRes.valid ? +countRes.stdout : 0;
|
|
63
|
+
}
|
|
50
64
|
}
|
package/src/execute/watcher.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RootRegistry, MethodSource } from '@travetto/registry';
|
|
2
2
|
import { WorkPool, IterableWorkSet, ManualAsyncIterator } from '@travetto/worker';
|
|
3
3
|
import { RootIndex } from '@travetto/manifest';
|
|
4
|
-
import {
|
|
4
|
+
import { ObjectUtil } from '@travetto/base';
|
|
5
5
|
|
|
6
6
|
import { SuiteRegistry } from '../registry/suite';
|
|
7
7
|
import { buildStandardTestManager } from '../worker/standard';
|
|
@@ -86,20 +86,4 @@ export class TestWatcher {
|
|
|
86
86
|
|
|
87
87
|
await pool.process(src);
|
|
88
88
|
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export async function main(format: string = 'tap', runAllOnStart: string = 'true'): Promise<void> {
|
|
92
|
-
defineGlobalEnv({ test: true, dynamic: true });
|
|
93
|
-
console.log('Starting');
|
|
94
|
-
ConsoleManager.setDebugFromEnv();
|
|
95
|
-
// Quit on parent disconnect
|
|
96
|
-
if (process.send) {
|
|
97
|
-
process.on('disconnect', () => process.exit(0));
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
await TestWatcher.watch(format, runAllOnStart !== 'false');
|
|
101
|
-
console.log('Done');
|
|
102
|
-
} catch (err) {
|
|
103
|
-
console.error(err);
|
|
104
|
-
}
|
|
105
89
|
}
|
package/src/worker/child.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
|
|
3
3
|
import { path } from '@travetto/manifest';
|
|
4
|
-
import { ConsoleManager,
|
|
4
|
+
import { ConsoleManager, ErrorUtil, TimeUtil } from '@travetto/base';
|
|
5
5
|
import { ChildCommChannel } from '@travetto/worker';
|
|
6
6
|
|
|
7
7
|
import { RunnerUtil } from '../execute/util';
|
|
@@ -88,10 +88,4 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
88
88
|
concurrency: 1
|
|
89
89
|
}).run();
|
|
90
90
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export async function main(): Promise<void> {
|
|
94
|
-
defineGlobalEnv({ test: true, set: { FORCE_COLOR: 0 } });
|
|
95
|
-
ConsoleManager.setDebugFromEnv();
|
|
96
|
-
await new TestChildWorker().activate();
|
|
97
91
|
}
|
package/src/worker/standard.ts
CHANGED
|
@@ -37,11 +37,11 @@ export function buildStandardTestManager(consumer: TestConsumer): () => Worker<s
|
|
|
37
37
|
const channel = new ParentCommChannel<TestEvent & { error?: Error }>(
|
|
38
38
|
ExecUtil.fork(
|
|
39
39
|
RootIndex.resolveFileImport('@travetto/cli/support/entry.cli'),
|
|
40
|
-
['
|
|
40
|
+
['test:child'],
|
|
41
41
|
{
|
|
42
42
|
cwd,
|
|
43
|
-
env: { TRV_MANIFEST: RootIndex.getModule(module)!.outputPath },
|
|
44
|
-
stdio: [
|
|
43
|
+
env: { TRV_MANIFEST: RootIndex.getModule(module)!.outputPath, TRV_QUIET: '1' },
|
|
44
|
+
stdio: ['ignore', 'ignore', 2, 'ipc']
|
|
45
45
|
}
|
|
46
46
|
)
|
|
47
47
|
);
|
package/support/bin/run.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { ShutdownManager, TimeUtil } from '@travetto/base';
|
|
2
2
|
|
|
3
|
-
import { RunnerUtil } from '../../src/execute/util';
|
|
4
|
-
import { Runner } from '../../src/execute/runner';
|
|
5
|
-
|
|
6
3
|
import type { RunState } from '../../src/execute/types';
|
|
7
4
|
|
|
8
5
|
declare global {
|
|
@@ -19,6 +16,9 @@ declare global {
|
|
|
19
16
|
* @param opts
|
|
20
17
|
*/
|
|
21
18
|
export async function runTests(opts: RunState): Promise<void> {
|
|
19
|
+
const { RunnerUtil } = await import('../../src/execute/util.js');
|
|
20
|
+
const { Runner } = await import('../../src/execute/runner.js');
|
|
21
|
+
|
|
22
22
|
RunnerUtil.registerCleanup('runner');
|
|
23
23
|
|
|
24
24
|
if (process.env.TRV_TEST_DELAY) {
|
package/support/cli.test.ts
CHANGED
|
@@ -1,76 +1,57 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
|
|
4
|
-
import { path
|
|
3
|
+
import { path } from '@travetto/manifest';
|
|
5
4
|
import { GlobalEnvConfig } from '@travetto/base';
|
|
6
|
-
import { CliCommand,
|
|
5
|
+
import { CliCommandShape, CliCommand, CliValidationError } from '@travetto/cli';
|
|
7
6
|
import { WorkPool } from '@travetto/worker';
|
|
7
|
+
import { Max, Min } from '@travetto/schema';
|
|
8
8
|
|
|
9
|
-
import
|
|
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
|
-
};
|
|
9
|
+
import { TestFormat, TestMode } from './bin/types';
|
|
18
10
|
|
|
19
11
|
/**
|
|
20
12
|
* Launch test framework and execute tests
|
|
21
13
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
}
|
|
14
|
+
@CliCommand()
|
|
15
|
+
export class TestCommand implements CliCommandShape {
|
|
16
|
+
/** Output format for test results */
|
|
17
|
+
format: TestFormat = 'tap';
|
|
18
|
+
/** Number of tests to run concurrently */
|
|
19
|
+
@Min(1) @Max(WorkPool.MAX_SIZE)
|
|
20
|
+
concurrency: number = WorkPool.MAX_SIZE;
|
|
21
|
+
/** Test run mode */
|
|
22
|
+
mode: TestMode = 'standard';
|
|
43
23
|
|
|
44
24
|
envInit(): GlobalEnvConfig {
|
|
45
25
|
return { test: true };
|
|
46
26
|
}
|
|
47
27
|
|
|
48
|
-
|
|
49
|
-
return '
|
|
28
|
+
isFirstFile(first: string): Promise<boolean> {
|
|
29
|
+
return fs.stat(path.resolve(first ?? '')).then(x => x.isFile(), () => false);
|
|
50
30
|
}
|
|
51
31
|
|
|
52
|
-
async
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const [first] = regexes;
|
|
32
|
+
async resolvedMode(first: string, rest: string[]): Promise<TestMode> {
|
|
33
|
+
return (await this.isFirstFile(first)) && rest.length === 0 ? 'single' : this.mode;
|
|
34
|
+
}
|
|
56
35
|
|
|
57
|
-
|
|
36
|
+
async validate(first: string = 'test/.*', rest: string[]): Promise<CliValidationError | undefined> {
|
|
58
37
|
|
|
59
|
-
const
|
|
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
|
-
};
|
|
38
|
+
const mode = await this.resolvedMode(first, rest);
|
|
65
39
|
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return this.showHelp('Only files in the test/ folder are permitted to be run');
|
|
71
|
-
}
|
|
40
|
+
if (mode === 'single' && !await this.isFirstFile(first)) {
|
|
41
|
+
return { message: 'You must specify a proper test file to run in single mode', source: 'arg' };
|
|
42
|
+
} else if (!/test\//.test(first)) {
|
|
43
|
+
return { message: 'Only files in the test/ folder are permitted to be run', source: 'arg' };
|
|
72
44
|
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async main(first: string = 'test/.*', regexes: string[] = []): Promise<void> {
|
|
48
|
+
const { runTests } = await import('./bin/run.js');
|
|
73
49
|
|
|
74
|
-
|
|
50
|
+
return runTests({
|
|
51
|
+
args: [first, ...regexes],
|
|
52
|
+
mode: await this.resolvedMode(first, regexes),
|
|
53
|
+
concurrency: this.concurrency,
|
|
54
|
+
format: this.format
|
|
55
|
+
});
|
|
75
56
|
}
|
|
76
57
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GlobalEnvConfig, ShutdownManager } from '@travetto/base';
|
|
2
|
+
import { CliCommand } from '@travetto/cli';
|
|
3
|
+
|
|
4
|
+
/** Test child worker target */
|
|
5
|
+
@CliCommand({ hidden: true })
|
|
6
|
+
export class TestChildWorkerCommand {
|
|
7
|
+
envInit(): GlobalEnvConfig {
|
|
8
|
+
return { test: true, set: { FORCE_COLOR: 0 } };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async main(): Promise<void> {
|
|
12
|
+
if (process.send) {
|
|
13
|
+
// Shutdown when ipc bridge is closed
|
|
14
|
+
process.on('disconnect', () => ShutdownManager.execute());
|
|
15
|
+
}
|
|
16
|
+
const { TestChildWorker } = await import('../src/worker/child.js');
|
|
17
|
+
return new TestChildWorker().activate();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CliCommand } from '@travetto/cli';
|
|
2
|
+
import { RootIndex } from '@travetto/manifest';
|
|
3
|
+
|
|
4
|
+
import { SuiteRegistry } from '../src/registry/suite';
|
|
5
|
+
import { RunnerUtil } from '../src/execute/util';
|
|
6
|
+
import { GlobalEnvConfig } from '@travetto/base';
|
|
7
|
+
|
|
8
|
+
@CliCommand({ hidden: true })
|
|
9
|
+
export class TestCountCommand {
|
|
10
|
+
|
|
11
|
+
envInit(): GlobalEnvConfig {
|
|
12
|
+
return { debug: false, test: true };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async main(patterns: string[]) {
|
|
16
|
+
const regexes = patterns.map(x => new RegExp(x));
|
|
17
|
+
const files = await RunnerUtil.getTestFiles(regexes);
|
|
18
|
+
|
|
19
|
+
// Load all tests
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
await import(RootIndex.getFromSource(file)!.import);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await SuiteRegistry.init();
|
|
25
|
+
|
|
26
|
+
const suites = SuiteRegistry.getClasses();
|
|
27
|
+
const total = suites
|
|
28
|
+
.map(c => SuiteRegistry.get(c))
|
|
29
|
+
.filter(c => !RootIndex.getFunctionMetadata(c.class)?.abstract)
|
|
30
|
+
.reduce((acc, c) => acc + (c.tests?.length ?? 0), 0);
|
|
31
|
+
|
|
32
|
+
console.log(total);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GlobalEnvConfig } from '@travetto/base';
|
|
2
|
+
import { CliCommand } from '@travetto/cli';
|
|
3
|
+
|
|
4
|
+
import { runTests } from './bin/run';
|
|
5
|
+
import { TestFormat } from './bin/types';
|
|
6
|
+
|
|
7
|
+
/** Direct test invocation */
|
|
8
|
+
@CliCommand({ hidden: true })
|
|
9
|
+
export class TestDirectCommand {
|
|
10
|
+
|
|
11
|
+
format: TestFormat = 'tap';
|
|
12
|
+
|
|
13
|
+
envInit(): GlobalEnvConfig {
|
|
14
|
+
return { test: true };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
main(file: string, args: string[]): Promise<void> {
|
|
18
|
+
return runTests({ args: [file, ...args], format: this.format, mode: 'single', concurrency: 1 });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { GlobalEnvConfig } from '@travetto/base';
|
|
2
|
+
import { CliCommand } from '@travetto/cli';
|
|
3
|
+
|
|
4
|
+
import { TestFormat } from './bin/types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Invoke the test watcher
|
|
8
|
+
*/
|
|
9
|
+
@CliCommand()
|
|
10
|
+
export class TestWatcherCommand {
|
|
11
|
+
|
|
12
|
+
format: TestFormat = 'tap';
|
|
13
|
+
mode: 'all' | 'change' = 'all';
|
|
14
|
+
|
|
15
|
+
envInit(): GlobalEnvConfig {
|
|
16
|
+
return { test: true, dynamic: true };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async main(): Promise<void> {
|
|
20
|
+
// Quit on parent disconnect
|
|
21
|
+
if (process.send) {
|
|
22
|
+
process.on('disconnect', () => process.exit(0));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const { TestWatcher } = await import('../src/execute/watcher.js');
|
|
27
|
+
console.log('Starting');
|
|
28
|
+
await TestWatcher.watch(this.format, this.mode === 'all');
|
|
29
|
+
console.log('Done');
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error(err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -237,10 +237,10 @@ export class AssertTransformer {
|
|
|
237
237
|
if (matched) {
|
|
238
238
|
const resolved = state.resolveType(root);
|
|
239
239
|
if (resolved.key === 'literal' && matched.find(x => resolved.ctor === x)) { // Ensure method is against real type
|
|
240
|
-
|
|
241
|
-
fn: key.text,
|
|
242
|
-
args: [comp.arguments[0], comp.expression.expression, ...args.slice(1)]
|
|
243
|
-
}
|
|
240
|
+
switch (key.text) {
|
|
241
|
+
case 'includes': return { fn: key.text, args: [comp.expression.expression, comp.arguments[0], ...args.slice(1)] };
|
|
242
|
+
case 'test': return { fn: key.text, args: [comp.arguments[0], comp.expression.expression, ...args.slice(1)] };
|
|
243
|
+
}
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
}
|
package/support/bin/direct.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ConsoleManager, defineGlobalEnv } from '@travetto/base';
|
|
2
|
-
import { runTests } from './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
|
-
}
|