@travetto/test 7.0.0-rc.1 → 7.0.0-rc.3

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.
Files changed (46) hide show
  1. package/README.md +7 -8
  2. package/__index__.ts +1 -0
  3. package/package.json +7 -7
  4. package/src/assert/check.ts +46 -46
  5. package/src/assert/util.ts +31 -31
  6. package/src/communication.ts +66 -0
  7. package/src/consumer/registry-index.ts +11 -11
  8. package/src/consumer/types/cumulative.ts +91 -62
  9. package/src/consumer/types/delegating.ts +30 -27
  10. package/src/consumer/types/event.ts +11 -4
  11. package/src/consumer/types/exec.ts +12 -3
  12. package/src/consumer/types/runnable.ts +4 -3
  13. package/src/consumer/types/summarizer.ts +12 -10
  14. package/src/consumer/types/tap-summary.ts +22 -20
  15. package/src/consumer/types/tap.ts +15 -15
  16. package/src/consumer/types/xunit.ts +15 -15
  17. package/src/consumer/types.ts +6 -2
  18. package/src/decorator/suite.ts +2 -2
  19. package/src/decorator/test.ts +6 -4
  20. package/src/execute/barrier.ts +8 -8
  21. package/src/execute/console.ts +1 -1
  22. package/src/execute/executor.ts +32 -21
  23. package/src/execute/phase.ts +7 -7
  24. package/src/execute/run.ts +247 -0
  25. package/src/execute/types.ts +2 -17
  26. package/src/execute/watcher.ts +33 -60
  27. package/src/fixture.ts +2 -2
  28. package/src/model/common.ts +4 -0
  29. package/src/model/event.ts +3 -1
  30. package/src/model/suite.ts +10 -21
  31. package/src/model/test.ts +48 -2
  32. package/src/model/util.ts +8 -0
  33. package/src/registry/registry-adapter.ts +23 -21
  34. package/src/registry/registry-index.ts +25 -25
  35. package/src/worker/child.ts +21 -21
  36. package/src/worker/standard.ts +28 -19
  37. package/src/worker/types.ts +9 -5
  38. package/support/bin/run.ts +10 -10
  39. package/support/cli.test.ts +20 -41
  40. package/support/cli.test_diff.ts +47 -0
  41. package/support/cli.test_digest.ts +7 -7
  42. package/support/cli.test_direct.ts +13 -12
  43. package/support/cli.test_watch.ts +3 -8
  44. package/support/transformer.assert.ts +12 -12
  45. package/src/execute/runner.ts +0 -87
  46. package/src/execute/util.ts +0 -108
@@ -1,14 +1,14 @@
1
+ import { ManifestModuleUtil } from '@travetto/manifest';
1
2
  import { Registry } from '@travetto/registry';
2
3
  import { WorkPool } from '@travetto/worker';
3
- import { AsyncQueue, Runtime, RuntimeIndex, castTo, describeFunction } from '@travetto/runtime';
4
+ import { AsyncQueue, RuntimeIndex, TimeUtil, watchCompiler } from '@travetto/runtime';
4
5
 
5
6
  import { buildStandardTestManager } from '../worker/standard.ts';
6
7
  import { TestConsumerRegistryIndex } from '../consumer/registry-index.ts';
7
8
  import { CumulativeSummaryConsumer } from '../consumer/types/cumulative.ts';
8
- import { TestRun } from '../model/test.ts';
9
- import { RunnerUtil } from './util.ts';
10
- import { TestReadyEvent, TestRemovedEvent } from '../worker/types.ts';
11
- import { SuiteRegistryIndex } from '../registry/registry-index.ts';
9
+ import type { TestDiffInput, TestRun } from '../model/test.ts';
10
+ import { RunUtil } from './run.ts';
11
+ import { isTestRunEvent, type TestReadyEvent } from '../worker/types.ts';
12
12
 
