sxy-test-runner 2.4.6 → 2.4.7

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/AGENTS.md CHANGED
@@ -467,6 +467,10 @@ Widening `watch.watchFiles` (for example to `**/*.{js,json,env}`) closes the fir
467
467
  it matters, at the cost of more reruns.
468
468
 
469
469
  Diffs, logs and stack traces are not in the agent stream; errors are reduced to one line.
470
+ Where an assertion's message spans several lines - `node:assert` puts only a preamble on the
471
+ first, with the values below - the compared values are appended, so the line still says what
472
+ went wrong: `AssertionError: Expected values to be strictly equal: 1 !== 2`. Long values are
473
+ truncated to keep it to one line; the terminal keeps the full diff.
470
474
 
471
475
  In `once`, read the process's exit code for pass/fail and the `status` on `done` for what
472
476
  kind of failure it was - `status=failed` accompanies exit `20`. `watching` and `change`
@@ -1,3 +1,4 @@
1
+ import { inspect } from 'node:util';
1
2
  export function failureEntries(testResults, erroringTests = {}) {
2
3
  const entries = [];
3
4
  for (const [file, test] of Object.entries(erroringTests)) {
@@ -31,11 +32,31 @@ function simplifyError(error) {
31
32
  return 'unknown error';
32
33
  if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
33
34
  const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error';
34
- return `${name}: ${firstLine(error.message)}`;
35
+ return `${name}: ${firstLine(error.message)}${comparison(error, error.message)}`;
35
36
  }
36
37
  // eslint-disable-next-line @typescript-eslint/no-base-to-string -- only a fallback
37
38
  return firstLine(String(error));
38
39
  }
40
+ // node:assert's first line is only a preamble - "Expected values to be strictly equal:" -
41
+ // with the compared values on the lines below, so the first line alone says nothing about
42
+ // what went wrong. The values are on the error, so put them back. Chai fits its whole
43
+ // message on one line and is left alone.
44
+ function comparison(error, message) {
45
+ if (!message.includes('\n'))
46
+ return '';
47
+ if (!('actual' in error) && !('expected' in error))
48
+ return '';
49
+ const actual = ('actual' in error) ? summarise(error.actual) : '?';
50
+ const expected = ('expected' in error) ? summarise(error.expected) : '?';
51
+ return ` ${actual} !== ${expected}`;
52
+ }
53
+ // one line and short: these lines are read in a status file or an agent stream, where a
54
+ // pretty-printed object would swamp everything around it. The terminal still has the full diff.
55
+ function summarise(value) {
56
+ const text = inspect(value, { depth: 2, breakLength: Infinity })
57
+ .replace(/\s*[\r\n]+\s*/g, ' ');
58
+ return (text.length > 60) ? `${text.slice(0, 60)}...` : text;
59
+ }
39
60
  function firstLine(text) {
40
61
  return text.split('\n')[0]?.trim() ?? '';
41
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"failureEntries.js","sourceRoot":"","sources":["../../../src/cli/lib/failureEntries.ts"],"names":[],"mappings":"AAYA,MAAM,UAAU,cAAc,CAC1B,WAAyB,EACzB,gBAA0C,EAAE;IAE5C,MAAM,OAAO,GAAmB,EAAE,CAAA;IAElC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,iBAAiB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACnC,KAAK,MAAM,cAAc,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;YACtD,IAAI,cAAc,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBACrC,OAAO,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,QAAQ,EAAE,cAAc,CAAC,KAAK;oBAC9B,KAAK,EAAE,UAAU,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE;iBACzD,CAAC,CAAA;YACN,CAAC;YAED,KAAK,MAAM,QAAQ,IAAI,cAAc,CAAC,SAAS,EAAE,CAAC;gBAC9C,IAAI,QAAQ,CAAC,OAAO,KAAK,KAAK;oBAAE,SAAQ;gBACxC,OAAO,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,UAAU,CAAC,IAAI;oBACrB,QAAQ,EAAE,cAAc,CAAC,KAAK;oBAC9B,IAAI,EAAE,QAAQ,CAAC,MAAM;oBACrB,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;iBACvC,CAAC,CAAA;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAA;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,eAAe,CAAA;IAEpF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvF,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAA;QACvF,OAAO,GAAG,IAAI,KAAK,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAA;IACjD,CAAC;IAED,mFAAmF;IACnF,OAAO,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AACnC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAC5C,CAAC","sourcesContent":["import type { TestFile, TestResult } from '../../types.js'\n\n// One failure, flattened. `describe` is absent when a file would not load, and `test` is\n// absent when the describe itself threw. Shared by the status file's nested tree and the\n// agent reporter's flat lines, so both describe a failure the same way.\nexport interface FailureEntry {\n file: string\n describe?: string\n test?: string\n error: string\n}\n\nexport function failureEntries(\n testResults: TestResult[],\n erroringTests: Record<string, TestFile> = {}\n): FailureEntry[] {\n const entries: FailureEntry[] = []\n\n for (const [file, test] of Object.entries(erroringTests)) {\n entries.push({ file, error: `did not load: ${simplifyError(test.error)}` })\n }\n\n for (const testResult of testResults) {\n for (const describeResult of testResult.describeResults) {\n if (describeResult.error !== undefined) {\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n error: `threw: ${simplifyError(describeResult.error)}`\n })\n }\n\n for (const itResult of describeResult.itResults) {\n if (itResult.success !== false) continue\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n test: itResult.should,\n error: simplifyError(itResult.error)\n })\n }\n }\n }\n\n return entries\n}\n\nfunction simplifyError(error: unknown): string {\n if (error === undefined || error === null || error === false) return 'unknown error'\n\n if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error'\n return `${name}: ${firstLine(error.message)}`\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- only a fallback\n return firstLine(String(error))\n}\n\nfunction firstLine(text: string): string {\n return text.split('\\n')[0]?.trim() ?? ''\n}\n"]}
1
+ {"version":3,"file":"failureEntries.js","sourceRoot":"","sources":["../../../src/cli/lib/failureEntries.ts"],"names":[],"mappings":"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","sourcesContent":["import { inspect } from 'node:util'\n\nimport type { TestFile, TestResult } from '../../types.js'\n\n// One failure, flattened. `describe` is absent when a file would not load, and `test` is\n// absent when the describe itself threw. Shared by the status file's nested tree and the\n// agent reporter's flat lines, so both describe a failure the same way.\nexport interface FailureEntry {\n file: string\n describe?: string\n test?: string\n error: string\n}\n\nexport function failureEntries(\n testResults: TestResult[],\n erroringTests: Record<string, TestFile> = {}\n): FailureEntry[] {\n const entries: FailureEntry[] = []\n\n for (const [file, test] of Object.entries(erroringTests)) {\n entries.push({ file, error: `did not load: ${simplifyError(test.error)}` })\n }\n\n for (const testResult of testResults) {\n for (const describeResult of testResult.describeResults) {\n if (describeResult.error !== undefined) {\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n error: `threw: ${simplifyError(describeResult.error)}`\n })\n }\n\n for (const itResult of describeResult.itResults) {\n if (itResult.success !== false) continue\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n test: itResult.should,\n error: simplifyError(itResult.error)\n })\n }\n }\n }\n\n return entries\n}\n\nfunction simplifyError(error: unknown): string {\n if (error === undefined || error === null || error === false) return 'unknown error'\n\n if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error'\n return `${name}: ${firstLine(error.message)}${comparison(error, error.message)}`\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- only a fallback\n return firstLine(String(error))\n}\n\n// node:assert's first line is only a preamble - \"Expected values to be strictly equal:\" -\n// with the compared values on the lines below, so the first line alone says nothing about\n// what went wrong. The values are on the error, so put them back. Chai fits its whole\n// message on one line and is left alone.\nfunction comparison(error: object, message: string): string {\n if (!message.includes('\\n')) return ''\n if (!('actual' in error) && !('expected' in error)) return ''\n\n const actual = ('actual' in error) ? summarise(error.actual) : '?'\n const expected = ('expected' in error) ? summarise(error.expected) : '?'\n return ` ${actual} !== ${expected}`\n}\n\n// one line and short: these lines are read in a status file or an agent stream, where a\n// pretty-printed object would swamp everything around it. The terminal still has the full diff.\nfunction summarise(value: unknown): string {\n const text = inspect(value, { depth: 2, breakLength: Infinity })\n .replace(/\\s*[\\r\\n]+\\s*/g, ' ')\n return (text.length > 60) ? `${text.slice(0, 60)}...` : text\n}\n\nfunction firstLine(text: string): string {\n return text.split('\\n')[0]?.trim() ?? ''\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sxy-test-runner",
3
- "version": "2.4.6",
3
+ "version": "2.4.7",
4
4
  "license": "MIT",
5
5
  "homepage": "https://github.com/RobertSandiford/sxy-test-runner",
6
6
  "repository": {