@testspectra/cli 1.1.8-rc.29 → 1.1.8-rc.30

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.
@@ -1,4 +1,4 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { describe, expect, it, vi } from 'vitest';
2
2
  import { isVitestNoise, sanitizeErrorMessage } from '@testspectra/react';
3
3
  import { Reporter } from '../../runner/reporter.js';
4
4
  describe('spectra test error streaming and formatting', () => {
@@ -40,13 +40,46 @@ describe('spectra test error streaming and formatting', () => {
40
40
  expect(result.passedCount).toBe(0);
41
41
  expect(result.status).toBe('failed');
42
42
  });
43
- it('guarantees failedCount is at least 1 when test run fails without test events', () => {
43
+ it('prints error message below test title and steps on failure', () => {
44
44
  const reporter = new Reporter();
45
- const ok = false;
46
- const result = reporter.generateResult(ok ? 'passed' : 'failed', '0.1s');
47
- const failedCount = !ok && result.failedCount === 0 ? 1 : result.failedCount;
48
- const totalTests = result.passedCount + failedCount;
49
- expect(failedCount).toBe(1);
50
- expect(totalTests).toBe(1);
45
+ const consoleLogs = [];
46
+ const spy = vi.spyOn(console, 'log').mockImplementation((msg) => {
47
+ if (typeof msg === 'string')
48
+ consoleLogs.push(msg);
49
+ });
50
+ reporter.addLog({
51
+ timestamp: '12:00:00',
52
+ level: 'INFO',
53
+ message: '[TESTSPECTRA_TEST_START] {"title":"ApiInterception / TC-0009-post-mocking"}',
54
+ });
55
+ reporter.addLog({
56
+ timestamp: '12:00:00',
57
+ level: 'INFO',
58
+ message: '[TESTSPECTRA_STEP_PASS] {"type":"action","key":"navigate","args":["/#api-post"],"test":"ApiInterception / TC-0009-post-mocking"} (8ms)',
59
+ });
60
+ reporter.addLog({
61
+ timestamp: '12:00:00',
62
+ level: 'INFO',
63
+ message: '[TESTSPECTRA_STEP_FAIL] {"type":"assertion","target":"#post-status-badge","args":["201 Created"],"test":"ApiInterception / TC-0009-post-mocking"} (2005ms)',
64
+ });
65
+ reporter.addLog({
66
+ timestamp: '12:00:00',
67
+ level: 'INFO',
68
+ message: '[TESTSPECTRA_TEST_ERROR] Expected element "#post-status-badge" to have text "201 Created", but got ""',
69
+ });
70
+ reporter.addLog({
71
+ timestamp: '12:00:00',
72
+ level: 'INFO',
73
+ message: '[TESTSPECTRA_TEST_FAIL] ApiInterception / TC-0009-post-mocking (2066ms)',
74
+ });
75
+ spy.mockRestore();
76
+ const titleIndex = consoleLogs.findIndex((log) => log.includes('ApiInterception / TC-0009-post-mocking'));
77
+ const stepPassIndex = consoleLogs.findIndex((log) => log.includes('Navigate to'));
78
+ const stepFailIndex = consoleLogs.findIndex((log) => log.includes('#post-status-badge'));
79
+ const errorIndex = consoleLogs.findIndex((log) => log.includes('↳ Error:'));
80
+ expect(titleIndex).toBeGreaterThanOrEqual(0);
81
+ expect(stepPassIndex).toBeGreaterThan(titleIndex);
82
+ expect(stepFailIndex).toBeGreaterThan(stepPassIndex);
83
+ expect(errorIndex).toBeGreaterThan(stepFailIndex);
51
84
  });
52
85
  });
@@ -29,20 +29,32 @@ export class RustCoreBridge {
29
29
  const binaryName = isWindows ? 'testspectra-runner.exe' : 'testspectra-runner';
30
30
  // In local monorepo development (where core/Cargo.toml exists), prioritize the locally-built
31
31
  // binary in core/target/release or cli/bin over any stale optionalDependency in node_modules.
32
- const workspaceRoot = path.resolve(__dirname, '../../..');
33
- const isMonorepo = fs.existsSync(path.join(workspaceRoot, 'core/Cargo.toml'));
32
+ let workspaceRoot = null;
33
+ let curr = __dirname;
34
+ while (curr !== path.dirname(curr)) {
35
+ if (fs.existsSync(path.join(curr, 'core/Cargo.toml')) || fs.existsSync(path.join(curr, 'pnpm-workspace.yaml'))) {
36
+ workspaceRoot = curr;
37
+ break;
38
+ }
39
+ curr = path.dirname(curr);
40
+ }
34
41
  const candidatePaths = [
35
- path.join(workspaceRoot, 'core/target/release', binaryName),
36
- path.join(workspaceRoot, 'target/release', binaryName),
37
- path.join(workspaceRoot, 'cli/bin', binaryName),
42
+ ...(workspaceRoot
43
+ ? [
44
+ path.join(workspaceRoot, 'core/target/release', binaryName),
45
+ path.join(workspaceRoot, 'target/release', binaryName),
46
+ path.join(workspaceRoot, 'cli/bin', binaryName),
47
+ path.join(workspaceRoot, 'core/cli/target/release', binaryName),
48
+ path.join(workspaceRoot, 'core/target/debug', binaryName),
49
+ path.join(workspaceRoot, 'target/debug', binaryName),
50
+ path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
51
+ ]
52
+ : []),
53
+ path.join(__dirname, '../bin', binaryName),
38
54
  path.join(__dirname, '../../bin', binaryName),
39
- path.join(workspaceRoot, 'core/cli/target/release', binaryName),
40
- path.join(workspaceRoot, 'core/target/debug', binaryName),
41
- path.join(workspaceRoot, 'target/debug', binaryName),
42
- path.join(workspaceRoot, 'core/cli/target/debug', binaryName),
43
55
  path.join(__dirname, '../../bin/testspectra-runner'),
44
56
  ];
45
- if (isMonorepo) {
57
+ if (workspaceRoot) {
46
58
  for (const p of candidatePaths) {
47
59
  if (fs.existsSync(p)) {
48
60
  return p;
@@ -35,6 +35,7 @@ export declare class Reporter {
35
35
  private networkEvents;
36
36
  private pendingTests;
37
37
  private workerToTest;
38
+ private pendingErrorMsg?;
38
39
  private passedCount;
39
40
  private failedCount;
40
41
  private isInteractive;
@@ -155,6 +155,7 @@ export class Reporter {
155
155
  networkEvents = [];
156
156
  pendingTests = new Map();
157
157
  workerToTest = new Map();
158
+ pendingErrorMsg;
158
159
  passedCount = 0;
159
160
  failedCount = 0;
160
161
  isInteractive;
@@ -358,11 +359,7 @@ export class Reporter {
358
359
  latest.errorMsg = latest.errorMsg ? `${latest.errorMsg}\n${err}` : err;
359
360
  }
360
361
  else {
361
- const errLines = err.split('\n');
362
- console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errLines[0]}\x1b[0m`);
363
- for (let i = 1; i < errLines.length; i++) {
364
- console.log(` \x1b[38;2;248;113;113m${errLines[i]}\x1b[0m`);
365
- }
362
+ this.pendingErrorMsg = this.pendingErrorMsg ? `${this.pendingErrorMsg}\n${err}` : err;
366
363
  }
367
364
  return;
368
365
  }
@@ -398,20 +395,14 @@ export class Reporter {
398
395
  this.failedCount++;
399
396
  const testCase = this.pendingTests.get(title) || this.resolveTestCase(title);
400
397
  const steps = testCase?.steps || [];
401
- const errorMsg = testCase?.errorMsg;
398
+ const errorMsg = testCase?.errorMsg || this.pendingErrorMsg;
399
+ this.pendingErrorMsg = undefined;
402
400
  const workerId = testCase?.workerId;
403
401
  this.pendingTests.delete(title);
404
402
  if (workerId !== undefined) {
405
403
  this.workerToTest.delete(workerId);
406
404
  }
407
405
  this.clearActiveSlots();
408
- if (errorMsg) {
409
- const errLines = errorMsg.split('\n');
410
- console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errLines[0]}\x1b[0m`);
411
- for (let i = 1; i < errLines.length; i++) {
412
- console.log(` \x1b[38;2;248;113;113m${errLines[i]}\x1b[0m`);
413
- }
414
- }
415
406
  console.log(`${chalk.hex('#f87171')('✗')} ${chalk.white.bold(title)} ${chalk.gray(`(${duration}ms)`)}`);
416
407
  for (const step of steps) {
417
408
  if (step.passed) {
@@ -421,6 +412,13 @@ export class Reporter {
421
412
  console.log(` ${chalk.red('✖')} ${chalk.red(step.text)} ${chalk.gray(`(${step.duration}ms)`)}`);
422
413
  }
423
414
  }
415
+ if (errorMsg) {
416
+ const errLines = errorMsg.split('\n');
417
+ console.log(` \x1b[38;2;251;191;36m↳ Error:\x1b[0m \x1b[38;2;248;113;113m${errLines[0]}\x1b[0m`);
418
+ for (let i = 1; i < errLines.length; i++) {
419
+ console.log(` \x1b[38;2;248;113;113m${errLines[i]}\x1b[0m`);
420
+ }
421
+ }
424
422
  console.log('');
425
423
  this.renderActiveSlots();
426
424
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testspectra/cli",
3
- "version": "1.1.8-rc.29",
3
+ "version": "1.1.8-rc.30",
4
4
  "description": "TestSpectra Cross-Platform Test Runner CLI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,9 +26,9 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@clack/prompts": "^1.7.0",
29
- "@testspectra/matchers": "^1.1.8-rc.29",
30
- "@testspectra/react": "^1.1.8-rc.29",
31
- "@testspectra/skills": "^1.1.8-rc.29",
29
+ "@testspectra/matchers": "^1.1.8-rc.30",
30
+ "@testspectra/react": "^1.1.8-rc.30",
31
+ "@testspectra/skills": "^1.1.8-rc.30",
32
32
  "chalk": "^5.3.0",
33
33
  "commander": "^12.1.0",
34
34
  "dotenv": "^16.4.5",
@@ -38,9 +38,9 @@
38
38
  "zod": "^3.23.8"
39
39
  },
40
40
  "optionalDependencies": {
41
- "@testspectra/cli-darwin-arm64": "1.1.8-rc.29",
42
- "@testspectra/cli-linux-x64": "1.1.8-rc.29",
43
- "@testspectra/cli-win32-x64": "1.1.8-rc.29"
41
+ "@testspectra/cli-darwin-arm64": "1.1.8-rc.30",
42
+ "@testspectra/cli-linux-x64": "1.1.8-rc.30",
43
+ "@testspectra/cli-win32-x64": "1.1.8-rc.30"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^20.14.0",
@@ -17,8 +17,8 @@
17
17
  "postinstall": "spectra sync-types"
18
18
  },
19
19
  "devDependencies": {
20
- "@testspectra/cli": "^1.1.8-rc.29",
21
- "@testspectra/matchers": "^1.1.8-rc.29",
20
+ "@testspectra/cli": "^1.1.8-rc.30",
21
+ "@testspectra/matchers": "^1.1.8-rc.30",
22
22
  "@types/node": "^20.14.0",
23
23
  "@wdio/cli": "^9.2.8",
24
24
  "@wdio/local-runner": "^9.2.8",
@@ -12,8 +12,8 @@
12
12
  "postinstall": "spectra sync-types"
13
13
  },
14
14
  "devDependencies": {
15
- "@testspectra/cli": "^1.1.8-rc.29",
16
- "@testspectra/matchers": "^1.1.8-rc.29",
15
+ "@testspectra/cli": "^1.1.8-rc.30",
16
+ "@testspectra/matchers": "^1.1.8-rc.30",
17
17
  "@types/node": "^20.14.0",
18
18
  "@wdio/cli": "^9.2.8",
19
19
  "@wdio/local-runner": "^9.2.8",
@@ -14,9 +14,9 @@
14
14
  "postinstall": "spectra sync-types"
15
15
  },
16
16
  "devDependencies": {
17
- "@testspectra/cli": "^1.1.8-rc.29",
18
- "@testspectra/matchers": "^1.1.8-rc.29",
19
- "@testspectra/react": "^1.1.8-rc.29",
17
+ "@testspectra/cli": "^1.1.8-rc.30",
18
+ "@testspectra/matchers": "^1.1.8-rc.30",
19
+ "@testspectra/react": "^1.1.8-rc.30",
20
20
  "@types/node": "^20.14.0",
21
21
  "@types/react": "^18.3.3",
22
22
  "@types/react-dom": "^18.3.0",