13
13
  /**
14
14
  * Test Watcher.
@@ -25,78 +25,51 @@ export class TestWatcher {
25
25
 
26
26
  await Registry.init();
27
27
 
28
- const events: TestRun[] = [];
28
+ const events: (TestRun | TestDiffInput)[] = [];
29
29
 
30
30
  if (runAllOnStart) {
31
- const tests = await RunnerUtil.getTestDigest();
32
- events.push(...RunnerUtil.getTestRuns(tests));
31
+ events.push(...await RunUtil.resolveGlobInput({ globs: [] }));
33
32
  }
34
33
 
35
- const itr = new AsyncQueue(events);
34
+ const queue = new AsyncQueue(events);
36
35
  const consumer = new CumulativeSummaryConsumer(
37
36
  await TestConsumerRegistryIndex.getInstance({ consumer: format })
38
- )
39
- .withFilter(x => x.metadata?.partial !== true || x.type !== 'suite');
40
-
41
- Registry.onMethodChange((event) => {
42
- const [cls, method] = ('prev' in event && event.prev ? event.prev : null) ??
43
- ('curr' in event && event.curr ? event.curr : []);
44
-
45
- if (!cls || describeFunction(cls).abstract) {
46
- return;
47
- }
48
-
49
- const classId = cls.Ⲑid;
50
- if (!method) {
51
- consumer.removeClass(classId);
52
- return;
53
- }
54
-
55
- const conf = SuiteRegistryIndex.getTestConfig(cls, method)!;
56
- if (event.type !== 'removing') {
57
- if (conf) {
58
- const run: TestRun = {
59
- import: conf.import, classId: conf.classId, methodNames: [conf.methodName], metadata: { partial: true }
60
- };
61
- console.log('Triggering', run);
62
- itr.add(run, true); // Shift to front
63
- }
64
- } else {
65
- process.send?.({
66
- type: 'removeTest',
67
- methodNames: method?.name ? [method.name!] : undefined!,
68
- method: method?.name,
69
- classId,
70
- import: Runtime.getImport(cls)
71
- } satisfies TestRemovedEvent);
72
- }
73
- }, SuiteRegistryIndex);
74
-
75
- // If a file is changed, but doesn't emit classes, re-run whole file
76
- Registry.onNonClassChanges(imp => itr.add({ import: imp }));
37
+ );
77
38
 
78
- process.on('message', ev => {
79
- if (typeof ev === 'object' && ev && 'type' in ev && ev.type === 'run-test') {
80
- console.log('Received message', ev);
81
- // Legacy
82
- if ('file' in ev && typeof ev.file === 'string') {
83
- ev = { import: RuntimeIndex.getFromSource(ev.file)?.import! };
84
- }
85
- console.debug('Manually triggered', ev);
86
- itr.add(castTo(ev), true);
39
+ process.on('message', event => {
40
+ if (isTestRunEvent(event)) {
41
+ queue.add(event, true);
87
42
  }
88
43
  });
89
44
 
90
45
  process.send?.({ type: 'ready' } satisfies TestReadyEvent);
91
46
 
92
- await WorkPool.run(
47
+ const queueProcessor = WorkPool.run(
93
48
  buildStandardTestManager.bind(null, consumer),
94
- itr,
49
+ queue,
95
50
  {
96
- idleTimeoutMillis: 120000,
51
+ idleTimeoutMillis: TimeUtil.asMillis('2m'),
97
52
  min: 2,
98
53
  max: WorkPool.DEFAULT_SIZE
99
54
  }
100
55
  );
56
+
57
+ for await (const event of watchCompiler()) {
58
+ const fileType = ManifestModuleUtil.getFileType(event.file);
59
+ if (
60
+ (fileType === 'ts' || fileType === 'js') &&
61
+ RuntimeIndex.findModuleForArbitraryFile(event.file) !== undefined
62
+ ) {
63
+ if (event.action === 'delete') {
64
+ consumer.removeTest(event.import);
65
+ } else {
66
+ const diffSource = consumer.produceDiffSource(event.import);
67
+ queue.add({ import: event.import, diffSource }, true);
68
+ }
69
+ }
70
+ }
71
+
72
+ // Cleanup
73
+ await queueProcessor;
101
74
  }
102
75
  }
package/src/fixture.ts CHANGED
@@ -5,8 +5,8 @@ export class TestFixtures extends FileLoader {
5
5
  super([
6
6
  '@#test/fixtures',
7
7
  '@#support/fixtures',
8
- ...modules.flat().map(x => `${x}#support/fixtures`),
8
+ ...modules.flat().map(mod => `${mod}#support/fixtures`),
9
9
  '@@#support/fixtures'
10
- ].map(v => Runtime.modulePath(v)));
10
+ ].map(value => Runtime.modulePath(value)));
11
11
  }
12
12
  }
@@ -29,6 +29,10 @@ export interface SuiteCore {
29
29
  * Description
30
30
  */
