@travetto/test 6.0.2 → 7.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/README.md +7 -3
- package/__index__.ts +3 -1
- package/package.json +7 -7
- package/src/consumer/decorator.ts +13 -0
- package/src/consumer/registry-adapter.ts +43 -0
- package/src/consumer/registry-index.ts +82 -0
- package/src/consumer/types/cumulative.ts +4 -4
- package/src/consumer/types/event.ts +1 -1
- package/src/consumer/types/exec.ts +1 -1
- package/src/consumer/types/json.ts +1 -1
- package/src/consumer/types/noop.ts +1 -1
- package/src/consumer/types/tap-summary.ts +1 -1
- package/src/consumer/types/tap.ts +2 -2
- package/src/consumer/types/xunit.ts +1 -1
- package/src/consumer/types.ts +13 -0
- package/src/decorator/suite.ts +43 -29
- package/src/decorator/test.ts +19 -18
- package/src/execute/executor.ts +10 -5
- package/src/execute/runner.ts +3 -3
- package/src/execute/watcher.ts +15 -13
- package/src/model/common.ts +4 -4
- package/src/model/suite.ts +5 -5
- package/src/registry/registry-adapter.ts +127 -0
- package/src/registry/registry-index.ts +117 -0
- package/support/bin/run.ts +13 -16
- package/support/cli.test_child.ts +3 -1
- package/support/cli.test_digest.ts +13 -6
- package/support/cli.test_direct.ts +14 -1
- package/src/consumer/registry.ts +0 -68
- package/src/consumer/types/all.ts +0 -10
- package/src/registry/suite.ts +0 -143
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This module provides unit testing functionality that integrates with the framewo
|
|
|
21
21
|
**Note**: All tests should be under the `**/*` folders. The pattern for tests is defined as as a standard glob using [Node](https://nodejs.org)'s built in globbing support.
|
|
22
22
|
|
|
23
23
|
## Definition
|
|
24
|
-
A test suite is a collection of individual tests. All test suites are classes with the [@Suite](https://github.com/travetto/travetto/tree/main/module/test/src/decorator/suite.ts#
|
|
24
|
+
A test suite is a collection of individual tests. All test suites are classes with the [@Suite](https://github.com/travetto/travetto/tree/main/module/test/src/decorator/suite.ts#L14) decorator. Tests are defined as methods on the suite class, using the [@Test](https://github.com/travetto/travetto/tree/main/module/test/src/decorator/test.ts#L23) decorator. All tests intrinsically support `async`/`await`.
|
|
25
25
|
|
|
26
26
|
A simple example would be:
|
|
27
27
|
|
|
@@ -79,9 +79,11 @@ would translate to:
|
|
|
79
79
|
"use strict";
|
|
80
80
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
81
81
|
const tslib_1 = require("tslib");
|
|
82
|
+
const Δmethod = tslib_1.__importStar(require("@travetto/schema/src/decorator/method.js"));
|
|
82
83
|
const Δdebug = tslib_1.__importStar(require("@travetto/runtime/src/debug.js"));
|
|
83
84
|
const Δcheck = tslib_1.__importStar(require("@travetto/test/src/assert/check.js"));
|
|
84
85
|
const Δfunction = tslib_1.__importStar(require("@travetto/runtime/src/function.js"));
|
|
86
|
+
const Δschema = tslib_1.__importStar(require("@travetto/schema/src/decorator/schema.js"));
|
|
85
87
|
var mod_1 = ["@travetto/test", "doc/assert-example.ts"];
|
|
86
88
|
const node_assert_1 = tslib_1.__importDefault(require("node:assert"));
|
|
87
89
|
const test_1 = require("@travetto/test");
|
|
@@ -94,10 +96,12 @@ let SimpleTest = class SimpleTest {
|
|
|
94
96
|
}
|
|
95
97
|
};
|
|
96
98
|
tslib_1.__decorate([
|
|
97
|
-
(0, test_1.Test)()
|
|
99
|
+
(0, test_1.Test)(),
|
|
100
|
+
Δmethod.Method({ returnType: {} })
|
|
98
101
|
], SimpleTest.prototype, "test", null);
|
|
99
102
|
SimpleTest = tslib_1.__decorate([
|
|
100
|
-
(0, test_1.Suite)()
|
|
103
|
+
(0, test_1.Suite)(),
|
|
104
|
+
Δschema.Schema()
|
|
101
105
|
], SimpleTest);
|
|
102
106
|
```
|
|
103
107
|
|
package/__index__.ts
CHANGED
|
@@ -4,8 +4,10 @@ export * from './src/decorator/test.ts';
|
|
|
4
4
|
export * from './src/model/suite.ts';
|
|
5
5
|
export * from './src/model/test.ts';
|
|
6
6
|
export * from './src/model/event.ts';
|
|
7
|
-
export * from './src/registry/
|
|
7
|
+
export * from './src/registry/registry-index.ts';
|
|
8
|
+
export * from './src/registry/registry-adapter.ts';
|
|
8
9
|
export * from './src/fixture.ts';
|
|
9
10
|
export * from './src/consumer/types.ts';
|
|
11
|
+
export * from './src/consumer/registry-index.ts';
|
|
10
12
|
export * from './src/execute/error.ts';
|
|
11
13
|
export { TestWatchEvent } from './src/worker/types.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/test",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.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/registry": "^
|
|
31
|
-
"@travetto/runtime": "^
|
|
32
|
-
"@travetto/terminal": "^
|
|
33
|
-
"@travetto/worker": "^
|
|
30
|
+
"@travetto/registry": "^7.0.0-rc.1",
|
|
31
|
+
"@travetto/runtime": "^7.0.0-rc.1",
|
|
32
|
+
"@travetto/terminal": "^7.0.0-rc.1",
|
|
33
|
+
"@travetto/worker": "^7.0.0-rc.1",
|
|
34
34
|
"yaml": "^2.8.1"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^
|
|
38
|
-
"@travetto/transformer": "^
|
|
37
|
+
"@travetto/cli": "^7.0.0-rc.1",
|
|
38
|
+
"@travetto/transformer": "^7.0.0-rc.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/transformer": {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Class } from '@travetto/runtime';
|
|
2
|
+
|
|
3
|
+
import { TestConsumerShape } from './types';
|
|
4
|
+
import { TestConsumerRegistryIndex } from './registry-index';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Registers a class a valid test consumer
|
|
8
|
+
*/
|
|
9
|
+
export function TestConsumer(): (cls: Class<TestConsumerShape>) => void {
|
|
10
|
+
return function (cls: Class<TestConsumerShape>): void {
|
|
11
|
+
TestConsumerRegistryIndex.getForRegister(cls).register();
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { classConstruct, describeFunction, type Class } from '@travetto/runtime';
|
|
4
|
+
import { RegistryAdapter } from '@travetto/registry';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import type { TestConsumerShape, TestConsumerConfig } from './types.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Test Results Handler Registry
|
|
11
|
+
*/
|
|
12
|
+
export class TestConsumerRegistryAdapter implements RegistryAdapter<TestConsumerConfig> {
|
|
13
|
+
config: TestConsumerConfig;
|
|
14
|
+
#cls: Class<TestConsumerShape>;
|
|
15
|
+
|
|
16
|
+
constructor(cls: Class<TestConsumerShape>) {
|
|
17
|
+
this.#cls = cls;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
register(...data: Partial<TestConsumerConfig>[]): TestConsumerConfig {
|
|
21
|
+
const desc = describeFunction(this.#cls);
|
|
22
|
+
this.config ??= {
|
|
23
|
+
cls: this.#cls,
|
|
24
|
+
type: desc.module?.includes('@travetto') ? path.basename(desc.modulePath) : desc.import
|
|
25
|
+
};
|
|
26
|
+
Object.assign(this.config, ...data);
|
|
27
|
+
return this.config;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
finalize(): void {
|
|
31
|
+
// Do nothing for now
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get(): TestConsumerConfig {
|
|
35
|
+
return this.config;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async instance(options?: Record<string, unknown>): Promise<TestConsumerShape> {
|
|
39
|
+
const inst = classConstruct(this.#cls);
|
|
40
|
+
await inst.setOptions?.(options);
|
|
41
|
+
return inst;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { RuntimeIndex, type Class } from '@travetto/runtime';
|
|
2
|
+
import { Registry, RegistryIndex, RegistryIndexStore } from '@travetto/registry';
|
|
3
|
+
|
|
4
|
+
import type { TestConsumerShape } from './types.ts';
|
|
5
|
+
import type { RunState } from '../execute/types.ts';
|
|
6
|
+
import { TestConsumerRegistryAdapter } from './registry-adapter.ts';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Test Results Handler Registry
|
|
10
|
+
*/
|
|
11
|
+
export class TestConsumerRegistryIndex implements RegistryIndex {
|
|
12
|
+
|
|
13
|
+
static #instance = Registry.registerIndex(this);
|
|
14
|
+
|
|
15
|
+
static getForRegister(cls: Class): TestConsumerRegistryAdapter {
|
|
16
|
+
return this.#instance.store.getForRegister(cls);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get all registered types
|
|
21
|
+
*/
|
|
22
|
+
static getTypes(): Promise<string[]> {
|
|
23
|
+
return this.#instance.getTypes();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get a consumer instance that supports summarization
|
|
28
|
+
* @param consumer The consumer identifier or the actual consumer
|
|
29
|
+
*/
|
|
30
|
+
static getInstance(state: Pick<RunState, 'consumer' | 'consumerOptions'>): Promise<TestConsumerShape> {
|
|
31
|
+
return this.#instance.getInstance(state);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#initialized: Promise<void>;
|
|
35
|
+
store = new RegistryIndexStore(TestConsumerRegistryAdapter);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Manual initialization when running outside of the bootstrap process
|
|
39
|
+
*/
|
|
40
|
+
async #init(): Promise<void> {
|
|
41
|
+
const allFiles = RuntimeIndex.find({
|
|
42
|
+
module: m => m.name === '@travetto/test',
|
|
43
|
+
file: f => f.relativeFile.startsWith('src/consumer/types/')
|
|
44
|
+
});
|
|
45
|
+
for (const f of allFiles) {
|
|
46
|
+
await import(f.outputFile);
|
|
47
|
+
}
|
|
48
|
+
for (const cls of this.store.getClasses()) {
|
|
49
|
+
this.store.finalize(cls);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
process(): void { }
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get types
|
|
57
|
+
*/
|
|
58
|
+
async getTypes(): Promise<string[]> {
|
|
59
|
+
await (this.#initialized ??= this.#init());
|
|
60
|
+
const out: string[] = [];
|
|
61
|
+
for (const cls of this.store.getClasses()) {
|
|
62
|
+
const adapter = this.store.get(cls);
|
|
63
|
+
out.push(adapter.get().type);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get a consumer instance that supports summarization
|
|
70
|
+
* @param consumer The consumer identifier or the actual consumer
|
|
71
|
+
*/
|
|
72
|
+
async getInstance(state: Pick<RunState, 'consumer' | 'consumerOptions'>): Promise<TestConsumerShape> {
|
|
73
|
+
await (this.#initialized ??= this.#init());
|
|
74
|
+
for (const cls of this.store.getClasses()) {
|
|
75
|
+
const adapter = this.store.get(cls);
|
|
76
|
+
if (adapter.get().type === state.consumer) {
|
|
77
|
+
return adapter.instance(state.consumerOptions);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`No test consumer registered for type ${state.consumer}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -6,8 +6,8 @@ import type { TestConsumerShape } from '../types.ts';
|
|
|
6
6
|
import type { TestEvent } from '../../model/event.ts';
|
|
7
7
|
import type { TestResult } from '../../model/test.ts';
|
|
8
8
|
import type { SuiteResult } from '../../model/suite.ts';
|
|
9
|
-
import { SuiteRegistry } from '../../registry/suite.ts';
|
|
10
9
|
import { DelegatingConsumer } from './delegating.ts';
|
|
10
|
+
import { SuiteRegistryIndex } from '../../registry/registry-index.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Cumulative Summary consumer
|
|
@@ -30,7 +30,7 @@ export class CumulativeSummaryConsumer extends DelegatingConsumer {
|
|
|
30
30
|
// Was only loading to verify existence (TODO: double-check)
|
|
31
31
|
if (existsSync(RuntimeIndex.getFromImport(test.import)!.sourceFile)) {
|
|
32
32
|
(this.#state[test.classId] ??= {})[test.methodName] = test.status;
|
|
33
|
-
const SuiteCls =
|
|
33
|
+
const SuiteCls = SuiteRegistryIndex.getClasses().find(x => x.Ⲑid === test.classId);
|
|
34
34
|
return SuiteCls ? this.computeTotal(SuiteCls) : this.removeClass(test.classId);
|
|
35
35
|
} else {
|
|
36
36
|
return this.removeClass(test.classId);
|
|
@@ -51,8 +51,8 @@ export class CumulativeSummaryConsumer extends DelegatingConsumer {
|
|
|
51
51
|
* Compute totals
|
|
52
52
|
*/
|
|
53
53
|
computeTotal(cls: Class): SuiteResult {
|
|
54
|
-
const suite =
|
|
55
|
-
const total = suite.tests.reduce((acc, x) => {
|
|
54
|
+
const suite = SuiteRegistryIndex.getConfig(cls);
|
|
55
|
+
const total = Object.values(suite.tests).reduce((acc, x) => {
|
|
56
56
|
const status = this.#state[x.classId][x.methodName] ?? 'unknown';
|
|
57
57
|
acc[status] += 1;
|
|
58
58
|
return acc;
|
|
@@ -4,7 +4,7 @@ import { Util } from '@travetto/runtime';
|
|
|
4
4
|
|
|
5
5
|
import type { TestEvent } from '../../model/event.ts';
|
|
6
6
|
import type { TestConsumerShape } from '../types.ts';
|
|
7
|
-
import { TestConsumer } from '../
|
|
7
|
+
import { TestConsumer } from '../decorator.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Streams all test events a JSON payload, in an nd-json format
|
|
@@ -3,7 +3,7 @@ import { Util } from '@travetto/runtime';
|
|
|
3
3
|
|
|
4
4
|
import type { TestEvent } from '../../model/event.ts';
|
|
5
5
|
import type { TestConsumerShape } from '../types.ts';
|
|
6
|
-
import { TestConsumer } from '../
|
|
6
|
+
import { TestConsumer } from '../decorator.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Triggers each event as an IPC command to a parent process
|
|
@@ -5,7 +5,7 @@ import type { TestEvent } from '../../model/event.ts';
|
|
|
5
5
|
import type { TestResult } from '../../model/test.ts';
|
|
6
6
|
|
|
7
7
|
import type { SuitesSummary, TestConsumerShape, TestRunState } from '../types.ts';
|
|
8
|
-
import { TestConsumer } from '../
|
|
8
|
+
import { TestConsumer } from '../decorator.ts';
|
|
9
9
|
|
|
10
10
|
import { TapEmitter } from './tap.ts';
|
|
11
11
|
import { CONSOLE_ENHANCER, TestResultsEnhancer } from '../enhancer.ts';
|
|
@@ -6,7 +6,7 @@ import { TimeUtil, RuntimeIndex, hasToJSON } from '@travetto/runtime';
|
|
|
6
6
|
|
|
7
7
|
import type { TestEvent } from '../../model/event.ts';
|
|
8
8
|
import type { SuitesSummary, TestConsumerShape } from '../types.ts';
|
|
9
|
-
import { TestConsumer } from '../
|
|
9
|
+
import { TestConsumer } from '../decorator.ts';
|
|
10
10
|
import { TestResultsEnhancer, CONSOLE_ENHANCER } from '../enhancer.ts';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -91,7 +91,7 @@ export class TapEmitter implements TestConsumerShape {
|
|
|
91
91
|
let subCount = 0;
|
|
92
92
|
for (const asrt of test.assertions) {
|
|
93
93
|
const text = asrt.message ? `${asrt.text} (${this.#enhancer.failure(asrt.message)})` : asrt.text;
|
|
94
|
-
const pth = `./${path.relative(process.cwd(), RuntimeIndex.getFromImport(asrt.import)!.sourceFile)}
|
|
94
|
+
const pth = asrt.import ? `./${path.relative(process.cwd(), RuntimeIndex.getFromImport(asrt.import)!.sourceFile)}` : '<unknown>';
|
|
95
95
|
let subMessage = [
|
|
96
96
|
this.#enhancer.assertNumber(++subCount),
|
|
97
97
|
'-',
|
|
@@ -6,7 +6,7 @@ import { RuntimeIndex } from '@travetto/runtime';
|
|
|
6
6
|
|
|
7
7
|
import type { TestEvent } from '../../model/event.ts';
|
|
8
8
|
import type { SuitesSummary, TestConsumerShape } from '../types.ts';
|
|
9
|
-
import { TestConsumer } from '../
|
|
9
|
+
import { TestConsumer } from '../decorator.ts';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Xunit consumer, compatible with JUnit formatters
|
package/src/consumer/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Class } from '@travetto/runtime';
|
|
2
|
+
|
|
1
3
|
import { TestEvent } from '../model/event.ts';
|
|
2
4
|
import { Counts, SuiteResult } from '../model/suite.ts';
|
|
3
5
|
|
|
@@ -23,6 +25,17 @@ export type TestRunState = {
|
|
|
23
25
|
testCount?: number;
|
|
24
26
|
};
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Configuration for a test consumer
|
|
30
|
+
*/
|
|
31
|
+
export interface TestConsumerConfig {
|
|
32
|
+
cls: Class<TestConsumerShape>;
|
|
33
|
+
/**
|
|
34
|
+
* Consumer type key
|
|
35
|
+
*/
|
|
36
|
+
type: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
/**
|
|
27
40
|
* A test consumer shape
|
|
28
41
|
*/
|
package/src/decorator/suite.ts
CHANGED
|
@@ -1,60 +1,74 @@
|
|
|
1
|
-
import { castTo, Class, ClassInstance, describeFunction } from '@travetto/runtime';
|
|
1
|
+
import { castTo, Class, ClassInstance, describeFunction, getClass } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { SuiteRegistry } from '../registry/suite.ts';
|
|
4
3
|
import { SuiteConfig } from '../model/suite.ts';
|
|
4
|
+
import { SuiteRegistryIndex } from '../registry/registry-index.ts';
|
|
5
5
|
|
|
6
6
|
export type SuitePhase = 'beforeAll' | 'beforeEach' | 'afterAll' | 'afterEach';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Register a class to be defined as a test suite, and a candidate for testing
|
|
10
10
|
* @param description The Suite description
|
|
11
|
-
* @augments `@travetto/
|
|
11
|
+
* @augments `@travetto/schema:Schema`
|
|
12
|
+
* @kind decorator
|
|
12
13
|
*/
|
|
13
14
|
export function Suite(): ClassDecorator;
|
|
14
15
|
export function Suite(...rest: Partial<SuiteConfig>[]): ClassDecorator;
|
|
15
16
|
export function Suite(description: string, ...rest: Partial<SuiteConfig>[]): ClassDecorator;
|
|
16
17
|
export function Suite(description?: string | Partial<SuiteConfig>, ...rest: Partial<SuiteConfig>[]): ClassDecorator {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const dec = (target: Class): typeof target => {
|
|
27
|
-
const cfg = { description: descriptionString, ...extra };
|
|
28
|
-
if (describeFunction(target).abstract) {
|
|
29
|
-
cfg.skip = true;
|
|
30
|
-
}
|
|
31
|
-
SuiteRegistry.register(target, cfg);
|
|
32
|
-
return target;
|
|
18
|
+
const dec = (cls: Class): typeof cls => {
|
|
19
|
+
const isAbstract = describeFunction(cls).abstract;
|
|
20
|
+
SuiteRegistryIndex.getForRegister(cls).register(
|
|
21
|
+
...(typeof description !== 'string' && description ? [description] : []),
|
|
22
|
+
...rest,
|
|
23
|
+
...isAbstract ? [{ skip: true }] : [],
|
|
24
|
+
...(typeof description === 'string' ? [{ description }] : []),
|
|
25
|
+
);
|
|
26
|
+
return cls;
|
|
33
27
|
};
|
|
34
28
|
|
|
35
29
|
return castTo(dec);
|
|
36
30
|
}
|
|
37
31
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Registers function to run before any tests are run
|
|
34
|
+
* @kind decorator
|
|
35
|
+
*/
|
|
36
|
+
export function BeforeAll() {
|
|
37
|
+
return (instance: ClassInstance, property: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
|
|
38
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).register({ beforeAll: [descriptor.value] });
|
|
41
39
|
return descriptor;
|
|
42
40
|
};
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
/**
|
|
46
|
-
* Registers function to run before any tests are run
|
|
47
|
-
*/
|
|
48
|
-
export const BeforeAll = listener.bind(null, 'beforeAll');
|
|
49
43
|
/**
|
|
50
44
|
* Registers function to run before each test is run
|
|
45
|
+
* @kind decorator
|
|
51
46
|
*/
|
|
52
|
-
export
|
|
47
|
+
export function BeforeEach() {
|
|
48
|
+
return (instance: ClassInstance, property: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
|
|
49
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).register({ beforeEach: [descriptor.value] });
|
|
50
|
+
return descriptor;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
53
54
|
/**
|
|
54
55
|
* Registers function to run after all tests are run
|
|
56
|
+
* @kind decorator
|
|
55
57
|
*/
|
|
56
|
-
export
|
|
58
|
+
export function AfterAll() {
|
|
59
|
+
return (instance: ClassInstance, property: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
|
|
60
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).register({ afterAll: [descriptor.value] });
|
|
61
|
+
return descriptor;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
57
65
|
/**
|
|
58
66
|
* Registers function to run after each test is run
|
|
67
|
+
* @kind decorator
|
|
59
68
|
*/
|
|
60
|
-
export
|
|
69
|
+
export function AfterEach() {
|
|
70
|
+
return (instance: ClassInstance, property: string, descriptor: PropertyDescriptor): PropertyDescriptor => {
|
|
71
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).register({ afterEach: [descriptor.value] });
|
|
72
|
+
return descriptor;
|
|
73
|
+
};
|
|
74
|
+
}
|
package/src/decorator/test.ts
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
import { ClassInstance } from '@travetto/runtime';
|
|
1
|
+
import { ClassInstance, getClass } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import { SuiteRegistry } from '../registry/suite.ts';
|
|
4
3
|
import { TestConfig, ThrowableError } from '../model/test.ts';
|
|
4
|
+
import { SuiteRegistryIndex } from '../registry/registry-index.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* The `@AssertCheck` indicates that a function's assert calls should be transformed
|
|
8
|
+
* @augments `@travetto/test:AssertCheck`
|
|
9
|
+
* @kind decorator
|
|
8
10
|
*/
|
|
9
11
|
export function AssertCheck(): MethodDecorator {
|
|
10
|
-
return (
|
|
12
|
+
return (instance: ClassInstance, property: string | symbol, descriptor: PropertyDescriptor) => descriptor;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The `@Test` decorator register a test to be run as part of the enclosing suite.
|
|
15
17
|
* @param description The test description
|
|
16
|
-
* @augments `@travetto/
|
|
18
|
+
* @augments `@travetto/schema:Method`
|
|
17
19
|
* @augments `@travetto/test:AssertCheck`
|
|
18
20
|
* @augments `@travetto/runtime:DebugBreak`
|
|
21
|
+
* @kind decorator
|
|
19
22
|
*/
|
|
20
23
|
export function Test(): MethodDecorator;
|
|
21
24
|
export function Test(...rest: Partial<TestConfig>[]): MethodDecorator;
|
|
22
25
|
export function Test(description: string, ...rest: Partial<TestConfig>[]): MethodDecorator;
|
|
23
26
|
export function Test(description?: string | Partial<TestConfig>, ...rest: Partial<TestConfig>[]): MethodDecorator {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Object.assign(extra, r);
|
|
31
|
-
}
|
|
32
|
-
return (inst: ClassInstance, prop: string | symbol, descriptor: PropertyDescriptor) => {
|
|
33
|
-
SuiteRegistry.registerField(inst.constructor, descriptor.value, extra);
|
|
27
|
+
return (instance: ClassInstance, property: string | symbol, descriptor: PropertyDescriptor) => {
|
|
28
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).registerTest(property, descriptor.value,
|
|
29
|
+
...(typeof description !== 'string' && description) ? [description] : [],
|
|
30
|
+
...rest,
|
|
31
|
+
...(typeof description === 'string') ? [{ description }] : []
|
|
32
|
+
);
|
|
34
33
|
return descriptor;
|
|
35
34
|
};
|
|
36
35
|
}
|
|
@@ -38,10 +37,11 @@ export function Test(description?: string | Partial<TestConfig>, ...rest: Partia
|
|
|
38
37
|
/**
|
|
39
38
|
* Marks a method as should throw to indicate a lack of throwing is a problem
|
|
40
39
|
* @param state The parameters to use for checking if the response is valid
|
|
40
|
+
* @kind decorator
|
|
41
41
|
*/
|
|
42
42
|
export function ShouldThrow(state: ThrowableError): MethodDecorator {
|
|
43
|
-
return (
|
|
44
|
-
|
|
43
|
+
return (instance: ClassInstance, property: string | symbol, descriptor: PropertyDescriptor) => {
|
|
44
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).registerTest(property, descriptor.value, { shouldThrow: state });
|
|
45
45
|
return descriptor;
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -49,10 +49,11 @@ export function ShouldThrow(state: ThrowableError): MethodDecorator {
|
|
|
49
49
|
/**
|
|
50
50
|
* Sets the full timeout window for a given test
|
|
51
51
|
* @param ms Max time to wait
|
|
52
|
+
* @kind decorator
|
|
52
53
|
*/
|
|
53
54
|
export function Timeout(ms: number): MethodDecorator {
|
|
54
|
-
return (
|
|
55
|
-
|
|
55
|
+
return (instance: ClassInstance, property: string | symbol, descriptor: PropertyDescriptor) => {
|
|
56
|
+
SuiteRegistryIndex.getForRegister(getClass(instance)).registerTest(property, descriptor.value, { timeout: ms });
|
|
56
57
|
return descriptor;
|
|
57
58
|
};
|
|
58
59
|
}
|
package/src/execute/executor.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AssertionError } from 'node:assert';
|
|
2
2
|
|
|
3
|
-
import { Env, TimeUtil, Runtime, castTo } from '@travetto/runtime';
|
|
3
|
+
import { Env, TimeUtil, Runtime, castTo, classConstruct } from '@travetto/runtime';
|
|
4
|
+
import { Registry } from '@travetto/registry';
|
|
4
5
|
|
|
5
|
-
import { SuiteRegistry } from '../registry/suite.ts';
|
|
6
6
|
import { TestConfig, TestResult, TestRun } from '../model/test.ts';
|
|
7
7
|
import { SuiteConfig, SuiteFailure, SuiteResult } from '../model/suite.ts';
|
|
8
8
|
import { TestConsumerShape } from '../consumer/types.ts';
|
|
@@ -13,6 +13,7 @@ import { TestPhaseManager } from './phase.ts';
|
|
|
13
13
|
import { AssertUtil } from '../assert/util.ts';
|
|
14
14
|
import { Barrier } from './barrier.ts';
|
|
15
15
|
import { ExecutionError } from './error.ts';
|
|
16
|
+
import { SuiteRegistryIndex } from '../registry/registry-index.ts';
|
|
16
17
|
|
|
17
18
|
const TEST_TIMEOUT = TimeUtil.fromValue(Env.TRV_TEST_TIMEOUT.val) ?? 5000;
|
|
18
19
|
|
|
@@ -55,7 +56,7 @@ export class TestExecutor {
|
|
|
55
56
|
* This method should never throw under any circumstances.
|
|
56
57
|
*/
|
|
57
58
|
async #executeTestMethod(test: TestConfig): Promise<Error | undefined> {
|
|
58
|
-
const suite =
|
|
59
|
+
const suite = SuiteRegistryIndex.getConfig(test.class);
|
|
59
60
|
|
|
60
61
|
// Ensure all the criteria below are satisfied before moving forward
|
|
61
62
|
return Barrier.awaitOperation(test.timeout || TEST_TIMEOUT, async () => {
|
|
@@ -175,6 +176,9 @@ export class TestExecutor {
|
|
|
175
176
|
* Execute an entire suite
|
|
176
177
|
*/
|
|
177
178
|
async executeSuite(suite: SuiteConfig, tests: TestConfig[]): Promise<void> {
|
|
179
|
+
|
|
180
|
+
suite.instance = classConstruct(suite.class);
|
|
181
|
+
|
|
178
182
|
if (!tests.length || await this.#shouldSkip(suite, suite.instance)) {
|
|
179
183
|
return;
|
|
180
184
|
}
|
|
@@ -246,15 +250,16 @@ export class TestExecutor {
|
|
|
246
250
|
if (!(err instanceof Error)) {
|
|
247
251
|
throw err;
|
|
248
252
|
}
|
|
253
|
+
console.error(err);
|
|
249
254
|
this.#onSuiteFailure(AssertUtil.gernerateImportFailure(run.import, err));
|
|
250
255
|
return;
|
|
251
256
|
}
|
|
252
257
|
|
|
253
258
|
// Initialize registry (after loading the above)
|
|
254
|
-
await
|
|
259
|
+
await Registry.finalizeForIndex(SuiteRegistryIndex);
|
|
255
260
|
|
|
256
261
|
// Convert inbound arguments to specific tests to run
|
|
257
|
-
const suites =
|
|
262
|
+
const suites = SuiteRegistryIndex.getSuiteTests(run);
|
|
258
263
|
if (!suites.length) {
|
|
259
264
|
console.warn('Unable to find suites for ', run);
|
|
260
265
|
}
|
package/src/execute/runner.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { TestRun } from '../model/test.ts';
|
|
|
10
10
|
import { TestExecutor } from './executor.ts';
|
|
11
11
|
import { RunnerUtil } from './util.ts';
|
|
12
12
|
import { RunState } from './types.ts';
|
|
13
|
-
import {
|
|
13
|
+
import { TestConsumerRegistryIndex } from '../consumer/registry-index.ts';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Test Runner
|
|
@@ -27,7 +27,7 @@ export class Runner {
|
|
|
27
27
|
* Run all files
|
|
28
28
|
*/
|
|
29
29
|
async runFiles(globs?: string[]): Promise<boolean> {
|
|
30
|
-
const target = await
|
|
30
|
+
const target = await TestConsumerRegistryIndex.getInstance(this.#state);
|
|
31
31
|
const consumer = new RunnableTestConsumer(target);
|
|
32
32
|
const tests = await RunnerUtil.getTestDigest(globs, this.#state.tags);
|
|
33
33
|
const testRuns = RunnerUtil.getTestRuns(tests)
|
|
@@ -60,7 +60,7 @@ export class Runner {
|
|
|
60
60
|
RuntimeIndex.reinitForModule(entry.module);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
const target = await
|
|
63
|
+
const target = await TestConsumerRegistryIndex.getInstance(this.#state);
|
|
64
64
|
|
|
65
65
|
const consumer = new RunnableTestConsumer(target)
|
|
66
66
|
.withTransformer(e => {
|