@travetto/test 8.0.0-alpha.7 → 8.0.0-alpha.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/test",
3
- "version": "8.0.0-alpha.7",
3
+ "version": "8.0.0-alpha.9",
4
4
  "type": "module",
5
5
  "description": "Declarative test framework",
6
6
  "keywords": [
@@ -28,14 +28,14 @@
28
28
  "directory": "module/test"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/registry": "^8.0.0-alpha.7",
32
- "@travetto/runtime": "^8.0.0-alpha.7",
33
- "@travetto/terminal": "^8.0.0-alpha.7",
34
- "@travetto/worker": "^8.0.0-alpha.7",
31
+ "@travetto/registry": "^8.0.0-alpha.9",
32
+ "@travetto/runtime": "^8.0.0-alpha.9",
33
+ "@travetto/terminal": "^8.0.0-alpha.9",
34
+ "@travetto/worker": "^8.0.0-alpha.9",
35
35
  "yaml": "^2.8.2"
36
36
  },
37
37
  "peerDependencies": {
38
- "@travetto/cli": "^8.0.0-alpha.12",
38
+ "@travetto/cli": "^8.0.0-alpha.14",
39
39
  "@travetto/transformer": "^8.0.0-alpha.4"
40
40
  },
41
41
  "peerDependenciesMeta": {
@@ -1,5 +1,4 @@
1
1
  import path from 'node:path';
2
- import { AssertionError } from 'node:assert';
3
2
  import { stringify } from 'yaml';
4
3
 
5
4
  import { Terminal, StyleUtil } from '@travetto/terminal';
@@ -9,6 +8,7 @@ import type { TestEvent } from '../../model/event.ts';
9
8
  import type { SuitesSummary, TestConsumerShape } from '../types.ts';
10
9
  import { TestConsumer } from '../decorator.ts';
11
10
  import { type TestResultsEnhancer, CONSOLE_ENHANCER } from '../enhancer.ts';
11
+ import { TestConsumerUtil } from './util.ts';
12
12
 
13
13
  const SPACE = ' ';
14
14
 
@@ -57,22 +57,6 @@ export class TapEmitter implements TestConsumerShape {
57
57
  this.log(`---\n${this.#enhancer.objectInspect(body)}\n...`);
58
58
  }
59
59
 
60
- /**
61
- * Error to string
62
- * @param error
63
- */
64
- errorToString(error?: Error): string | undefined {
65
- if (error instanceof AssertionError) {
66
- return;
67
- } else if (error instanceof Error) {
68
- return error.stack ?
69
- error.stack.split(/\n/).slice(0, this.#options?.verbose ? -1 : 5).join('\n') :
70
- error.message;
71
- } else {
72
- return `${error}`;
73
- }
74
- }
75
-
76
60
  /**
77
61
  * Listen for each event
78
62
  */
@@ -140,7 +124,7 @@ export class TapEmitter implements TestConsumerShape {
140
124
  case 'errored':
141
125
  case 'failed': {
142
126
  if (test.error) {
143
- const message = this.errorToString(test.error);
127
+ const message = TestConsumerUtil.errorToString(test.error, !!this.#options?.verbose);
144
128
  if (message) {
145
129
  this.logMeta({ error: message });
146
130
  }
@@ -0,0 +1,28 @@
1
+ import util from 'node:util';
2
+ import { AssertionError } from 'node:assert';
3
+
4
+ import { TypedObject } from '@travetto/runtime';
5
+
6
+ export class TestConsumerUtil {
7
+ /**
8
+ * Convert error to string
9
+ */
10
+ static errorToString(error?: Error, verbose?: boolean): string | undefined {
11
+ if (error instanceof AssertionError) {
12
+ return;
13
+ } else if (error instanceof Error) {
14
+ const stack = error.stack ?
15
+ error.stack.split(/\n/).slice(0, verbose ? -1 : 5).join('\n') :
16
+ error.message;
17
+ const subObject: Record<string, unknown> = {};
18
+ for (const key of TypedObject.keys(error)) {
19
+ if (key !== 'stack' && key !== 'message' && key !== 'name') {
20
+ subObject[key] = error[key];
21
+ }
22
+ }
23
+ return `${stack}${Object.keys(subObject).length ? `\n${util.inspect(subObject)}` : ''}`;
24
+ } else {
25
+ return `${error}`;
26
+ }
27
+ }
28
+ }
@@ -7,6 +7,7 @@ import { RuntimeIndex } from '@travetto/runtime';
7
7
  import type { TestEvent } from '../../model/event.ts';
8
8
  import type { SuitesSummary, TestConsumerShape } from '../types.ts';
9
9
  import { TestConsumer } from '../decorator.ts';
10
+ import { TestConsumerUtil } from './util.ts';
10
11
 
11
12
  /**
12
13
  * Xunit consumer, compatible with JUnit formatters
@@ -59,9 +60,9 @@ export class XunitEmitter implements TestConsumerShape {
59
60
  let body = '';
60
61
 
61
62
  if (test.error) {
62
- const assertion = test.assertions.find(item => !!item.error)!;
63
+ const errorMessage = TestConsumerUtil.errorToString(test.error);
63
64
  const node = test.status === 'failed' ? 'failure' : 'error';
64
- body = `<${node} type="${assertion.text}" message="${encodeURIComponent(assertion.message!)}"><![CDATA[${assertion.error!.stack}]]></${node}>`;
65
+ body = `<${node} type="${test.error.constructor.name}" message="${encodeURIComponent(test.error.message)}"><![CDATA[${errorMessage}]]></${node}>`;
65
66
  }
66
67
 
67
68
  const groupedByLevel: Record<string, string[]> = {};
@@ -31,15 +31,13 @@ export class TestExecutor {
31
31
  *
32
32
  * This method should never throw under any circumstances.
33
33
  */
34
- async #executeTestMethod(test: TestConfig): Promise<Error | undefined> {
35
- const suite = SuiteRegistryIndex.getConfig(test.class);
36
-
34
+ async #executeTestMethod(instance: unknown, test: TestConfig): Promise<Error | undefined> {
37
35
  // Ensure all the criteria below are satisfied before moving forward
38
36
  return Barrier.awaitOperation(test.timeout || TEST_TIMEOUT, async () => {
39
37
  const env = process.env;
40
38
  process.env = { ...env }; // Created an isolated environment
41
39
  try {
42
- await castTo<Record<string, Function>>(suite.instance)[test.methodName]();
40
+ await castTo<Record<string, Function>>(instance)[test.methodName]();
43
41
  } finally {
44
42
  process.env = env; // Restore
45
43
  }
@@ -58,7 +56,7 @@ export class TestExecutor {
58
56
  /**
59
57
  * Execute the test, capture output, assertions and promises
60
58
  */
61
- async executeTest(test: TestConfig, suite: SuiteConfig, override?: Partial<TestResult>): Promise<TestResult> {
59
+ async executeTest(instance: unknown, test: TestConfig, suite: SuiteConfig, override?: Partial<TestResult>): Promise<TestResult> {
62
60
 
63
61
  const result = TestModelUtil.createTestResult(suite, test, override);
64
62
 
@@ -81,7 +79,7 @@ export class TestExecutor {
81
79
  } else {
82
80
  // Run method and get result
83
81
  const startTime = Date.now();
84
- const error = await this.#executeTestMethod(test);
82
+ const error = await this.#executeTestMethod(instance, test);
85
83
  const [status, finalError] = AssertCheck.validateTestResultError(test, error);
86
84
  result.status = status;
87
85
  result.selfDuration = Date.now() - startTime;
@@ -104,9 +102,9 @@ export class TestExecutor {
104
102
  */
105
103
  async executeSuite(suite: SuiteConfig, tests: TestConfig[]): Promise<void> {
106
104
 
107
- suite.instance = classConstruct(suite.class);
105
+ const instance = classConstruct(suite.class);
108
106
 
109
- const shouldSkip = await this.#shouldSkip(suite, suite.instance);
107
+ const shouldSkip = await this.#shouldSkip(suite, instance);
110
108
 
111
109
  const result: SuiteResult = TestModelUtil.createSuiteResult(suite);
112
110
 
@@ -126,7 +124,7 @@ export class TestExecutor {
126
124
  return;
127
125
  }
128
126
 
129
- const manager = new TestPhaseManager(suite);
127
+ const manager = new TestPhaseManager(suite, instance);
130
128
  const originalEnv = { ...process.env };
131
129
  const startTime = Date.now();
132
130
  const testResultOverrides: Record<string, Partial<TestResult>> = {};
@@ -158,7 +156,7 @@ export class TestExecutor {
158
156
  const testStart = Date.now();
159
157
  const testResultOverride = (testResultOverrides[test.methodName] ??= {});
160
158
 
161
- if (await this.#shouldSkip(test, suite.instance)) {
159
+ if (await this.#shouldSkip(test, instance)) {
162
160
  testResultOverride.status = 'skipped';
163
161
  }
164
162
 
@@ -172,7 +170,7 @@ export class TestExecutor {
172
170
  }
173
171
 
174
172
  // Run test
175
- const testResult = await this.executeTest(test, suite, testResultOverride);
173
+ const testResult = await this.executeTest(instance, test, suite, testResultOverride);
176
174
 
177
175
  // Handle after each
178
176
  try {
@@ -13,9 +13,11 @@ const TEST_PHASE_TIMEOUT = TimeUtil.duration(Env.TRV_TEST_PHASE_TIMEOUT.value ??
13
13
  export class TestPhaseManager {
14
14
  #progress: ('all' | 'each')[] = [];
15
15
  #suite: SuiteConfig;
16
+ #instance: unknown;
16
17
 
17
- constructor(suite: SuiteConfig) {
18
+ constructor(suite: SuiteConfig, instance: unknown) {
18
19
  this.#suite = suite;
20
+ this.#instance = instance;
19
21
  }
20
22
 
21
23
  /**
@@ -29,7 +31,7 @@ export class TestPhaseManager {
29
31
  }
30
32
 
31
33
  // Ensure all the criteria below are satisfied before moving forward
32
- error = await Barrier.awaitOperation(TEST_PHASE_TIMEOUT, async () => handler[phase]?.(this.#suite.instance));
34
+ error = await Barrier.awaitOperation(TEST_PHASE_TIMEOUT, async () => handler[phase]?.(this.#instance));
33
35
 
34
36
  if (error) {
35
37
  throw error;
@@ -3,7 +3,7 @@ import fs from 'node:fs/promises';
3
3
  import readline from 'node:readline/promises';
4
4
  import path from 'node:path';
5
5
 
6
- import { Env, ExecUtil, Util, RuntimeIndex, Runtime, TimeUtil, JSONUtil } from '@travetto/runtime';
6
+ import { Env, ExecUtil, Util, RuntimeIndex, Runtime, TimeUtil, JSONUtil, describeFunction } from '@travetto/runtime';
7
7
  import { WorkPool } from '@travetto/worker';
8
8
  import { Registry } from '@travetto/registry';
9
9
 
@@ -126,6 +126,7 @@ export class RunUtil {
126
126
  const imported = await Registry.manualInit([importPath]);
127
127
  const classes = Object.fromEntries(
128
128
  imported
129
+ .filter(cls => !describeFunction(cls).abstract)
129
130
  .filter(cls => SuiteRegistryIndex.hasConfig(cls))
130
131
  .map(cls => [cls.Ⲑid, SuiteRegistryIndex.getConfig(cls)])
131
132
  );
@@ -19,10 +19,6 @@ export interface SuiteConfig extends SuiteCore {
19
19
  * Should this be skipped
20
20
  */
21
21
  skip: Skip;
22
- /**
23
- * Actual class instance
24
- */
25
- instance?: unknown;
26
22
  /**
27
23
  * Tests to run
28
24
  */