31
31
  description?: string;
32
+ /**
33
+ * Hash of the suite/test source code
34
+ */
35
+ sourceHash?: number;
32
36
  }
33
37
 
34
38
  /**
@@ -14,6 +14,8 @@ export type EventPhase = 'before' | 'after';
14
14
  type EventTpl<T extends EventEntity, P extends EventPhase, V extends {}> =
15
15
  { type: T, phase: P, metadata?: Record<string, unknown> } & V;
16
16
 
17
+ export type TestRemoveEvent = { type: 'removeTest', import: string, classId?: string, methodName?: string, metadata?: Record<string, unknown> };
18
+
17
19
  /**
18
20
  * Different test event shapes
19
21
  */
@@ -22,4 +24,4 @@ export type TestEvent =
22
24
  EventTpl<'test', 'before', { test: TestConfig }> |
23
25
  EventTpl<'test', 'after', { test: TestResult }> |
24
26
  EventTpl<'suite', 'before', { suite: SuiteConfig }> |
25
- EventTpl<'suite', 'after', { suite: SuiteResult }>;
27
+ EventTpl<'suite', 'after', { suite: SuiteResult }>;
@@ -1,6 +1,6 @@
1
1
  import type { Class } from '@travetto/runtime';
2
2
 
3
- import { Assertion, TestConfig, TestResult } from './test.ts';
3
+ import { Assertion, TestConfig, TestResult, type TestStatus } from './test.ts';
4
4
  import { Skip, SuiteCore } from './common.ts';
5
5
 
6
6
  /**
@@ -22,7 +22,7 @@ export interface SuiteConfig extends SuiteCore {
22
22
  /**
23
23
  * Tests to run
24
24
  */
25
- tests: Record<string | symbol, TestConfig>;
25
+ tests: Record<string, TestConfig>;
26
26
  /**
27
27
  * Before all handlers
28
28
  */
@@ -48,37 +48,26 @@ export interface Counts {
48
48
  passed: number;
49
49
  skipped: number;
50
50
  failed: number;
51
+ unknown: number;
51
52
  total: number;
52
53
  }
53
54
 
54
55
  /**
55
56
  * Results of a suite run
56
57
  */
