@travetto/test 2.1.3 → 2.2.0

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 CHANGED
@@ -88,12 +88,12 @@ let SimpleTest = class SimpleTest {
88
88
  ᚕ_check_1.AssertCheck.check({ file: ᚕsrc(__filename), line: 11, text: "{ size: 20, address: { state: 'VA' } } === {}", operator: "deepStrictEqual" }, true, { size: 20, address: { state: 'VA' } }, {});
89
89
  }
90
90
  };
91
- (0, tslib_1.__decorate)([
91
+ tslib_1.__decorate([
92
92
  (0, test_1.Test)({ lines: { start: 8, end: 12, codeStart: 11 } })
93
93
  ], SimpleTest.prototype, "test", null);
94
- SimpleTest = (0, tslib_1.__decorate)([
94
+ SimpleTest = tslib_1.__decorate([
95
95
  ᚕ_decorator_1.Register(),
96
- (0, test_1.Suite)({ lines: { start: 5, end: 13 } })
96
+ (0, test_1.Suite)({ lines: {} })
97
97
  ], SimpleTest);
98
98
  Object.defineProperty(exports, 'ᚕtrv', { configurable: true, value: true });
99
99
  ```
package/bin/cli-test.ts CHANGED
@@ -16,27 +16,29 @@ export class TestPlugin extends BasePlugin {
16
16
  name = 'test';
17
17
  _types: string[];
18
18
 
19
- getTypes() {
19
+ getTypes(): string[] {
20
20
  if (!this._types) {
21
21
  this._types = ScanFs.scanDirSync({},
22
22
  PathUtil.resolveUnix(__dirname, '..', 'src/consumer/types/')
23
23
  )
24
- .filter(x => x.stats.isFile())
25
- .map(x => readFileSync(x.file, 'utf8').match(/@Consumable[(]'([^']+)/)?.[1] as string);
24
+ .filter(x => x.stats?.isFile())
25
+ .map(x => readFileSync(x.file, 'utf8').match(/@Consumable[(]'([^']+)/)?.[1])
26
+ .filter((x?: string): x is string => !!x);
26
27
  }
27
28
  return this._types;
28
29
  }
29
30
 
31
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
30
32
  getOptions() {
31
33
  return {
32
34
  format: this.choiceOption({ desc: 'Output format for test results', def: 'tap', choices: this.getTypes() }),
33
35
  concurrency: this.intOption({ desc: 'Number of tests to run concurrently', lower: 1, upper: 32, def: Math.min(4, os.cpus().length - 1) }),
34
36
  isolated: this.boolOption({ desc: 'Isolated mode' }),
35
37
  mode: this.choiceOption({ desc: 'Test run mode', def: 'standard', choices: [...modes] })
36
- };
38
+ } as const;
37
39
  }
38
40
 
39
- envInit() {
41
+ envInit(): void {
40
42
  EnvInit.init({
41
43
  debug: '0',
42
44
  set: { TRV_LOG_TIME: '0' },
@@ -49,11 +51,11 @@ export class TestPlugin extends BasePlugin {
49
51
  });
50
52
  }
51
53
 
52
- getArgs() {
54
+ getArgs(): string {
53
55
  return '[regexes...]';
54
56
  }
55
57
 
56
- async isFile(file: string, errorIfNot?: string) {
58
+ async isFile(file: string, errorIfNot?: string): Promise<true | undefined> {
57
59
  try {
58
60
  const stat = await FsUtil.exists(file);
59
61
  const res = stat?.isFile();
@@ -67,12 +69,12 @@ export class TestPlugin extends BasePlugin {
67
69
  }
68
70
  }
69
71
 
70
- async onSingle(state: Partial<RunState>, file: string) {
72
+ async onSingle(state: Partial<RunState>, file: string): Promise<void> {
71
73
  await this.isFile(file, 'You must specify a proper test file to run in single mode');
72
74
  state.mode = 'single';
73
75
  }
74
76
 
75
- async onStandard(state: Partial<RunState>, first: string) {
77
+ async onStandard(state: Partial<RunState>, first: string): Promise<void> {
76
78
  const isFile = await this.isFile(first);
77
79
 
78
80
  if (!first) {
@@ -95,7 +97,7 @@ export class TestPlugin extends BasePlugin {
95
97
 
96
98
  const [first] = regexes;
97
99
 
98
- const state: Partial<RunState> = {
100
+ const state: RunState = {
99
101
  args: regexes,
100
102
  mode: this.cmd.mode,
101
103
  concurrency: +this.cmd.concurrency,
@@ -108,6 +110,6 @@ export class TestPlugin extends BasePlugin {
108
110
  case 'standard': await this.onStandard(state, first); break;
109
111
  }
110
112
 
111
- await runTests(state as RunState);
113
+ await runTests(state);
112
114
  }
113
115
  }
package/bin/lib/run.ts CHANGED
@@ -4,7 +4,7 @@ import type { RunState } from '../../src/execute/types';
4
4
  * Run tests given the input state
5
5
  * @param opts
6
6
  */
7
- export async function runTests(opts: RunState) {
7
+ export async function runTests(opts: RunState): Promise<void> {
8
8
  const { PhaseManager, Util } = await import('@travetto/base');
9
9
  await PhaseManager.run('init', '*', ['@trv:registry/init']); // Delay registry
10
10
 
@@ -14,6 +14,7 @@ export async function runTests(opts: RunState) {
14
14
  RunnerUtil.registerCleanup('runner');
15
15
 
16
16
  if (process.env.TRV_TEST_DELAY) {
17
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17
18
  await Util.wait(process.env.TRV_TEST_DELAY as '2s');
18
19
  }
19
20
 
package/bin/test-child.ts CHANGED
@@ -3,7 +3,7 @@ import { createWriteStream } from 'fs';
3
3
  import { AppCache } from '@travetto/boot';
4
4
  import { EnvInit } from '@travetto/base/bin/init';
5
5
 
6
- export async function customLogs() {
6
+ export async function customLogs(): Promise<void> {
7
7
  const { ConsoleManager } = await import('@travetto/base');
8
8
 
9
9
  const c = new console.Console({
@@ -16,7 +16,7 @@ export async function customLogs() {
16
16
  });
17
17
  }
18
18
 
19
- export async function main() {
19
+ export async function main(): Promise<void> {
20
20
  EnvInit.init({
21
21
  debug: '0',
22
22
  set: { TRV_LOG_TIME: '0' },
@@ -2,7 +2,7 @@ import { EnvInit } from '@travetto/base/bin/init';
2
2
  import { runTests } from './lib/run';
3
3
 
4
4
  // Direct entry point
5
- export function main(...args: string[]) {
5
+ export function main(...args: string[]): Promise<void> {
6
6
  EnvInit.init({
7
7
  debug: '0',
8
8
  set: { TRV_LOG_TIME: '0' },
package/bin/test-watch.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EnvInit } from '@travetto/base/bin/init';
2
2
 
3
- export async function main(format: string = 'tap') {
3
+ export async function main(format: string = 'tap'): Promise<void> {
4
4
  EnvInit.init({
5
5
  debug: '0',
6
6
  set: { TRV_LOG_TIME: '0' },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/test",
3
3
  "displayName": "Testing",
4
- "version": "2.1.3",
4
+ "version": "2.2.0",
5
5
  "description": "Declarative test framework",
6
6
  "keywords": [
7
7
  "unit-testing",
@@ -29,15 +29,15 @@
29
29
  "directory": "module/test"
30
30
  },
31
31
  "dependencies": {
32
- "@travetto/base": "^2.1.2",
33
- "@travetto/transformer": "^2.1.2",
34
- "@travetto/registry": "^2.1.3",
35
- "@travetto/watch": "^2.1.2",
36
- "@travetto/worker": "^2.1.2",
37
- "@travetto/yaml": "^2.1.3"
32
+ "@travetto/base": "^2.2.0",
33
+ "@travetto/transformer": "^2.2.0",
34
+ "@travetto/registry": "^2.2.0",
35
+ "@travetto/watch": "^2.2.0",
36
+ "@travetto/worker": "^2.2.0",
37
+ "@travetto/yaml": "^2.2.0"
38
38
  },
39
39
  "optionalPeerDependencies": {
40
- "@travetto/cli": "^2.1.2"
40
+ "@travetto/cli": "^2.2.0"
41
41
  },
42
42
  "publishConfig": {
43
43
  "access": "public"
@@ -20,11 +20,11 @@ class $AssertCapture {
20
20
  * @param test Test to capture for
21
21
  * @param listener optional listener for events
22
22
  */
23
- collector(test: TestConfig, listener?: (a: Assertion) => void) {
23
+ collector(test: TestConfig, listener?: (a: Assertion) => void): () => Assertion[] {
24
24
  const assertions: Assertion[] = [];
25
25
 
26
26
  // Emit and collect, every assertion as it occurs
27
- const handler = (a: CaptureAssert) => {
27
+ const handler = (a: CaptureAssert): void => {
28
28
  const assrt = {
29
29
  ...a,
30
30
  classId: test.classId,
@@ -44,7 +44,7 @@ class $AssertCapture {
44
44
  };
45
45
  }
46
46
 
47
- add(a: CaptureAssert) {
47
+ add(a: CaptureAssert): void {
48
48
  this.#emitter.emit('assert', a);
49
49
  }
50
50
  }
@@ -3,7 +3,7 @@ import * as assert from 'assert';
3
3
  import { PathUtil } from '@travetto/boot';
4
4
  import { Util, AppError, ClassInstance, Class } from '@travetto/base';
5
5
 
6
- import { ThrowableError, TestConfig } from '../model/test';
6
+ import { ThrowableError, TestConfig, Assertion } from '../model/test';
7
7
  import { AssertCapture, CaptureAssert } from './capture';
8
8
  import { AssertUtil } from './util';
9
9
  import { ASSERT_FN_OPERATOR, OP_MAPPING } from './types';
@@ -14,6 +14,11 @@ declare module 'assert' {
14
14
  }
15
15
  }
16
16
 
17
+ type StringFields<T> = {
18
+ [K in Extract<keyof T, string>]:
19
+ (T[K] extends string ? K : never) // eslint-disable-line @typescript-eslint/ban-types
20
+ }[Extract<keyof T, string>];
21
+
17
22
  /**
18
23
  * Check assertion
19
24
  */
@@ -24,7 +29,7 @@ export class AssertCheck {
24
29
  * @param positive Is the check positive or negative
25
30
  * @param args The arguments passed in
26
31
  */
27
- static check(assertion: CaptureAssert, positive: boolean, ...args: unknown[]) {
32
+ static check(assertion: CaptureAssert, positive: boolean, ...args: unknown[]): void {
28
33
  let fn = assertion.operator;
29
34
  assertion.operator = ASSERT_FN_OPERATOR[fn];
30
35
 
@@ -34,36 +39,45 @@ export class AssertCheck {
34
39
  };
35
40
 
36
41
  // Invert check for negative
37
- const assertFn = positive ? assert : (x: unknown, msg?: string) => assert(!x, msg);
42
+ const assertFn = positive ? assert : (x: unknown, msg?: string): unknown => assert(!x, msg);
38
43
 
39
44
  // Check fn to call
40
45
  if (fn === 'fail') {
41
46
  if (args.length > 1) {
47
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
42
48
  [assertion.actual, assertion.expected, assertion.message, assertion.operator] = args as [unknown, unknown, string, string];
43
49
  } else {
50
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
51
  [assertion.message] = args as [string];
45
52
  }
46
53
  } else if (/throw|reject/i.test(fn)) {
47
54
  assertion.operator = fn;
48
55
  if (typeof args[1] !== 'string') {
56
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
49
57
  [, assertion.expected, assertion.message] = args as [unknown, unknown, string];
50
58
  } else {
59
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
51
60
  [, assertion.message] = args as [unknown, string];
52
61
  }
53
62
  } else if (fn === 'ok' || fn === 'assert') {
54
63
  fn = assertion.operator = 'ok';
64
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
55
65
  [assertion.actual, assertion.message] = args as [unknown, string];
56
66
  assertion.expected = { toClean: () => positive ? 'truthy' : 'falsy' };
57
67
  common.state = 'should be';
58
68
  } else if (fn === 'includes') {
59
69
  assertion.operator = fn;
70
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
60
71
  [assertion.expected, assertion.actual, assertion.message] = args as [unknown, unknown, string];
61
72
  } else if (fn === 'instanceof') {
62
73
  assertion.operator = fn;
74
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
63
75
  [assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
76
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
64
77
  assertion.actual = (assertion.actual as ClassInstance)?.constructor;
65
78
  } else { // Handle unknown
66
79
  assertion.operator = fn ?? '';
80
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
67
81
  [assertion.actual, assertion.expected, assertion.message] = args as [unknown, unknown, string];
68
82
  }
69
83
 
@@ -77,44 +91,56 @@ export class AssertCheck {
77
91
  assertion.expected = AssertUtil.cleanValue(assertion.expected);
78
92
  }
79
93
 
94
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
80
95
  const [actual, expected, message] = args as [unknown, unknown, string];
81
96
 
82
97
  // Actually run the assertion
83
98
  switch (fn) {
99
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
84
100
  case 'instanceof': assertFn(actual instanceof (expected as Class), message); break;
101
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
85
102
  case 'in': assertFn((actual as string) in (expected as object), message); break;
103
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
86
104
  case 'lessThan': assertFn((actual as number) < (expected as number), message); break;
105
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
87
106
  case 'lessThanEqual': assertFn((actual as number) <= (expected as number), message); break;
107
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
88
108
  case 'greaterThan': assertFn((actual as number) > (expected as number), message); break;
109
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
89
110
  case 'greaterThanEqual': assertFn((actual as number) >= (expected as number), message); break;
111
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
90
112
  case 'ok': assertFn.apply(null, args as [unknown, string]); break; // eslint-disable-line prefer-spread
91
113
  default:
114
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
92
115
  if (fn && assert[fn as keyof typeof assert]) { // Assert call
93
116
  if (/not/i.test(fn)) {
94
117
  common.state = 'should not';
95
118
  }
119
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
96
120
  assert[fn as 'ok'].apply(null, args as [boolean, string | undefined]);
121
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
97
122
  } else if (expected && !!(expected as Record<string, Function>)[fn]) { // Dotted Method call (e.g. assert.rejects)
123
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
98
124
  assertFn((expected as typeof assert)[fn as 'ok'](actual));
99
125
  }
100
126
  }
101
127
 
102
128
  // Pushing on not error
103
129
  AssertCapture.add(assertion);
104
- } catch (e) {
130
+ } catch (err) {
105
131
  // On error, produce the appropriate error message
106
- if (e instanceof assert.AssertionError) {
132
+ if (err instanceof assert.AssertionError) {
107
133
  if (!assertion.message) {
108
134
  assertion.message = (OP_MAPPING[fn] ?? '{state} be {expected}');
109
135
  }
110
136
  assertion.message = assertion.message
111
- .replace(/[{]([A-Za-z]+)[}]/g, (a, k) => common[k] || assertion[k as keyof typeof assertion] as string)
137
+ .replace(/[{]([A-Za-z]+)[}]/g, (a, k: StringFields<Assertion>) => common[k] || assertion[k]!)
112
138
  .replace(/not not/g, ''); // Handle double negatives
113
- assertion.error = e;
114
- e.message = assertion.message;
139
+ assertion.error = err;
140
+ err.message = assertion.message;
115
141
  AssertCapture.add(assertion);
116
142
  }
117
- throw e;
143
+ throw err;
118
144
  }
119
145
  }
120
146
 
@@ -158,6 +184,30 @@ export class AssertCheck {
158
184
  }
159
185
  }
160
186
 
187
+ static #onError(
188
+ positive: boolean,
189
+ message: string | undefined,
190
+ err: unknown, missed: Error | undefined,
191
+ shouldThrow: ThrowableError | undefined,
192
+ assertion: CaptureAssert
193
+ ): void {
194
+ if (Util.isPrimitive(err)) {
195
+ err = new Error(`${err}`);
196
+ }
197
+ if (!(err instanceof Error)) {
198
+ throw err;
199
+ }
200
+ if (positive) {
201
+ missed = new AppError('Error thrown, but expected no errors', 'general', {}, err.stack);
202
+ }
203
+
204
+ const resolvedErr = (missed && err) ?? this.checkError(shouldThrow, err);
205
+ if (resolvedErr) {
206
+ assertion.message = message || missed?.message || resolvedErr.message;
207
+ throw (assertion.error = resolvedErr);
208
+ }
209
+ }
210
+
161
211
  /**
162
212
  * Check the throw, doesNotThrow behavior of an assertion
163
213
  * @param assertion The basic assertion information
@@ -166,7 +216,13 @@ export class AssertCheck {
166
216
  * @param shouldThrow Should this action throw
167
217
  * @param message Message to share on failure
168
218
  */
169
- static checkThrow(assertion: CaptureAssert, positive: boolean, action: Function, shouldThrow?: ThrowableError, message?: string) {
219
+ static checkThrow(
220
+ assertion: CaptureAssert,
221
+ positive: boolean,
222
+ action: Function,
223
+ shouldThrow?: ThrowableError,
224
+ message?: string
225
+ ): void {
170
226
  let missed: Error | undefined;
171
227
 
172
228
  try {
@@ -177,17 +233,8 @@ export class AssertCheck {
177
233
  }
178
234
  throw (missed = new AppError(`No error thrown, but expected ${shouldThrow ?? 'an error'}`));
179
235
  }
180
- } catch (e) {
181
- if (positive) {
182
- missed = new AppError('Error thrown, but expected no errors');
183
- missed.stack = e.stack;
184
- }
185
-
186
- e = (missed && e) || this.checkError(shouldThrow, e);
187
- if (e) {
188
- assertion.message = message || missed?.message || e.message;
189
- throw (assertion.error = e);
190
- }
236
+ } catch (err) {
237
+ this.#onError(positive, message, err, missed, shouldThrow, assertion);
191
238
  } finally {
192
239
  AssertCapture.add(assertion);
193
240
  }
@@ -201,7 +248,13 @@ export class AssertCheck {
201
248
  * @param shouldThrow Should this action reject
202
249
  * @param message Message to share on failure
203
250
  */
204
- static async checkThrowAsync(assertion: CaptureAssert, positive: boolean, action: Function | Promise<unknown>, shouldThrow?: ThrowableError, message?: string) {
251
+ static async checkThrowAsync(
252
+ assertion: CaptureAssert,
253
+ positive: boolean,
254
+ action: Function | Promise<unknown>,
255
+ shouldThrow?: ThrowableError,
256
+ message?: string
257
+ ): Promise<void> {
205
258
  let missed: Error | undefined;
206
259
 
207
260
  try {
@@ -216,16 +269,8 @@ export class AssertCheck {
216
269
  }
217
270
  throw (missed = new AppError(`No error thrown, but expected ${shouldThrow ?? 'an error'} `));
218
271
  }
219
- } catch (e) {
220
- if (positive) {
221
- missed = new AppError('Error thrown, but expected no errors');
222
- }
223
-
224
- e = (missed && e) || this.checkError(shouldThrow, e);
225
- if (e) {
226
- assertion.message = message || missed?.message || e.message;
227
- throw (assertion.error = e);
228
- }
272
+ } catch (err) {
273
+ this.#onError(positive, message, err, missed, shouldThrow, assertion);
229
274
  } finally {
230
275
  AssertCapture.add(assertion);
231
276
  }
@@ -234,7 +279,7 @@ export class AssertCheck {
234
279
  /**
235
280
  * Look for any unhandled exceptions
236
281
  */
237
- static checkUnhandled(test: TestConfig, err: Error | assert.AssertionError) {
282
+ static checkUnhandled(test: TestConfig, err: Error | assert.AssertionError): void {
238
283
  let line = AssertUtil.getPositionOfError(err, test.file).line;
239
284
  if (line === 1) {
240
285
  line = test.lines.start;
@@ -7,6 +7,7 @@ import { TestConfig, Assertion, TestResult } from '../model/test';
7
7
  import { SuiteConfig } from '../model/suite';
8
8
 
9
9
  function isCleanable(o: unknown): o is { toClean(): unknown } {
10
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
11
  return !!o && !!(o as { toClean: unknown }).toClean;
11
12
  }
12
13
 
@@ -17,7 +18,7 @@ export class AssertUtil {
17
18
  /**
18
19
  * Clean a value for displaying in the output
19
20
  */
20
- static cleanValue(val: unknown) {
21
+ static cleanValue(val: unknown): unknown {
21
22
  if (isCleanable(val)) {
22
23
  return val.toClean();
23
24
  } else if (val === null || val === undefined
@@ -26,6 +27,7 @@ export class AssertUtil {
26
27
  ) {
27
28
  return JSON.stringify(val);
28
29
  } else {
30
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
29
31
  const subV = val as (Class | ClassInstance);
30
32
  if (subV.ᚕid || !subV.constructor || (!subV.constructor.ᚕid && Util.isFunction(subV))) { // If a function, show name
31
33
  return subV.name;
@@ -38,7 +40,7 @@ export class AssertUtil {
38
40
  /**
39
41
  * Determine file location for a given error and the stack trace
40
42
  */
41
- static getPositionOfError(err: Error, filename: string) {
43
+ static getPositionOfError(err: Error, filename: string): { file: string, line: number } {
42
44
  const lines = (err.stack ?? new Error().stack!)
43
45
  .replace(/[\\]/g, '/')
44
46
  .split('\n')
@@ -82,7 +84,7 @@ export class AssertUtil {
82
84
  /**
83
85
  * Generate a suite error given a suite config, and an error
84
86
  */
85
- static generateSuiteError(suite: SuiteConfig, methodName: string, error: Error) {
87
+ static generateSuiteError(suite: SuiteConfig, methodName: string, error: Error): { assert: Assertion, testResult: TestResult, testConfig: TestConfig } {
86
88
  const { file, ...pos } = this.getPositionOfError(error, suite.file);
87
89
  let line = pos.line;
88
90
 
@@ -20,5 +20,8 @@ export type TestResultsEnhancer = typeof COLOR_ENHANCER;
20
20
  /**
21
21
  * Dummy enhancer does nothing
22
22
  */
23
- export const DUMMY_ENHANCER = (Object.keys(COLOR_ENHANCER) as (keyof typeof COLOR_ENHANCER)[])
24
- .reduce((acc, k) => (acc[k] = (x: unknown) => `${x}`) && acc, {} as TestResultsEnhancer);
23
+ export const DUMMY_ENHANCER: TestResultsEnhancer = [
24
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
25
+ (Object.keys(COLOR_ENHANCER) as (keyof typeof COLOR_ENHANCER)[])
26
+ .reduce<Partial<TestResultsEnhancer>>((acc, k) => (acc[k] = (x: unknown): string => `${x}`) && acc, {})
27
+ ].filter((x): x is TestResultsEnhancer => !!x)[0];
@@ -9,9 +9,9 @@ class $TestConsumerRegistry {
9
9
  #primary: Class<TestConsumer>;
10
10
 
11
11
  /**
12
- * Manual initialization when running oustide of the bootstrap process
12
+ * Manual initialization when running outside of the bootstrap process
13
13
  */
14
- async manualInit() {
14
+ async manualInit(): Promise<void> {
15
15
  await import('./types/index');
16
16
  }
17
17
 
@@ -21,7 +21,7 @@ class $TestConsumerRegistry {
21
21
  * @param cls The consumer class
22
22
  * @param isDefault Set as the default consumer
23
23
  */
24
- add(type: string, cls: Class<TestConsumer>, isDefault = false) {
24
+ add(type: string, cls: Class<TestConsumer>, isDefault = false): void {
25
25
  if (isDefault) {
26
26
  this.#primary = cls;
27
27
  }
@@ -32,8 +32,8 @@ class $TestConsumerRegistry {
32
32
  * Retrieve a registered consumer
33
33
  * @param type The unique identifier
34
34
  */
35
- get(type: string) {
36
- return this.#registered.get(type);
35
+ get(type: string): Class<TestConsumer> {
36
+ return this.#registered.get(type)!;
37
37
  }
38
38
 
39
39
  /**
@@ -42,6 +42,7 @@ class $TestConsumerRegistry {
42
42
  */
43
43
  getInstance(consumer: string | TestConsumer): TestConsumer {
44
44
  return typeof consumer === 'string' ?
45
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
45
46
  new ((this.get(consumer) ?? this.#primary) as ConcreteClass)() :
46
47
  consumer;
47
48
  }
@@ -54,8 +55,9 @@ export const TestConsumerRegistry = new $TestConsumerRegistry();
54
55
  * @param type The unique identifier for the consumer
55
56
  * @param isDefault Is this the default consumer. Last one wins
56
57
  */
57
- export function Consumable(type: string, isDefault = false) {
58
+ export function Consumable(type: string, isDefault = false): ClassDecorator {
59
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
58
60
  return function (cls: Class<TestConsumer>) {
59
61
  TestConsumerRegistry.add(type, cls, isDefault);
60
- };
62
+ } as ClassDecorator;
61
63
  }
@@ -26,7 +26,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
26
26
  */
27
27
  summarizeSuite(test: TestResult): SuiteResult {
28
28
  try {
29
- // TODO: Load asyncronously
29
+ // TODO: Load asynchronously
30
30
  require(test.file);
31
31
  this.#state[test.classId] = this.#state[test.classId] ?? {};
32
32
  this.#state[test.classId][test.methodName] = test.status;
@@ -42,7 +42,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
42
42
  /**
43
43
  * Remove a class
44
44
  */
45
- removeClass(clsId: string) {
45
+ removeClass(clsId: string): SuiteResult {
46
46
  this.#state[clsId] = {};
47
47
  return {
48
48
  classId: clsId, passed: 0, failed: 0, skipped: 0, total: 0, tests: [], duration: 0, file: '', lines: { start: 0, end: 0 }
@@ -52,7 +52,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
52
52
  /**
53
53
  * Compute totals
54
54
  */
55
- computeTotal(cls: Class) {
55
+ computeTotal(cls: Class): SuiteResult {
56
56
  const suite = SuiteRegistry.get(cls);
57
57
  const total = suite.tests.reduce((acc, x) => {
58
58
  const status = this.#state[x.classId][x.methodName] ?? 'unknown';
@@ -77,7 +77,7 @@ export class CumulativeSummaryConsumer implements TestConsumer {
77
77
  * Listen for event, process the full event, and if the event is an after test,
78
78
  * send a full suite summary
79
79
  */
80
- onEvent(e: TestEvent) {
80
+ onEvent(e: TestEvent): void {
81
81
  this.#target.onEvent(e);
82
82
  try {
83
83
  if (e.type === 'test' && e.phase === 'after') {
@@ -6,7 +6,7 @@ import { ConsumerUtil } from '../util';
6
6
  import { Consumable } from '../registry';
7
7
 
8
8
  /**
9
- * Streams all test events a JSON payload, in an ndjson format
9
+ * Streams all test events a JSON payload, in an nd-json format
10
10
  */
11
11
  @Consumable('event')
12
12
  export class EventStreamer implements TestConsumer {
@@ -16,7 +16,7 @@ export class EventStreamer implements TestConsumer {
16
16
  this.#stream = stream;
17
17
  }
18
18
 
19
- onEvent(event: TestEvent) {
19
+ onEvent(event: TestEvent): void {
20
20
  const out = { ...event };
21
21
  ConsumerUtil.serializeErrors(out);
22
22
  this.#stream.write(`${JSON.stringify(out)}\n`);
@@ -11,7 +11,7 @@ import { Consumable } from '../registry';
11
11
  @Consumable('exec')
12
12
  export class ExecutionEmitter extends ChildCommChannel<TestEvent> implements TestConsumer {
13
13
 
14
- onEvent(event: TestEvent) {
14
+ onEvent(event: TestEvent): void {
15
15
  const out = { ...event };
16
16
  ConsumerUtil.serializeErrors(out);
17
17
  this.send(event.type, out);
@@ -16,9 +16,9 @@ export class JSONEmitter implements TestConsumer {
16
16
  this.#stream = stream;
17
17
  }
18
18
 
19
- onEvent(event: TestEvent) { }
19
+ onEvent(event: TestEvent): void { }
20
20
 
21
- onSummary(summary: SuitesSummary) {
21
+ onSummary(summary: SuitesSummary): void {
22
22
  this.#stream.write(JSON.stringify(summary, undefined, 2));
23
23
  }
24
24
  }