@travetto/test 5.0.0-rc.3 → 5.0.0-rc.4
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 -8
- package/package.json +7 -7
- package/src/assert/capture.ts +3 -5
- package/src/assert/check.ts +4 -12
- package/src/assert/util.ts +9 -7
- package/src/consumer/types/cumulative.ts +6 -5
- package/src/consumer/types/tap.ts +3 -2
- package/src/consumer/types/xunit.ts +4 -2
- package/src/decorator/test.ts +1 -0
- package/src/execute/executor.ts +10 -15
- package/src/execute/runner.ts +14 -13
- package/src/execute/util.ts +30 -30
- package/src/execute/watcher.ts +15 -13
- package/src/model/common.ts +2 -6
- package/src/model/suite.ts +2 -2
- package/src/model/test.ts +2 -2
- package/src/registry/suite.ts +5 -6
- package/src/trv.d.ts +0 -4
- package/src/worker/child.ts +2 -2
- package/src/worker/standard.ts +13 -12
- package/src/worker/types.ts +14 -1
- package/support/cli.test_count.ts +4 -5
- package/support/transformer.annotate.ts +0 -103
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 `test/.*` folders. The pattern for tests is defined as a regex and not standard globbing.
|
|
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#L13) 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#
|
|
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#L13) 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#L20) decorator. All tests intrinsically support `async`/`await`.
|
|
25
25
|
|
|
26
26
|
A simple example would be:
|
|
27
27
|
|
|
@@ -79,27 +79,25 @@ would translate to:
|
|
|
79
79
|
"use strict";
|
|
80
80
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
81
81
|
const tslib_1 = require("tslib");
|
|
82
|
-
const Ⲑ
|
|
82
|
+
const Ⲑ_debug_1 = tslib_1.__importStar(require("@travetto/runtime/src/debug.js"));
|
|
83
83
|
const Ⲑ_check_1 = tslib_1.__importStar(require("@travetto/test/src/assert/check.js"));
|
|
84
|
-
const Ⲑ_decorator_1 = tslib_1.__importStar(require("@travetto/registry/src/decorator.js"));
|
|
85
84
|
const Ⲑ_function_1 = tslib_1.__importStar(require("@travetto/runtime/src/function.js"));
|
|
86
85
|
var ᚕm = ["@travetto/test", "doc/assert-example"];
|
|
87
86
|
const node_assert_1 = tslib_1.__importDefault(require("node:assert"));
|
|
88
87
|
const test_1 = require("@travetto/test");
|
|
89
88
|
let SimpleTest = class SimpleTest {
|
|
90
|
-
static Ⲑinit = Ⲑ_function_1.
|
|
89
|
+
static Ⲑinit = Ⲑ_function_1.registerFunction(SimpleTest, ᚕm, { hash: 1887908328, lines: [5, 12] }, { test: { hash: 102834457, lines: [8, 11, 10] } }, false, false);
|
|
91
90
|
async test() {
|
|
92
|
-
if (Ⲑ
|
|
91
|
+
if (Ⲑ_debug_1.tryDebugger)
|
|
93
92
|
debugger;
|
|
94
93
|
Ⲑ_check_1.AssertCheck.check({ module: ᚕm, line: 10, text: "{ size: 20, address: { state: 'VA' } }", operator: "deepStrictEqual" }, true, { size: 20, address: { state: 'VA' } }, {});
|
|
95
94
|
}
|
|
96
95
|
};
|
|
97
96
|
tslib_1.__decorate([
|
|
98
|
-
(0, test_1.Test)(
|
|
97
|
+
(0, test_1.Test)()
|
|
99
98
|
], SimpleTest.prototype, "test", null);
|
|
100
99
|
SimpleTest = tslib_1.__decorate([
|
|
101
|
-
|
|
102
|
-
(0, test_1.Suite)({ ident: "@Suite()" })
|
|
100
|
+
(0, test_1.Suite)()
|
|
103
101
|
], SimpleTest);
|
|
104
102
|
```
|
|
105
103
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/test",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.4",
|
|
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/runtime": "^5.0.0-rc.
|
|
31
|
-
"@travetto/registry": "^5.0.0-rc.
|
|
32
|
-
"@travetto/terminal": "^5.0.0-rc.
|
|
33
|
-
"@travetto/worker": "^5.0.0-rc.
|
|
30
|
+
"@travetto/runtime": "^5.0.0-rc.4",
|
|
31
|
+
"@travetto/registry": "^5.0.0-rc.4",
|
|
32
|
+
"@travetto/terminal": "^5.0.0-rc.4",
|
|
33
|
+
"@travetto/worker": "^5.0.0-rc.4",
|
|
34
34
|
"yaml": "^2.4.5"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^5.0.0-rc.
|
|
38
|
-
"@travetto/transformer": "^5.0.0-rc.
|
|
37
|
+
"@travetto/cli": "^5.0.0-rc.4",
|
|
38
|
+
"@travetto/transformer": "^5.0.0-rc.3"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@travetto/transformer": {
|
package/src/assert/capture.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import { RuntimeIndex } from '@travetto/runtime';
|
|
3
2
|
|
|
4
3
|
import { Assertion, TestConfig } from '../model/test';
|
|
5
4
|
|
|
6
5
|
export interface CaptureAssert extends Partial<Assertion> {
|
|
7
|
-
module
|
|
8
|
-
file: string;
|
|
6
|
+
module?: [string, string];
|
|
9
7
|
line: number;
|
|
10
8
|
text: string;
|
|
11
9
|
operator: string;
|
|
@@ -28,9 +26,9 @@ class $AssertCapture {
|
|
|
28
26
|
|
|
29
27
|
// Emit and collect, every assertion as it occurs
|
|
30
28
|
const handler = (a: CaptureAssert): void => {
|
|
31
|
-
const assrt = {
|
|
29
|
+
const assrt: Assertion = {
|
|
32
30
|
...a,
|
|
33
|
-
|
|
31
|
+
import: a.import ?? a.module!.join('/'),
|
|
34
32
|
classId: test.classId,
|
|
35
33
|
methodName: test.methodName
|
|
36
34
|
};
|
package/src/assert/check.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
|
-
import { AppError, ClassInstance, Class
|
|
3
|
+
import { AppError, ClassInstance, Class } from '@travetto/runtime';
|
|
4
4
|
|
|
5
5
|
import { ThrowableError, TestConfig, Assertion } from '../model/test';
|
|
6
6
|
import { AssertCapture, CaptureAssert } from './capture';
|
|
@@ -23,9 +23,6 @@ export class AssertCheck {
|
|
|
23
23
|
* @param args The arguments passed in
|
|
24
24
|
*/
|
|
25
25
|
static check(assertion: CaptureAssert, positive: boolean, ...args: unknown[]): void {
|
|
26
|
-
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
27
|
-
assertion.file = RuntimeIndex.getSourceFile(assertion.module);
|
|
28
|
-
|
|
29
26
|
let fn = assertion.operator;
|
|
30
27
|
assertion.operator = ASSERT_FN_OPERATOR[fn];
|
|
31
28
|
|
|
@@ -37,6 +34,7 @@ export class AssertCheck {
|
|
|
37
34
|
// Invert check for negative
|
|
38
35
|
const assertFn = positive ? assert : (x: unknown, msg?: string): unknown => assert(!x, msg);
|
|
39
36
|
|
|
37
|
+
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
40
38
|
// Check fn to call
|
|
41
39
|
if (fn === 'fail') {
|
|
42
40
|
if (args.length > 1) {
|
|
@@ -214,8 +212,6 @@ export class AssertCheck {
|
|
|
214
212
|
): void {
|
|
215
213
|
let missed: Error | undefined;
|
|
216
214
|
|
|
217
|
-
assertion.file = RuntimeIndex.getSourceFile(assertion.module);
|
|
218
|
-
|
|
219
215
|
try {
|
|
220
216
|
action();
|
|
221
217
|
if (!positive) {
|
|
@@ -248,8 +244,6 @@ export class AssertCheck {
|
|
|
248
244
|
): Promise<void> {
|
|
249
245
|
let missed: Error | undefined;
|
|
250
246
|
|
|
251
|
-
assertion.file = RuntimeIndex.getSourceFile(assertion.module);
|
|
252
|
-
|
|
253
247
|
try {
|
|
254
248
|
if ('then' in action) {
|
|
255
249
|
await action;
|
|
@@ -273,15 +267,13 @@ export class AssertCheck {
|
|
|
273
267
|
* Look for any unhandled exceptions
|
|
274
268
|
*/
|
|
275
269
|
static checkUnhandled(test: TestConfig, err: Error | assert.AssertionError): void {
|
|
276
|
-
let line = AssertUtil.getPositionOfError(err, test.
|
|
270
|
+
let line = AssertUtil.getPositionOfError(err, test.import).line;
|
|
277
271
|
if (line === 1) {
|
|
278
272
|
line = test.lineStart;
|
|
279
273
|
}
|
|
280
274
|
|
|
281
|
-
const entry = RuntimeIndex.getFromSource(test.file)!;
|
|
282
275
|
AssertCapture.add({
|
|
283
|
-
|
|
284
|
-
file: test.file,
|
|
276
|
+
import: test.import,
|
|
285
277
|
line,
|
|
286
278
|
operator: 'throws',
|
|
287
279
|
error: err,
|
package/src/assert/util.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import util from 'node:util';
|
|
2
2
|
|
|
3
|
-
import { Runtime } from '@travetto/runtime';
|
|
3
|
+
import { Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
4
4
|
|
|
5
5
|
import { TestConfig, Assertion, TestResult } from '../model/test';
|
|
6
6
|
import { SuiteConfig } from '../model/suite';
|
|
@@ -42,7 +42,7 @@ export class AssertUtil {
|
|
|
42
42
|
/**
|
|
43
43
|
* Determine file location for a given error and the stack trace
|
|
44
44
|
*/
|
|
45
|
-
static getPositionOfError(err: Error,
|
|
45
|
+
static getPositionOfError(err: Error, imp: string): { import: string, line: number } {
|
|
46
46
|
const cwd = Runtime.mainSourcePath;
|
|
47
47
|
const lines = (err.stack ?? new Error().stack!)
|
|
48
48
|
.replace(/[\\/]/g, '/')
|
|
@@ -50,6 +50,8 @@ export class AssertUtil {
|
|
|
50
50
|
// Exclude node_modules, target self
|
|
51
51
|
.filter(x => x.includes(cwd) && (!x.includes('node_modules') || x.includes('/support/')));
|
|
52
52
|
|
|
53
|
+
const filename = RuntimeIndex.getFromImport(imp)?.sourceFile!;
|
|
54
|
+
|
|
53
55
|
let best = lines.filter(x => x.includes(filename))[0];
|
|
54
56
|
|
|
55
57
|
if (!best) {
|
|
@@ -57,12 +59,12 @@ export class AssertUtil {
|
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
if (!best) {
|
|
60
|
-
return {
|
|
62
|
+
return { import: imp, line: 1 };
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
const pth = best.trim().split(/\s+/g).slice(1).pop()!;
|
|
64
66
|
if (!pth) {
|
|
65
|
-
return {
|
|
67
|
+
return { import: imp, line: 1 };
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
const [file, lineNo] = pth
|
|
@@ -79,7 +81,7 @@ export class AssertUtil {
|
|
|
79
81
|
|
|
80
82
|
const outFile = outFileParts.length > 1 ? outFileParts[1].replace(/^[\/]/, '') : filename;
|
|
81
83
|
|
|
82
|
-
const res = {
|
|
84
|
+
const res = { import: RuntimeIndex.getFromSource(outFile)?.import!, line };
|
|
83
85
|
|
|
84
86
|
return res;
|
|
85
87
|
}
|
|
@@ -88,7 +90,7 @@ export class AssertUtil {
|
|
|
88
90
|
* Generate a suite error given a suite config, and an error
|
|
89
91
|
*/
|
|
90
92
|
static generateSuiteError(suite: SuiteConfig, methodName: string, error: Error): { assert: Assertion, testResult: TestResult, testConfig: TestConfig } {
|
|
91
|
-
const {
|
|
93
|
+
const { import: imp, ...pos } = this.getPositionOfError(error, suite.import);
|
|
92
94
|
let line = pos.line;
|
|
93
95
|
|
|
94
96
|
if (line === 1 && suite.lineStart) {
|
|
@@ -97,7 +99,7 @@ export class AssertUtil {
|
|
|
97
99
|
|
|
98
100
|
const msg = error.message.split(/\n/)[0];
|
|
99
101
|
|
|
100
|
-
const core = {
|
|
102
|
+
const core = { import: imp, classId: suite.classId, methodName };
|
|
101
103
|
const coreAll = { ...core, description: msg, lineStart: line, lineEnd: line, lineBodyStart: line };
|
|
102
104
|
|
|
103
105
|
const assert: Assertion = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
|
|
3
|
-
import { Class } from '@travetto/runtime';
|
|
3
|
+
import { Class, RuntimeIndex } from '@travetto/runtime';
|
|
4
4
|
|
|
5
5
|
import { TestConsumer } from '../types';
|
|
6
6
|
import { TestEvent } from '../../model/event';
|
|
@@ -28,7 +28,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
|
|
|
28
28
|
*/
|
|
29
29
|
summarizeSuite(test: TestResult): SuiteResult {
|
|
30
30
|
// Was only loading to verify existence (TODO: double-check)
|
|
31
|
-
if (existsSync(test.
|
|
31
|
+
if (existsSync(RuntimeIndex.getFromImport(test.import)!.sourceFile)) {
|
|
32
32
|
this.#state[test.classId] = this.#state[test.classId] ?? {};
|
|
33
33
|
this.#state[test.classId][test.methodName] = test.status;
|
|
34
34
|
const SuiteCls = SuiteRegistry.getClasses().find(x =>
|
|
@@ -50,7 +50,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
|
|
|
50
50
|
removeClass(clsId: string): SuiteResult {
|
|
51
51
|
this.#state[clsId] = {};
|
|
52
52
|
return {
|
|
53
|
-
classId: clsId, passed: 0, failed: 0, skipped: 0, total: 0, tests: [], duration: 0,
|
|
53
|
+
classId: clsId, passed: 0, failed: 0, skipped: 0, total: 0, tests: [], duration: 0, import: '', lineStart: 0, lineEnd: 0
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -70,8 +70,9 @@ export class CumulativeSummaryConsumer implements TestConsumer {
|
|
|
70
70
|
passed: total.passed,
|
|
71
71
|
failed: total.failed,
|
|
72
72
|
skipped: total.skipped,
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
import: suite.import,
|
|
74
|
+
lineStart: suite.lineStart,
|
|
75
|
+
lineEnd: suite.lineEnd,
|
|
75
76
|
total: total.failed + total.passed,
|
|
76
77
|
tests: [],
|
|
77
78
|
duration: 0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Terminal } from '@travetto/terminal';
|
|
2
|
-
import { AppError, TimeUtil, Runtime } from '@travetto/runtime';
|
|
2
|
+
import { AppError, TimeUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
3
3
|
import { stringify } from 'yaml';
|
|
4
4
|
|
|
5
5
|
import { TestEvent } from '../../model/event';
|
|
@@ -66,11 +66,12 @@ export class TapEmitter implements TestConsumer {
|
|
|
66
66
|
let subCount = 0;
|
|
67
67
|
for (const asrt of test.assertions) {
|
|
68
68
|
const text = asrt.message ? `${asrt.text} (${this.#enhancer.failure(asrt.message)})` : asrt.text;
|
|
69
|
+
const pth = RuntimeIndex.getFromImport(asrt.import)!.sourceFile.replace(Runtime.mainSourcePath, '.');
|
|
69
70
|
let subMessage = [
|
|
70
71
|
this.#enhancer.assertNumber(++subCount),
|
|
71
72
|
'-',
|
|
72
73
|
this.#enhancer.assertDescription(text),
|
|
73
|
-
`${this.#enhancer.assertFile(
|
|
74
|
+
`${this.#enhancer.assertFile(pth)}:${this.#enhancer.assertLine(asrt.line)}`
|
|
74
75
|
].join(' ');
|
|
75
76
|
|
|
76
77
|
if (asrt.error) {
|
|
@@ -2,6 +2,8 @@ import { Writable } from 'node:stream';
|
|
|
2
2
|
|
|
3
3
|
import { stringify } from 'yaml';
|
|
4
4
|
|
|
5
|
+
import { RuntimeIndex } from '@travetto/runtime';
|
|
6
|
+
|
|
5
7
|
import { TestEvent } from '../../model/event';
|
|
6
8
|
import { SuitesSummary, TestConsumer } from '../types';
|
|
7
9
|
import { Consumable } from '../registry';
|
|
@@ -85,7 +87,7 @@ export class XunitEmitter implements TestConsumer {
|
|
|
85
87
|
failures="${suite.failed}"
|
|
86
88
|
errors="${suite.failed}"
|
|
87
89
|
skipped="${suite.skipped}"
|
|
88
|
-
file="${suite.
|
|
90
|
+
file="${RuntimeIndex.getFromImport(suite.import)!.sourceFile}"
|
|
89
91
|
>
|
|
90
92
|
${testBodies.join('\n')}
|
|
91
93
|
</testsuite>
|
|
@@ -101,7 +103,7 @@ export class XunitEmitter implements TestConsumer {
|
|
|
101
103
|
this.#stream.write(`
|
|
102
104
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
103
105
|
<testsuites
|
|
104
|
-
name="${summary.suites.length ? summary.suites[0].
|
|
106
|
+
name="${summary.suites.length ? RuntimeIndex.getFromImport(summary.suites[0].import)?.sourceFile : 'nameless'}"
|
|
105
107
|
time="${summary.duration}"
|
|
106
108
|
tests="${summary.total}"
|
|
107
109
|
failures="${summary.failed}"
|
package/src/decorator/test.ts
CHANGED
|
@@ -15,6 +15,7 @@ export function AssertCheck(): MethodDecorator {
|
|
|
15
15
|
* @param description The test description
|
|
16
16
|
* @augments `@travetto/test:Test`
|
|
17
17
|
* @augments `@travetto/test:AssertCheck`
|
|
18
|
+
* @augments `@travetto/runtime:DebugBreak`
|
|
18
19
|
*/
|
|
19
20
|
export function Test(): MethodDecorator;
|
|
20
21
|
export function Test(...rest: Partial<TestConfig>[]): MethodDecorator;
|
package/src/execute/executor.ts
CHANGED
|
@@ -65,11 +65,11 @@ export class TestExecutor {
|
|
|
65
65
|
/**
|
|
66
66
|
* Fail an entire file, marking the whole file as failed
|
|
67
67
|
*/
|
|
68
|
-
static failFile(consumer: TestConsumer,
|
|
69
|
-
const name = path.basename(
|
|
70
|
-
const classId = `${RuntimeIndex.
|
|
68
|
+
static failFile(consumer: TestConsumer, imp: string, err: Error): void {
|
|
69
|
+
const name = path.basename(imp);
|
|
70
|
+
const classId = `${RuntimeIndex.getFromImport(imp)?.id}○${name}`;
|
|
71
71
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
72
|
-
const suite = { class: { name }, classId, duration: 0, lineStart: 1, lineEnd: 1,
|
|
72
|
+
const suite = { class: { name }, classId, duration: 0, lineStart: 1, lineEnd: 1, import: imp, } as SuiteConfig & SuiteResult;
|
|
73
73
|
err.message = err.message.replaceAll(Runtime.mainSourcePath, '.');
|
|
74
74
|
const res = AssertUtil.generateSuiteError(suite, 'require', err);
|
|
75
75
|
consumer.onEvent({ type: 'suite', phase: 'before', suite });
|
|
@@ -90,7 +90,7 @@ export class TestExecutor {
|
|
|
90
90
|
total: 0,
|
|
91
91
|
lineStart: suite.lineStart,
|
|
92
92
|
lineEnd: suite.lineEnd,
|
|
93
|
-
|
|
93
|
+
import: suite.import,
|
|
94
94
|
classId: suite.classId,
|
|
95
95
|
duration: 0,
|
|
96
96
|
tests: []
|
|
@@ -109,13 +109,12 @@ export class TestExecutor {
|
|
|
109
109
|
|
|
110
110
|
const result: TestResult = {
|
|
111
111
|
methodName: test.methodName,
|
|
112
|
-
module: Runtime.main.name,
|
|
113
112
|
description: test.description,
|
|
114
113
|
classId: test.classId,
|
|
115
114
|
lineStart: test.lineStart,
|
|
116
115
|
lineEnd: test.lineEnd,
|
|
117
116
|
lineBodyStart: test.lineBodyStart,
|
|
118
|
-
|
|
117
|
+
import: test.import,
|
|
119
118
|
status: 'skipped',
|
|
120
119
|
assertions: [],
|
|
121
120
|
duration: 0,
|
|
@@ -258,19 +257,15 @@ export class TestExecutor {
|
|
|
258
257
|
/**
|
|
259
258
|
* Handle executing a suite's test/tests based on command line inputs
|
|
260
259
|
*/
|
|
261
|
-
static async execute(consumer: TestConsumer,
|
|
262
|
-
|
|
263
|
-
file = path.resolve(file);
|
|
264
|
-
|
|
265
|
-
const entry = RuntimeIndex.getEntry(file)!;
|
|
260
|
+
static async execute(consumer: TestConsumer, imp: string, ...args: string[]): Promise<void> {
|
|
266
261
|
|
|
267
262
|
try {
|
|
268
|
-
await import(
|
|
263
|
+
await import(imp);
|
|
269
264
|
} catch (err) {
|
|
270
265
|
if (!(err instanceof Error)) {
|
|
271
266
|
throw err;
|
|
272
267
|
}
|
|
273
|
-
this.failFile(consumer,
|
|
268
|
+
this.failFile(consumer, imp, err);
|
|
274
269
|
return;
|
|
275
270
|
}
|
|
276
271
|
|
|
@@ -278,7 +273,7 @@ export class TestExecutor {
|
|
|
278
273
|
await SuiteRegistry.init();
|
|
279
274
|
|
|
280
275
|
// Convert inbound arguments to specific tests to run
|
|
281
|
-
const params = SuiteRegistry.getRunParams(
|
|
276
|
+
const params = SuiteRegistry.getRunParams(imp, ...args);
|
|
282
277
|
|
|
283
278
|
// If running specific suites
|
|
284
279
|
if ('suites' in params) {
|
package/src/execute/runner.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
|
|
3
|
-
import { path as mp } from '@travetto/manifest';
|
|
4
3
|
import { TimeUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
|
|
5
4
|
import { WorkPool } from '@travetto/worker';
|
|
6
5
|
|
|
@@ -22,25 +21,21 @@ export class Runner {
|
|
|
22
21
|
this.#state = state;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
get patterns(): RegExp[] {
|
|
26
|
-
return this.#state.args.map(x => new RegExp(mp.toPosix(x)));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
24
|
/**
|
|
30
25
|
* Run all files
|
|
31
26
|
*/
|
|
32
27
|
async runFiles(): Promise<boolean> {
|
|
33
28
|
const consumer = await RunnableTestConsumer.get(this.#state.consumer ?? this.#state.format);
|
|
34
29
|
|
|
35
|
-
const
|
|
30
|
+
const imports = await RunnerUtil.getTestImports(this.#state.args);
|
|
36
31
|
|
|
37
|
-
console.debug('Running', {
|
|
32
|
+
console.debug('Running', { imports, patterns: this.#state.args });
|
|
38
33
|
|
|
39
34
|
const testCount = await RunnerUtil.getTestCount(this.#state.args);
|
|
40
35
|
await consumer.onStart({ testCount });
|
|
41
36
|
await WorkPool.run(
|
|
42
37
|
buildStandardTestManager.bind(null, consumer),
|
|
43
|
-
|
|
38
|
+
imports,
|
|
44
39
|
{
|
|
45
40
|
idleTimeoutMillis: TimeUtil.asMillis(10, 's'),
|
|
46
41
|
min: 1,
|
|
@@ -54,17 +49,23 @@ export class Runner {
|
|
|
54
49
|
* Run a single file
|
|
55
50
|
*/
|
|
56
51
|
async runSingle(): Promise<boolean> {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
let imp = RuntimeIndex.getFromImport(this.#state.args[0])?.import;
|
|
53
|
+
|
|
54
|
+
if (!imp) {
|
|
55
|
+
imp = RuntimeIndex.getFromSource(path.resolve(this.#state.args[0]))?.import;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const entry = RuntimeIndex.getFromImport(imp!)!;
|
|
59
|
+
if (entry.module !== Runtime.main.name) {
|
|
60
|
+
RuntimeIndex.reinitForModule(entry.module);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const consumer = await RunnableTestConsumer.get(this.#state.consumer ?? this.#state.format);
|
|
63
64
|
|
|
64
|
-
const [
|
|
65
|
+
const [, ...args] = this.#state.args;
|
|
65
66
|
|
|
66
67
|
await consumer.onStart({});
|
|
67
|
-
await TestExecutor.execute(consumer,
|
|
68
|
+
await TestExecutor.execute(consumer, imp!, ...args);
|
|
68
69
|
return consumer.summarizeAsBoolean();
|
|
69
70
|
}
|
|
70
71
|
|
package/src/execute/util.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createReadStream } from 'node:fs';
|
|
3
|
-
import
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import readline from 'node:readline/promises';
|
|
4
5
|
|
|
5
|
-
import { Env, ExecUtil, ShutdownManager, Util, RuntimeIndex } from '@travetto/runtime';
|
|
6
|
-
import type { IndexedFile } from '@travetto/manifest';
|
|
6
|
+
import { Env, ExecUtil, ShutdownManager, Util, RuntimeIndex, Runtime } from '@travetto/runtime';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Simple Test Utilities
|
|
@@ -19,38 +19,45 @@ export class RunnerUtil {
|
|
|
19
19
|
/**
|
|
20
20
|
* Determine if a given file path is a valid test file
|
|
21
21
|
*/
|
|
22
|
-
static isTestFile(file: string): Promise<boolean> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
22
|
+
static async isTestFile(file: string): Promise<boolean> {
|
|
23
|
+
const reader = readline.createInterface({ input: createReadStream(file) });
|
|
24
|
+
const state = { imp: false, suite: false };
|
|
25
|
+
for await (const line of reader) {
|
|
26
|
+
state.imp ||= line.includes('@travetto/test');
|
|
27
|
+
state.suite ||= line.includes('Suite'); // Decorator or name
|
|
28
|
+
if (state.imp && state.suite) {
|
|
29
|
+
reader.close();
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
37
|
* Find all valid test files given the globs
|
|
39
38
|
*/
|
|
40
|
-
static async
|
|
41
|
-
const files =
|
|
39
|
+
static async getTestImports(globs?: string[]): Promise<string[]> {
|
|
40
|
+
const files = new Set<string>();
|
|
41
|
+
// Collect globs
|
|
42
|
+
if (globs) {
|
|
43
|
+
for await (const item of fs.glob(globs)) {
|
|
44
|
+
files.add(Runtime.workspaceRelative(item));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const found = RuntimeIndex.find({
|
|
42
49
|
module: m => m.roles.includes('test') || m.roles.includes('std'),
|
|
43
50
|
folder: f => f === 'test',
|
|
44
51
|
file: f => f.role === 'test'
|
|
45
52
|
})
|
|
46
|
-
.filter(f =>
|
|
53
|
+
.filter(f => files.size === 0 || files.has(f.sourceFile));
|
|
47
54
|
|
|
48
|
-
const
|
|
49
|
-
.map(f => this.isTestFile(f.sourceFile).then(valid => ({
|
|
55
|
+
const validImports = found
|
|
56
|
+
.map(f => this.isTestFile(f.sourceFile).then(valid => ({ import: f.import, valid })));
|
|
50
57
|
|
|
51
|
-
return (await Promise.all(
|
|
58
|
+
return (await Promise.all(validImports))
|
|
52
59
|
.filter(x => x.valid)
|
|
53
|
-
.map(x => x.
|
|
60
|
+
.map(x => x.import);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
/**
|
|
@@ -70,11 +77,4 @@ export class RunnerUtil {
|
|
|
70
77
|
}
|
|
71
78
|
return countRes.valid ? +countRes.stdout : 0;
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Determine if we should invoke the debugger
|
|
76
|
-
*/
|
|
77
|
-
static get tryDebugger(): boolean {
|
|
78
|
-
return Env.TRV_TEST_BREAK_ENTRY.isTrue;
|
|
79
|
-
}
|
|
80
80
|
}
|
package/src/execute/watcher.ts
CHANGED
|
@@ -6,17 +6,19 @@ import { SuiteRegistry } from '../registry/suite';
|
|
|
6
6
|
import { buildStandardTestManager } from '../worker/standard';
|
|
7
7
|
import { TestConsumerRegistry } from '../consumer/registry';
|
|
8
8
|
import { CumulativeSummaryConsumer } from '../consumer/types/cumulative';
|
|
9
|
-
import {
|
|
9
|
+
import { RunRequest } from '../worker/types';
|
|
10
10
|
import { RunnerUtil } from './util';
|
|
11
11
|
import { TestEvent } from '../model/event';
|
|
12
12
|
|
|
13
|
-
function
|
|
13
|
+
function isRunRequest(ev: unknown): ev is RunRequest {
|
|
14
14
|
return typeof ev === 'object' && !!ev && 'type' in ev && typeof ev.type === 'string' && ev.type === 'run-test';
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
type RemoveTestEvent = { type: 'removeTest', method: string, import: string, classId: string };
|
|
18
|
+
|
|
17
19
|
export type TestWatchEvent =
|
|
18
20
|
TestEvent |
|
|
19
|
-
|
|
21
|
+
RemoveTestEvent |
|
|
20
22
|
{ type: 'ready' } |
|
|
21
23
|
{ type: 'log', message: string };
|
|
22
24
|
|
|
@@ -33,7 +35,7 @@ export class TestWatcher {
|
|
|
33
35
|
static async watch(format: string, runAllOnStart = true): Promise<void> {
|
|
34
36
|
console.debug('Listening for changes');
|
|
35
37
|
|
|
36
|
-
const itr = new WorkQueue<string>();
|
|
38
|
+
const itr = new WorkQueue<string | RunRequest>();
|
|
37
39
|
|
|
38
40
|
await SuiteRegistry.init();
|
|
39
41
|
SuiteRegistry.listen(RootRegistry);
|
|
@@ -52,7 +54,7 @@ export class TestWatcher {
|
|
|
52
54
|
const conf = SuiteRegistry.getByClassAndMethod(cls, method)!;
|
|
53
55
|
if (e.type !== 'removing') {
|
|
54
56
|
if (conf) {
|
|
55
|
-
const key =
|
|
57
|
+
const key = { import: conf.import, class: conf.class.name, method: conf.methodName };
|
|
56
58
|
itr.add(key, true); // Shift to front
|
|
57
59
|
}
|
|
58
60
|
} else {
|
|
@@ -60,29 +62,29 @@ export class TestWatcher {
|
|
|
60
62
|
type: 'removeTest',
|
|
61
63
|
method: method?.name,
|
|
62
64
|
classId: cls?.Ⲑid,
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
+
import: Runtime.getImport(cls)
|
|
66
|
+
} satisfies RemoveTestEvent);
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
// If a file is changed, but doesn't emit classes, re-run whole file
|
|
69
|
-
RootRegistry.onNonClassChanges(
|
|
71
|
+
RootRegistry.onNonClassChanges(imp => itr.add(imp));
|
|
70
72
|
|
|
71
73
|
await RootRegistry.init();
|
|
72
74
|
|
|
73
75
|
process.on('message', ev => {
|
|
74
|
-
if (
|
|
76
|
+
if (isRunRequest(ev)) {
|
|
75
77
|
console.debug('Manually triggered', ev);
|
|
76
|
-
itr.add(
|
|
78
|
+
itr.add(ev, true);
|
|
77
79
|
}
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
process.send?.({ type: 'ready' });
|
|
81
83
|
|
|
82
84
|
if (runAllOnStart) {
|
|
83
|
-
for (const
|
|
84
|
-
await import(
|
|
85
|
-
itr.add(
|
|
85
|
+
for (const imp of await RunnerUtil.getTestImports()) {
|
|
86
|
+
await import(imp);
|
|
87
|
+
itr.add(imp);
|
|
86
88
|
}
|
|
87
89
|
}
|
|
88
90
|
|
package/src/model/common.ts
CHANGED
|
@@ -5,10 +5,6 @@ export type Skip = boolean | ((instance: unknown) => boolean | Promise<boolean>)
|
|
|
5
5
|
* Core Suite definition
|
|
6
6
|
*/
|
|
7
7
|
export interface SuiteCore {
|
|
8
|
-
/**
|
|
9
|
-
* The module the test is declared in
|
|
10
|
-
*/
|
|
11
|
-
module: string;
|
|
12
8
|
/**
|
|
13
9
|
* The class id
|
|
14
10
|
*/
|
|
@@ -18,9 +14,9 @@ export interface SuiteCore {
|
|
|
18
14
|
*/
|
|
19
15
|
description: string;
|
|
20
16
|
/**
|
|
21
|
-
*
|
|
17
|
+
* The import location for the suite
|
|
22
18
|
*/
|
|
23
|
-
|
|
19
|
+
import: string;
|
|
24
20
|
/**
|
|
25
21
|
* The first line of the unit
|
|
26
22
|
*/
|
package/src/model/suite.ts
CHANGED
package/src/model/test.ts
CHANGED
package/src/registry/suite.ts
CHANGED
|
@@ -20,9 +20,8 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
20
20
|
const lines = describeFunction(cls)?.lines;
|
|
21
21
|
return {
|
|
22
22
|
class: cls,
|
|
23
|
-
module: Runtime.main.name,
|
|
24
23
|
classId: cls.Ⲑid,
|
|
25
|
-
|
|
24
|
+
import: Runtime.getImport(cls),
|
|
26
25
|
lineStart: lines?.[0],
|
|
27
26
|
lineEnd: lines?.[1],
|
|
28
27
|
tests: [],
|
|
@@ -37,10 +36,10 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
37
36
|
const lines = describeFunction(cls)?.methods?.[fn.name].lines;
|
|
38
37
|
return {
|
|
39
38
|
class: cls,
|
|
40
|
-
|
|
41
|
-
file: Runtime.getSource(cls),
|
|
39
|
+
import: Runtime.getImport(cls),
|
|
42
40
|
lineStart: lines?.[0],
|
|
43
41
|
lineEnd: lines?.[1],
|
|
42
|
+
lineBodyStart: lines?.[2],
|
|
44
43
|
methodName: fn.name
|
|
45
44
|
};
|
|
46
45
|
}
|
|
@@ -93,11 +92,11 @@ class $SuiteRegistry extends MetadataRegistry<SuiteConfig, TestConfig> {
|
|
|
93
92
|
/**
|
|
94
93
|
* Get run parameters from provided input
|
|
95
94
|
*/
|
|
96
|
-
getRunParams(
|
|
95
|
+
getRunParams(imp: string, clsName?: string, method?: string): { suites: SuiteConfig[] } | { suite: SuiteConfig, test?: TestConfig } {
|
|
97
96
|
if (clsName && /^\d+$/.test(clsName)) { // If we only have a line number
|
|
98
97
|
const line = parseInt(clsName, 10);
|
|
99
98
|
const suites = this.getValidClasses()
|
|
100
|
-
.filter(cls => Runtime.
|
|
99
|
+
.filter(cls => Runtime.getImport(cls) === imp)
|
|
101
100
|
.map(x => this.get(x)).filter(x => !x.skip);
|
|
102
101
|
const suite = suites.find(x => line >= x.lineStart && line <= x.lineEnd);
|
|
103
102
|
|
package/src/trv.d.ts
CHANGED
package/src/worker/child.ts
CHANGED
|
@@ -79,13 +79,13 @@ export class TestChildWorker extends ChildCommChannel<RunEvent> {
|
|
|
79
79
|
async onRunCommand(event: RunEvent): Promise<void> {
|
|
80
80
|
console.debug('Run');
|
|
81
81
|
|
|
82
|
-
console.debug('Running', {
|
|
82
|
+
console.debug('Running', { import: event.import });
|
|
83
83
|
|
|
84
84
|
try {
|
|
85
85
|
await new Runner({
|
|
86
86
|
format: 'exec',
|
|
87
87
|
mode: 'single',
|
|
88
|
-
args: [event.
|
|
88
|
+
args: [event.import, event.class!, event.method!],
|
|
89
89
|
concurrency: 1
|
|
90
90
|
}).run();
|
|
91
91
|
} finally {
|
package/src/worker/standard.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { fork } from 'node:child_process';
|
|
|
3
3
|
import { Env, RuntimeIndex } from '@travetto/runtime';
|
|
4
4
|
import { ParentCommChannel } from '@travetto/worker';
|
|
5
5
|
|
|
6
|
-
import { Events, RunEvent } from './types';
|
|
6
|
+
import { Events, RunEvent, RunRequest } from './types';
|
|
7
7
|
import { TestConsumer } from '../consumer/types';
|
|
8
8
|
import { ErrorUtil } from '../consumer/error';
|
|
9
9
|
import { TestEvent } from '../model/event';
|
|
@@ -11,28 +11,29 @@ import { TestEvent } from '../model/event';
|
|
|
11
11
|
/**
|
|
12
12
|
* Produce a handler for the child worker
|
|
13
13
|
*/
|
|
14
|
-
export async function buildStandardTestManager(consumer: TestConsumer,
|
|
15
|
-
process.send?.({ type: 'log', message: `Worker Executing ${
|
|
14
|
+
export async function buildStandardTestManager(consumer: TestConsumer, imp: string | RunRequest): Promise<void> {
|
|
15
|
+
process.send?.({ type: 'log', message: `Worker Executing ${imp}` });
|
|
16
16
|
|
|
17
17
|
let event: RunEvent;
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (typeof imp === 'string') {
|
|
19
|
+
event = { import: imp };
|
|
20
|
+
} else if ('file' in imp) {
|
|
21
|
+
event = { import: RuntimeIndex.getFromSource(imp.file)?.sourceFile!, class: imp.class, method: imp.method };
|
|
21
22
|
} else {
|
|
22
|
-
event =
|
|
23
|
+
event = imp;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
const { module } = RuntimeIndex.
|
|
26
|
-
const
|
|
26
|
+
const { module } = RuntimeIndex.getFromImport(event.import!)!;
|
|
27
|
+
const suiteMod = RuntimeIndex.getModule(module);
|
|
27
28
|
|
|
28
29
|
const channel = new ParentCommChannel<TestEvent & { error?: Error }>(
|
|
29
30
|
fork(
|
|
30
31
|
RuntimeIndex.resolveFileImport('@travetto/cli/support/entry.trv'), ['test:child'],
|
|
31
32
|
{
|
|
32
|
-
cwd,
|
|
33
|
+
cwd: suiteMod!.sourcePath,
|
|
33
34
|
env: {
|
|
34
35
|
...process.env,
|
|
35
|
-
...Env.TRV_MANIFEST.export(
|
|
36
|
+
...Env.TRV_MANIFEST.export(suiteMod!.outputPath),
|
|
36
37
|
...Env.TRV_QUIET.export(true)
|
|
37
38
|
},
|
|
38
39
|
stdio: ['ignore', 'ignore', 2, 'ipc']
|
|
@@ -63,7 +64,7 @@ export async function buildStandardTestManager(consumer: TestConsumer, file: str
|
|
|
63
64
|
// Kill on complete
|
|
64
65
|
await channel.destroy();
|
|
65
66
|
|
|
66
|
-
process.send?.({ type: 'log', message: `Worker Finished ${
|
|
67
|
+
process.send?.({ type: 'log', message: `Worker Finished ${imp}` });
|
|
67
68
|
|
|
68
69
|
// If we received an error, throw it
|
|
69
70
|
if (error) {
|
package/src/worker/types.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Run Request
|
|
3
|
+
*/
|
|
4
|
+
export type RunRequest = {
|
|
5
|
+
file: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
method?: string;
|
|
8
|
+
} | {
|
|
9
|
+
import: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
method?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
1
14
|
/**
|
|
2
15
|
* Test Run Event
|
|
3
16
|
*/
|
|
4
17
|
export type RunEvent = {
|
|
5
|
-
|
|
18
|
+
import: string;
|
|
6
19
|
error?: unknown;
|
|
7
20
|
class?: string;
|
|
8
21
|
method?: string;
|
|
@@ -13,15 +13,14 @@ export class TestCountCommand {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
async main(patterns: string[]) {
|
|
16
|
-
const
|
|
17
|
-
const files = await RunnerUtil.getTestFiles(regexes);
|
|
16
|
+
const imports = await RunnerUtil.getTestImports(patterns);
|
|
18
17
|
|
|
19
18
|
// Load all tests
|
|
20
|
-
for (const
|
|
19
|
+
for (const imp of imports) {
|
|
21
20
|
try {
|
|
22
|
-
await import(
|
|
21
|
+
await import(imp);
|
|
23
22
|
} catch (err) {
|
|
24
|
-
console.error('Failed to import',
|
|
23
|
+
console.error('Failed to import', imp, err);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import ts from 'typescript';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
TransformerState, DecoratorMeta, OnMethod, OnClass, CoreUtil, DecoratorUtil
|
|
5
|
-
} from '@travetto/transformer';
|
|
6
|
-
|
|
7
|
-
const RUN_UTIL = 'RunnerUtil';
|
|
8
|
-
|
|
9
|
-
const RunUtilⲐ = Symbol.for('@travetto/test:runner');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Annotate transformation state
|
|
13
|
-
*/
|
|
14
|
-
interface AnnotateState {
|
|
15
|
-
[RunUtilⲐ]?: ts.Expression;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Annotate tests and suites for better diagnostics
|
|
20
|
-
*/
|
|
21
|
-
export class AnnotationTransformer {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Initialize transformer state
|
|
26
|
-
*/
|
|
27
|
-
static initState(state: TransformerState & AnnotateState): void {
|
|
28
|
-
if (!state[RunUtilⲐ]) {
|
|
29
|
-
const runUtil = state.importFile('@travetto/test/src/execute/util').ident;
|
|
30
|
-
state[RunUtilⲐ] = CoreUtil.createAccess(state.factory, runUtil, RUN_UTIL, 'tryDebugger');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Build source annotation, indicating line ranges
|
|
36
|
-
* @param state
|
|
37
|
-
* @param node
|
|
38
|
-
* @param dec
|
|
39
|
-
*/
|
|
40
|
-
static buildAnnotation(state: TransformerState & AnnotateState, node: ts.Node, dec: ts.Decorator, expression: ts.CallExpression): ts.Decorator {
|
|
41
|
-
const ogN = (CoreUtil.hasOriginal(node) ? node.original : node);
|
|
42
|
-
const n = ts.isMethodDeclaration(ogN) ? ogN : undefined;
|
|
43
|
-
|
|
44
|
-
const newDec = state.factory.updateDecorator(
|
|
45
|
-
dec,
|
|
46
|
-
state.factory.createCallExpression(
|
|
47
|
-
expression.expression,
|
|
48
|
-
expression.typeArguments,
|
|
49
|
-
[
|
|
50
|
-
...(expression.arguments ?? []),
|
|
51
|
-
state.fromLiteral({
|
|
52
|
-
ident: `@${DecoratorUtil.getDecoratorIdent(dec).text}()`,
|
|
53
|
-
lineBodyStart: CoreUtil.getRangeOf(state.source, n?.body?.statements[0])?.[0]
|
|
54
|
-
})
|
|
55
|
-
]
|
|
56
|
-
)
|
|
57
|
-
);
|
|
58
|
-
return newDec;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
@OnClass('Suite')
|
|
62
|
-
static annotateSuiteDetails(state: TransformerState & AnnotateState, node: ts.ClassDeclaration, dm?: DecoratorMeta): ts.ClassDeclaration {
|
|
63
|
-
const dec = dm?.dec;
|
|
64
|
-
|
|
65
|
-
if (dec && ts.isCallExpression(dec.expression)) {
|
|
66
|
-
const newDec = this.buildAnnotation(state, node, dec, dec.expression);
|
|
67
|
-
return state.factory.updateClassDeclaration(node,
|
|
68
|
-
DecoratorUtil.spliceDecorators(node, dec, [newDec]),
|
|
69
|
-
node.name,
|
|
70
|
-
node.typeParameters,
|
|
71
|
-
node.heritageClauses,
|
|
72
|
-
node.members
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
return node;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
@OnMethod('Test')
|
|
79
|
-
static annotateTestDetails(state: TransformerState & AnnotateState, node: ts.MethodDeclaration, dm?: DecoratorMeta): ts.MethodDeclaration {
|
|
80
|
-
this.initState(state);
|
|
81
|
-
|
|
82
|
-
const dec = dm?.dec;
|
|
83
|
-
|
|
84
|
-
if (dec && ts.isCallExpression(dec.expression)) {
|
|
85
|
-
const newDec = this.buildAnnotation(state, node, dec, dec.expression);
|
|
86
|
-
return state.factory.updateMethodDeclaration(node,
|
|
87
|
-
DecoratorUtil.spliceDecorators(node, dec, [newDec]),
|
|
88
|
-
node.asteriskToken,
|
|
89
|
-
node.name,
|
|
90
|
-
node.questionToken,
|
|
91
|
-
node.typeParameters,
|
|
92
|
-
node.parameters,
|
|
93
|
-
node.type,
|
|
94
|
-
node.body ? state.factory.updateBlock(node.body, [
|
|
95
|
-
state.factory.createIfStatement(state[RunUtilⲐ]!,
|
|
96
|
-
state.factory.createExpressionStatement(state.factory.createIdentifier('debugger'))),
|
|
97
|
-
...node.body.statements
|
|
98
|
-
]) : node.body
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return node;
|
|
102
|
-
}
|
|
103
|
-
}
|