57
- export interface SuiteResult extends Counts {
58
+ export interface SuiteResult extends Counts, SuiteCore {
58
59
  /**
59
- * Class identifier
60
+ * All test results
60
61
  */
61
- classId: string;
62
- /**
63
- * Import for the suite
64
- */
65
- import: string;
66
- /**
67
- * Start of the suite
68
- */
69
- lineStart: number;
70
- /**
71
- * End of the suite
72
- */
73
- lineEnd: number;
74
- /**
75
- * ALl test results
76
- */
77
- tests: TestResult[];
62
+ tests: Record<string, TestResult>;
78
63
  /**
79
64
  * Suite duration
80
65
  */
81
66
  duration: number;
67
+ /**
68
+ * Overall status
69
+ */
70
+ status: TestStatus;
82
71
  }
83
72
 
84
73
  /**
package/src/model/test.ts CHANGED
@@ -2,9 +2,16 @@ import type { Class, ConsoleEvent, TimeSpan } from '@travetto/runtime';
2
2
 
3
3
  import { Skip, TestCore } from './common.ts';
4
4
 
5
- export type ThrowableError = string | RegExp | Class<Error> | ((e: Error | string) => boolean | void | undefined);
5
+ export type ThrowableError = string | RegExp | Class<Error> | ((error: Error | string) => boolean | void | undefined);
6
6
  export type TestLog = Omit<ConsoleEvent, 'args' | 'scope'> & { message: string };
7
7
 
8
+ export type TestDiffSource = Record<string, {
9
+ sourceHash: number;
10
+ methods: Record<string, number>;
11
+ }>;
12
+
13
+ export type TestStatus = 'passed' | 'skipped' | 'failed' | 'unknown';
14
+
8
15
  /**
9
16
  * Specific configuration for a test
10
17
  */
@@ -88,7 +95,7 @@ export interface TestResult extends TestCore {
88
95
  /**
89
96
  * status
90
97
  */
91
- status: 'passed' | 'skipped' | 'failed';
98
+ status: TestStatus;
92
99
  /**
93
100
  * Error if failed
94
101
  */
@@ -136,3 +143,42 @@ export type TestRun = {
136
143
  */
137
144
  runId?: string;
138
145
  };
146
+
147
+ /**
148
+ * Test Diff Input
149
+ */
150
+ export type TestDiffInput = {
151
+ /**
152
+ * Import for run
153
+ */
154
+ import: string;
155
+ /**
156
+ * Diff Source
157
+ */
158
+ diffSource: TestDiffSource;
159
+ /**
160
+ * Test run metadata
161
+ */
162
+ metadata?: Record<string, unknown>;
163
+ };
164
+
165
+ /**
166
+ * Test Glob Input
167
+ */
168
+ export type TestGlobInput = {
169
+ /**
170
+ * Globs to run
171
+ */
172
+ globs: string[];
173
+ /**
174
+ * Tags to filter by
175
+ */
176
+ tags?: string[];
177
+ /**
178
+ * Test run metadata
179
+ */
180
+ metadata?: Record<string, unknown>;
181
+ };
182
+
183
+
184
+ export type TestRunInput = TestRun | TestDiffInput | TestGlobInput;
@@ -0,0 +1,8 @@
1
+ import type { Counts } from './suite';
2
+ import type { TestStatus } from './test';
3
+
4
+ export class TestModelUtil {
5
+ static countsToTestStatus(counts: Counts): TestStatus {
6
+ return counts.failed ? 'failed' : (counts.passed ? 'passed' : counts.skipped ? 'skipped' : 'unknown');
7
+ }
8
+ }
@@ -6,24 +6,24 @@ import { SuiteConfig } from '../model/suite';
6
6
  import { TestConfig } from '../model/test';
7
7
 
8
8
  function combineClasses(baseConfig: SuiteConfig, ...subConfig: Partial<SuiteConfig>[]): SuiteConfig {
9
- for (const cfg of subConfig) {
10
- if (cfg.beforeAll) {
11
- baseConfig.beforeAll = [...baseConfig.beforeAll, ...cfg.beforeAll];
9
+ for (const config of subConfig) {
10
+ if (config.beforeAll) {
11
+ baseConfig.beforeAll = [...baseConfig.beforeAll, ...config.beforeAll];
12
12
  }
13
- if (cfg.beforeEach) {
14
- baseConfig.beforeEach = [...baseConfig.beforeEach, ...cfg.beforeEach];
13
+ if (config.beforeEach) {
14
+ baseConfig.beforeEach = [...baseConfig.beforeEach, ...config.beforeEach];
15
15
  }
16
- if (cfg.afterAll) {
17
- baseConfig.afterAll = [...baseConfig.afterAll, ...cfg.afterAll];
16
+ if (config.afterAll) {
17
+ baseConfig.afterAll = [...baseConfig.afterAll, ...config.afterAll];
18
18
  }
19
- if (cfg.afterEach) {
20
- baseConfig.afterEach = [...baseConfig.afterEach, ...cfg.afterEach];
19
+ if (config.afterEach) {
20
+ baseConfig.afterEach = [...baseConfig.afterEach, ...config.afterEach];
21
21
  }
22
- if (cfg.tags) {
23
- baseConfig.tags = [...baseConfig.tags ?? [], ...cfg.tags];
22
+ if (config.tags) {
23
+ baseConfig.tags = [...baseConfig.tags ?? [], ...config.tags];
24
24
  }
25
- if (cfg.tests) {
26
- for (const [key, test] of Object.entries(cfg.tests ?? {})) {
25
+ if (config.tests) {
26
+ for (const [key, test] of Object.entries(config.tests ?? {})) {
27
27
  baseConfig.tests[key] = {
28
28
  ...test,
29
29
  sourceImport: Runtime.getImport(baseConfig.class),
@@ -40,11 +40,11 @@ function combineClasses(baseConfig: SuiteConfig, ...subConfig: Partial<SuiteConf
40
40
  function combineMethods(suite: SuiteConfig, baseConfig: TestConfig, ...subConfig: Partial<TestConfig>[]): TestConfig {
41
41
  baseConfig.classId = suite.classId;
42
42
  baseConfig.import = suite.import;
43
- for (const cfg of subConfig) {
44
- safeAssign(baseConfig, cfg, {
43
+ for (const config of subConfig) {
44
+ safeAssign(baseConfig, config, {
45
45
  tags: [
46
46
  ...baseConfig.tags ?? [],
47
- ...cfg.tags ?? []
47
+ ...config.tags ?? []
48
48
  ]
49
49
  });
50
50
  }
@@ -61,7 +61,7 @@ export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> {
61
61
 
62
62
  register(...data: Partial<SuiteConfig>[]): SuiteConfig {
63
63
  if (!this.#config) {
64
- const lines = describeFunction(this.#cls)?.lines;
64
+ const { lines, hash } = describeFunction(this.#cls) ?? {};
65
65
  this.#config = asFull<SuiteConfig>({
66
66
  class: this.#cls,
67
67
  classId: this.#cls.Ⲑid,
@@ -69,6 +69,7 @@ export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> {
69
69
  import: Runtime.getImport(this.#cls),
70
70
  lineStart: lines?.[0],
71
71
  lineEnd: lines?.[1],
72
+ sourceHash: hash,
72
73
  tests: {},
73
74
  beforeAll: [],
74
75
  beforeEach: [],
@@ -80,11 +81,11 @@ export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> {
80
81
  return this.#config;
81
82
  }
82
83
 
83
- registerTest(method: string | symbol, ...data: Partial<TestConfig>[]): TestConfig {
84
+ registerTest(method: string, ...data: Partial<TestConfig>[]): TestConfig {
84
85
  const suite = this.register();
85
86
 
86
87
  if (!(method in this.#config.tests)) {
87
- const lines = describeFunction(this.#cls)?.methods?.[method]?.lines;
88
+ const { lines, hash } = describeFunction(this.#cls)?.methods?.[method] ?? {};
88
89
  const config = asFull<TestConfig>({
89
90
  class: this.#cls,
90
91
  tags: [],
@@ -92,7 +93,8 @@ export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> {
92
93
  lineStart: lines?.[0],
93
94
  lineEnd: lines?.[1],
94
95
  lineBodyStart: lines?.[2],
95
- methodName: method.toString(),
96
+ methodName: method,
97
+ sourceHash: hash,
96
98
  });
97
99
  this.#config.tests[method] = config;
98
100
  }
@@ -117,7 +119,7 @@ export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> {
117
119
  return this.#config;
118
120
  }
119
121
 
120
- getMethod(method: string | symbol): TestConfig {
122
+ getMethod(method: string): TestConfig {
121
123
  const test = this.#config.tests[method];
122
124
  if (!test) {
123
125
  throw new AppError(`Test not registered: ${String(method)} on ${this.#cls.name}`);
@@ -1,12 +1,12 @@
1
1
  import { AppError, Class, Runtime, describeFunction } from '@travetto/runtime';
2
- import { ChangeEvent, RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
2
+ import { RegistryIndex, RegistryIndexStore, Registry } from '@travetto/registry';
3
3
 
4
4
  import { SuiteConfig } from '../model/suite.ts';
5
5
  import { TestConfig, TestRun } from '../model/test.ts';
6
6
  import { SuiteRegistryAdapter } from './registry-adapter.ts';
7
7
 
8
- const sortedTests = (cfg: SuiteConfig): TestConfig[] =>
9
- Object.values(cfg.tests).toSorted((a, b) => a.lineStart - b.lineStart);
8
+ const sortedTests = (config: SuiteConfig): TestConfig[] =>
9
+ Object.values(config.tests).toSorted((a, b) => a.lineStart - b.lineStart);
10
10
 
11
11
  type SuiteTests = { suite: SuiteConfig, tests: TestConfig[] };
12
12
 
@@ -21,11 +21,7 @@ export class SuiteRegistryIndex implements RegistryIndex {
21
21
  return this.#instance.store.getForRegister(cls);
22
22
  }
23
23
 
24
- static has(cls: Class): boolean {
25
- return this.#instance.store.has(cls);
26
- }
27
-
28
- static getTestConfig(cls: Class, method: Function): TestConfig | undefined {
24
+ static getTestConfig(cls: Class, method: Function | string): TestConfig | undefined {
29
25
  return this.#instance.getTestConfig(cls, method);
30
26
  }
31
27
 
@@ -41,17 +37,19 @@ export class SuiteRegistryIndex implements RegistryIndex {
41
37
  return this.#instance.store.getClasses();
42
38
  }
43
39
 
40
+ static hasConfig(cls: Class): boolean {
41
+ return this.#instance.store.has(cls);
42
+ }
43
+
44
44
  store = new RegistryIndexStore(SuiteRegistryAdapter);
45
45
 
46
- process(_events: ChangeEvent<Class>[]): void {
47
- // No-op for now
48
- }
46
+ /** @private */ constructor(source: unknown) { Registry.validateConstructor(source); }
49
47
 
50
48
  /**
51
49
  * Find all valid tests (ignoring abstract)
52
50
  */
53
51
  getValidClasses(): Class[] {
54
- return this.store.getClasses().filter(c => !describeFunction(c).abstract);
52
+ return this.store.getClasses().filter(cls => !describeFunction(cls).abstract);
55
53
  }
56
54
 
57
55
  getConfig(cls: Class): SuiteConfig {
@@ -70,27 +68,28 @@ export class SuiteRegistryIndex implements RegistryIndex {
70
68
  const line = parseInt(clsId, 10);
71
69
  const suites = this.getValidClasses()
72
70
  .filter(cls => Runtime.getImport(cls) === imp)
73
- .map(x => this.getConfig(x)).filter(x => !x.skip);
74
- const suite = suites.find(x => line >= x.lineStart && line <= x.lineEnd);
71
+ .map(cls => this.getConfig(cls))
72
+ .filter(config => !config.skip);
73
+ const suite = suites.find(config => line >= config.lineStart && line <= config.lineEnd);
75
74
 
76
75
  if (suite) {
77
76
  const tests = sortedTests(suite);
78
- const test = tests.find(x => line >= x.lineStart && line <= x.lineEnd);
77
+ const test = tests.find(config => line >= config.lineStart && line <= config.lineEnd);
79
78
  return test ? [{ suite, tests: [test] }] : [{ suite, tests }];
80
79
  } else {
81
- return suites.map(x => ({ suite: x, tests: sortedTests(x) }));
80
+ return suites.map(config => ({ suite: config, tests: sortedTests(config) }));
82
81
  }
83
82
  } else { // Else lookup directly
84
83
  if (methodNames.length) {
85
- const cls = this.getValidClasses().find(x => x.Ⲑid === clsId);
84
+ const cls = this.getValidClasses().find(type => type.Ⲑid === clsId);
86
85
  if (!cls) {
87
86
  throw new AppError('Unable to find suite for class ID', { details: { classId: clsId } });
88
87
  }
89
88
  const suite = this.getConfig(cls);
90
- const tests = sortedTests(suite).filter(x => methodNames.includes(x.methodName));
89
+ const tests = sortedTests(suite).filter(config => methodNames.includes(config.methodName));
91
90
  return [{ suite, tests }];
92
91
  } else if (clsId) {
93
- const cls = this.getValidClasses().find(x => x.Ⲑid === clsId)!;
92
+ const cls = this.getValidClasses().find(type => type.Ⲑid === clsId)!;
94
93
  if (!cls) {
95
94
  throw new AppError('Unable to find suite for class ID', { details: { classId: clsId } });
96
95
  }
@@ -98,9 +97,9 @@ export class SuiteRegistryIndex implements RegistryIndex {
98
97
  return suite ? [{ suite, tests: sortedTests(suite) }] : [];
99
98
  } else {
100
99
  const suites = this.getValidClasses()
101
- .map(x => this.getConfig(x))
102
- .filter(x => !describeFunction(x.class).abstract); // Do not run abstract suites
103
- return suites.map(x => ({ suite: x, tests: sortedTests(x) }));
100
+ .map(type => this.getConfig(type))
101
+ .filter(config => !describeFunction(config.class).abstract); // Do not run abstract suites
102
+ return suites.map(config => ({ suite: config, tests: sortedTests(config) }));
104
103
  }
105
104
  }
106
105
  }
@@ -108,10 +107,11 @@ export class SuiteRegistryIndex implements RegistryIndex {
108
107
  /**
109
108
  * Find a test configuration given class and optionally a method
110
109
  */
111
- getTestConfig(cls: Class, method: Function): TestConfig | undefined {
110
+ getTestConfig(cls: Class, method: Function | string): TestConfig | undefined {
112
111
  if (this.store.has(cls)) {
113
- const conf = this.getConfig(cls);
114
- return Object.values(conf.tests).find(x => x.methodName === method.name);
112
+ const config = this.getConfig(cls);
113
+ const methodName = typeof method === 'string' ? method : method.name;
114
+ return Object.values(config.tests).find(item => item.methodName === methodName);
115
115
  }
116
116
  }
117
117
  }
@@ -1,12 +1,12 @@
1
1
  import { createWriteStream } from 'node:fs';
2
2
 
3
- import { ConsoleManager, Env, Util, Runtime } from '@travetto/runtime';
3
+ import { ConsoleManager, Env, Runtime } from '@travetto/runtime';
4
4
  import { IpcChannel } from '@travetto/worker';
5
5
 
6
- import { RunnerUtil } from '../execute/util.ts';
7
- import { Runner } from '../execute/runner.ts';
8
- import { Events } from './types.ts';
6
+ import { RunUtil } from '../execute/run.ts';
7
+ import { TestWorkerEvents } from './types.ts';
9
8
  import { TestRun } from '../model/test.ts';
9
+ import { CommunicationUtil } from '../communication.ts';
10
10
 
11
11
  /**
12
12
  * Child Worker for the Test Runner. Receives events as commands
@@ -16,16 +16,16 @@ export class TestChildWorker extends IpcChannel<TestRun> {
16
16
 
17
17
  #done = Promise.withResolvers<void>();
18
18
 
19
- async #exec(op: () => Promise<unknown>, type: string): Promise<void> {
19
+ async #exec(operation: () => Promise<unknown>, type: string): Promise<void> {
20
20
  try {
21
- await op();
21
+ await operation();
22
22
  this.send(type); // Respond
23
- } catch (err) {
24
- if (!(err instanceof Error)) {
25
- throw err;
23
+ } catch (error) {
24
+ if (!(error instanceof Error)) {
25
+ throw error;
26
26
  }
27
27
  // Mark as errored out
28
- this.send(type, JSON.parse(Util.serializeToJSON({ error: err })));
28
+ this.send(type, CommunicationUtil.serializeToObject({ error }));
29
29
  }
30
30
  }
31
31
 
@@ -33,22 +33,22 @@ export class TestChildWorker extends IpcChannel<TestRun> {
33
33
  * Start the worker
34
34
  */
35
35
  async activate(): Promise<void> {
36
- if (/\b@travetto[/]test\b/.test(Env.DEBUG.val ?? '')) {
36
+ if (/\b@travetto[/]test\b/.test(Env.DEBUG.value ?? '')) {
37
37
  const file = Runtime.toolPath(`test-worker.${process.pid}.log`);
38
38
  const stdout = createWriteStream(file, { flags: 'a' });
39
- const c = new console.Console({ stdout, inspectOptions: { depth: 4, colors: false } });
40
- ConsoleManager.set({ log: (ev) => c[ev.level](process.pid, ...ev.args) });
39
+ const cons = new console.Console({ stdout, inspectOptions: { depth: 4, colors: false } });
40
+ ConsoleManager.set({ log: (event) => cons[event.level](process.pid, ...event.args) });
41
41
  } else {
42
42
  ConsoleManager.set({ log: () => { } });
43
43
  }
44
44
 
45
- RunnerUtil.registerCleanup('worker');
45
+ RunUtil.registerCleanup('worker');
46
46
 
47
47
  // Listen for inbound requests
48
- this.on('*', ev => this.onCommand(ev));
48
+ this.on('*', event => this.onCommand(event));
49
49
 
50
50
  // Let parent know the child is ready for handling commands
51
- this.send(Events.READY);
51
+ this.send(TestWorkerEvents.READY);
52
52
 
53
53
  await this.#done.promise;
54
54
  }
@@ -59,10 +59,10 @@ export class TestChildWorker extends IpcChannel<TestRun> {
59
59
  async onCommand(event: TestRun & { type: string }): Promise<boolean> {
60
60
  console.debug('on message', { ...event });
61
61
 
62
- if (event.type === Events.INIT) { // On request to init, start initialization
63
- await this.#exec(() => this.onInitCommand(), Events.INIT_COMPLETE);
64
- } else if (event.type === Events.RUN) { // On request to run, start running
65
- await this.#exec(() => this.onRunCommand(event), Events.RUN_COMPLETE);
62
+ if (event.type === TestWorkerEvents.INIT) { // On request to init, start initialization
63
+ await this.#exec(() => this.onInitCommand(), TestWorkerEvents.INIT_COMPLETE);
64
+ } else if (event.type === TestWorkerEvents.RUN) { // On request to run, start running
65
+ await this.#exec(() => this.onRunCommand(event), TestWorkerEvents.RUN_COMPLETE);
66
66
  }
67
67
 
68
68
  return false;
@@ -80,7 +80,7 @@ export class TestChildWorker extends IpcChannel<TestRun> {
80
80
  console.debug('Running', { import: run.import });
81
81
 
82
82
  try {
83
- await new Runner({ consumer: 'exec', target: run }).run();
83
+ await RunUtil.runTests({ consumer: 'exec' }, run);
84
84
  } finally {
85
85
  this.#done.resolve();
86
86
  }