sxy-test-runner 2.3.20 → 2.4.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/AGENTS.md +105 -6
- package/dist/cli/help.js +1 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/init.js +3 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/lib/agentReport.d.ts +16 -0
- package/dist/cli/lib/agentReport.js +117 -0
- package/dist/cli/lib/agentReport.js.map +1 -0
- package/dist/cli/lib/defaultConfig.js +2 -1
- package/dist/cli/lib/defaultConfig.js.map +1 -1
- package/dist/cli/lib/failureEntries.d.ts +9 -0
- package/dist/cli/lib/failureEntries.js +42 -0
- package/dist/cli/lib/failureEntries.js.map +1 -0
- package/dist/cli/lib/failureLines.js +23 -33
- package/dist/cli/lib/failureLines.js.map +1 -1
- package/dist/cli/lib/listenToUser.d.ts +2 -1
- package/dist/cli/lib/listenToUser.js +6 -3
- package/dist/cli/lib/listenToUser.js.map +1 -1
- package/dist/cli/lib/reRunManager.d.ts +2 -1
- package/dist/cli/lib/reRunManager.js +9 -2
- package/dist/cli/lib/reRunManager.js.map +1 -1
- package/dist/cli/lib/runTests.js +14 -5
- package/dist/cli/lib/runTests.js.map +1 -1
- package/dist/cli/lib/validateConfig.js +3 -0
- package/dist/cli/lib/validateConfig.js.map +1 -1
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js +24 -23
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js.map +1 -1
- package/dist/cli/once.js +3 -0
- package/dist/cli/once.js.map +1 -1
- package/dist/cli/watch.js +33 -13
- package/dist/cli/watch.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -85,6 +85,12 @@ npx sxy-test-runner once --filter-describe "users"
|
|
|
85
85
|
# Run tests whose names contain "creates a user"
|
|
86
86
|
npx sxy-test-runner once --filter-it "creates a user"
|
|
87
87
|
|
|
88
|
+
# Watch mode, reporting each run as single agent readable lines
|
|
89
|
+
npx sxy-test-runner watch --agent
|
|
90
|
+
|
|
91
|
+
# Watch mode, recording each run's result where another process can read it
|
|
92
|
+
npx sxy-test-runner watch --status-file .sxyt-status.txt
|
|
93
|
+
|
|
88
94
|
# Generate an initial config
|
|
89
95
|
npx sxy-test-runner init
|
|
90
96
|
```
|
|
@@ -95,7 +101,13 @@ configured pattern. Both options are resolved relative to `testsBase` and can be
|
|
|
95
101
|
|
|
96
102
|
`--filter-describe` and `--filter-it` apply case-insensitive substring filters to describe
|
|
97
103
|
and test names. They work in both watch and `once` modes and can be combined. If the
|
|
98
|
-
filters select no runnable tests, `once` exits with code `4
|
|
104
|
+
filters select no runnable tests, `once` exits with code `5` - `4` means no test file
|
|
105
|
+
matched the `tests` pattern in the first place.
|
|
106
|
+
|
|
107
|
+
`--agent` and `--status-file` are watch-only, and are the two ways to follow a watch session
|
|
108
|
+
from outside it: `--agent` streams each run to stdout as single lines, `--status-file` writes
|
|
109
|
+
the latest run's result to a file. Both are covered under "Watch mode" below. `once` ignores
|
|
110
|
+
them - it already ends with an exit code.
|
|
99
111
|
|
|
100
112
|
Describe and test filters run after the selected test files have loaded. The runner gives
|
|
101
113
|
each test file a fresh ESM instance and propagates it through the file's static dependency
|
|
@@ -233,9 +245,14 @@ on parallel `it` execution.
|
|
|
233
245
|
|
|
234
246
|
Watch mode builds a static dependency tree for every test. When a file changes, it reruns
|
|
235
247
|
the changed test itself and tests whose static import graph includes that file. If
|
|
236
|
-
`watch.reRunFailingTests` is true,
|
|
237
|
-
|
|
238
|
-
|
|
248
|
+
`watch.reRunFailingTests` is true (the default), test files that failed earlier in the
|
|
249
|
+
session are included in every run until they pass, so a failure stays visible instead of
|
|
250
|
+
disappearing the moment an unrelated file is edited. Keyboard commands normally require
|
|
251
|
+
Enter; set `watch.ttyActionsOnKeypress` to `true` to trigger `r`, `l`, `a`, and `f`
|
|
252
|
+
immediately on keypress in a TTY session.
|
|
253
|
+
|
|
254
|
+
Changes that arrive while a run is in flight are folded into the next run rather than
|
|
255
|
+
queueing a run each, so a burst of edits produces one rerun covering all of them.
|
|
239
256
|
|
|
240
257
|
Important limitations:
|
|
241
258
|
|
|
@@ -247,7 +264,8 @@ Important limitations:
|
|
|
247
264
|
process. Keep mutable access sequential or provide application-level synchronization,
|
|
248
265
|
and arrange explicit cleanup for resources such as servers and database connections.
|
|
249
266
|
- Use `once` instead of watch mode for autonomous agent verification so the command
|
|
250
|
-
terminates and returns a meaningful status.
|
|
267
|
+
terminates and returns a meaningful status. To follow someone else's long running watch
|
|
268
|
+
session instead, see "Following a watch session as an agent" below.
|
|
251
269
|
|
|
252
270
|
### Watching someone else's watch session
|
|
253
271
|
|
|
@@ -290,6 +308,86 @@ than the code under test. Watch mode writes `-1` on startup, so a status from a
|
|
|
290
308
|
session is never read as current, and each write is renamed into place, so a poller only
|
|
291
309
|
ever sees a complete status.
|
|
292
310
|
|
|
311
|
+
### Following a watch session as an agent
|
|
312
|
+
|
|
313
|
+
`--agent` (or `watch.agent` in the config) replaces watch mode's usual output with one line
|
|
314
|
+
per event, for an agent reading the process's stdout rather than a terminal. It applies to
|
|
315
|
+
watch mode only - `once` already ends with an exit code, so a config setting it is ignored
|
|
316
|
+
there.
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
[sxyt] init | pid=16744
|
|
320
|
+
[sxyt] found | files=2
|
|
321
|
+
[sxyt] run | n=1 | files=2 | why=startup
|
|
322
|
+
[sxyt] fail | n=1 | file=tests/users.test.js | describe=creating a user | test=rejects a duplicate email | error=AssertionError: expected 409 to equal 200
|
|
323
|
+
[sxyt] done | n=1 | code=1 | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=150
|
|
324
|
+
[sxyt] watching | files=2
|
|
325
|
+
[sxyt] change | file=src/orders.js | event=update
|
|
326
|
+
[sxyt] run | n=2 | files=2 | why=auto
|
|
327
|
+
[sxyt] fail | n=2 | file=tests/users.test.js | describe=creating a user | test=rejects a duplicate email | error=AssertionError: expected 409 to equal 200
|
|
328
|
+
[sxyt] done | n=2 | code=1 | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=169
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The grammar is `[sxyt] <event>` followed by ` | <key>=<value>` pairs. Values are never
|
|
332
|
+
quoted: newlines in free text are collapsed to spaces and any `|` becomes `/`, so one event
|
|
333
|
+
is always exactly one line and a field boundary is always a real one.
|
|
334
|
+
|
|
335
|
+
| event | meaning |
|
|
336
|
+
| --- | --- |
|
|
337
|
+
| `init` | the process is up, before the config is read - proof it started at all |
|
|
338
|
+
| `found` | test discovery finished |
|
|
339
|
+
| `run` | a run has started. `why` is `startup`, `auto` (a file changed), or `all`/`last`/`failed` from a keyboard command |
|
|
340
|
+
| `fail` | one failure. `describe` is absent when a file would not load, `test` when the describe itself threw |
|
|
341
|
+
| `done` | the run finished |
|
|
342
|
+
| `change` | a watched file changed |
|
|
343
|
+
| `watching` | startup is complete and the watcher is live |
|
|
344
|
+
|
|
345
|
+
`n` ties a run's lines together. Failures are emitted before their `done`, so `done` is a
|
|
346
|
+
complete end-of-run marker: wait for it and everything about that run has already arrived.
|
|
347
|
+
|
|
348
|
+
`code` on `done` is the same status code the status file uses, so `0` is a pass and `2`
|
|
349
|
+
still means the run died partway rather than merely failing.
|
|
350
|
+
|
|
351
|
+
`covers` on the `run` line says what the run was built from, and so how far its result
|
|
352
|
+
generalises:
|
|
353
|
+
|
|
354
|
+
| covers | the run contained |
|
|
355
|
+
| --- | --- |
|
|
356
|
+
| `all` | every discovered test file - the startup run, or `a` from the keyboard |
|
|
357
|
+
| `changed+failing` | the changed files and every file still failing (`watch.reRunFailingTests` on, the default) |
|
|
358
|
+
| `changed` | only the changed files (`watch.reRunFailingTests` off) |
|
|
359
|
+
| `last` | the previous run's file set |
|
|
360
|
+
| `failing` | only the files still failing |
|
|
361
|
+
|
|
362
|
+
**No incremental run can tell you the suite is green.** `code` and the tallies describe
|
|
363
|
+
only the files that run contained. `failing` is broader - it counts the test files still
|
|
364
|
+
known to be failing across the session - but it is still only knowledge from files that
|
|
365
|
+
have actually been run, and a run happens only when the watcher both notices a change and
|
|
366
|
+
maps it to a test. Either half can miss:
|
|
367
|
+
|
|
368
|
+
- Nothing is watched but `watch.watchFiles` under `watch.watchFilesBase` - `**/*.js` from
|
|
369
|
+
the project root by default. Editing `.env`, a `.json` fixture, a template, a database
|
|
370
|
+
seed, or anything outside that base changes what the tests do and produces **no run at
|
|
371
|
+
all**. The stream simply stays quiet, and the previous `done` remains the last word while
|
|
372
|
+
silently going stale.
|
|
373
|
+
- Even for a watched file, the changed-to-test mapping is the static import graph, so it
|
|
374
|
+
misses dynamic imports and anything reached through a `._cached_.*` boundary. A test file
|
|
375
|
+
that was neither changed nor already failing is not re-run, so a break in it never reaches
|
|
376
|
+
`failing`.
|
|
377
|
+
|
|
378
|
+
So `failing=0` means "nothing is known to be broken", not "everything passes", and silence
|
|
379
|
+
means "no watched file changed", not "still green". To actually verify the suite, use a run
|
|
380
|
+
with `covers=all` - the startup run, `a` at the keyboard, or a separate `once` - and read
|
|
381
|
+
`code` and `failing` from that. For the ordinary edit-test loop in between, `failing` is the
|
|
382
|
+
right signal: with the default `watch.reRunFailingTests`, a failure keeps being re-run and
|
|
383
|
+
re-reported until it passes.
|
|
384
|
+
|
|
385
|
+
Widening `watch.watchFiles` (for example to `**/*.{js,json,env}`) closes the first gap where
|
|
386
|
+
it matters, at the cost of more reruns.
|
|
387
|
+
|
|
388
|
+
Diffs, logs and stack traces are not in the agent stream; errors are reduced to one line.
|
|
389
|
+
Use `once` when a finite run and an exit code are all that is needed.
|
|
390
|
+
|
|
293
391
|
## Configuration reference
|
|
294
392
|
|
|
295
393
|
The exported config type is available for JavaScript type checking:
|
|
@@ -326,7 +424,8 @@ export default {
|
|
|
326
424
|
runAllOnStartup: true,
|
|
327
425
|
reRunFailingTests: true,
|
|
328
426
|
ttyActionsOnKeypress: false,
|
|
329
|
-
statusFile: false
|
|
427
|
+
statusFile: false,
|
|
428
|
+
agent: false
|
|
330
429
|
}
|
|
331
430
|
}
|
|
332
431
|
```
|
package/dist/cli/help.js
CHANGED
|
@@ -30,6 +30,7 @@ export function help() {
|
|
|
30
30
|
' -d, --filter-describe <substring> Run matching describe blocks',
|
|
31
31
|
' -i, --filter-it <substring> Run matching tests',
|
|
32
32
|
' -s, --status-file <file> Write the latest watch run status to a file',
|
|
33
|
+
' --agent Report a watch session as single agent readable lines',
|
|
33
34
|
'',
|
|
34
35
|
'Watch controls:',
|
|
35
36
|
' r/l + Enter Re-run the last set of tests',
|
package/dist/cli/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,sBAAsB,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,MAAM,UAAU,IAAI;IAChB,MAAM,WAAW,GAAG,eAAe;SAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,eAAe,CAAC;SACnC,IAAI,CAAC,MAAM,CAAC,CAAA;IACjB,MAAM,iBAAiB,GAAG,eAAe;SACpC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC;SACtE,IAAI,CAAC,MAAM,CAAC,CAAA;IAEjB,GAAG,CAAC;QACA,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC;QAC7B,EAAE;QACF,QAAQ;QACR,KAAK,OAAO,oBAAoB;QAChC,KAAK,OAAO,iBAAiB;QAC7B,KAAK,OAAO,OAAO;QACnB,KAAK,OAAO,OAAO;QACnB,EAAE;QACF,WAAW;QACX,iEAAiE;QACjE,2CAA2C;QAC3C,+CAA+C;QAC/C,yBAAyB;QACzB,EAAE;QACF,UAAU;QACV,8DAA8D;QAC9D,iEAAiE;QACjE,0EAA0E;QAC1E,mEAAmE;QACnE,yDAAyD;QACzD,kFAAkF;QAClF,EAAE;QACF,iBAAiB;QACjB,sDAAsD;QACtD,0CAA0C;QAC1C,8CAA8C;QAC9C,qEAAqE;QACrE,EAAE;QACF,WAAW;QACX,KAAK,OAAO,EAAE;QACd,KAAK,OAAO,OAAO;QACnB,KAAK,OAAO,4CAA4C;QACxD,KAAK,OAAO,8BAA8B;QAC1C,KAAK,OAAO,sCAAsC;QAClD,EAAE;QACF,kBAAkB,WAAW,OAAO,iBAAiB,GAAG;QACxD,uBAAuB,OAAO,eAAe,WAAW,GAAG;KAC9D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AACjB,CAAC","sourcesContent":["import chalk from '../packages/chalk.js'\nimport { packageName, cliName, configFileNames, configLocations } from '../config.js'\nimport { out } from '../output.js'\n\nexport function help(): void {\n const configNames = configFileNames\n .map(name => `${name}.{js,cjs,mjs}`)\n .join(' or ')\n const configDirectories = configLocations\n .map(location => location === '' ? 'the project root' : `${location}/`)\n .join(' or ')\n\n out([\n chalk.brightblue(packageName),\n '',\n 'Usage:',\n ` ${cliName} [watch] [options]`,\n ` ${cliName} once [options]`,\n ` ${cliName} init`,\n ` ${cliName} help`,\n '',\n 'Commands:',\n ' watch Run tests and watch for related file changes (default)',\n ' once Run the test suite once and exit',\n ' init Create an initial configuration file',\n ' help Show this help',\n '',\n 'Options:',\n ' -c, --config <file> Use a specific configuration file',\n ' -t, --test-files <glob> Override the configured test pattern',\n ' -f, --filter <glob> Further constrain the configured test pattern',\n ' -d, --filter-describe <substring> Run matching describe blocks',\n ' -i, --filter-it <substring> Run matching tests',\n ' -s, --status-file <file> Write the latest watch run status to a file',\n '',\n 'Watch controls:',\n ' r/l + Enter Re-run the last set of tests',\n ' a + Enter Re-run all tests',\n ' f + Enter Re-run failing tests',\n ' Set watch.ttyActionsOnKeypress to true to run these without Enter',\n '',\n 'Examples:',\n ` ${cliName}`,\n ` ${cliName} once`,\n ` ${cliName} watch --test-files \"**/*.focused.test.js\"`,\n ` ${cliName} once --filter \"**/users/**\"`,\n ` ${cliName} once --config config/sxyt.config.js`,\n '',\n `Configuration: ${configNames} in ${configDirectories}.`,\n `Executable aliases: ${cliName}, sxy-test, ${packageName}.`\n ].join('\\n'))\n}\n"]}
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,sBAAsB,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,MAAM,UAAU,IAAI;IAChB,MAAM,WAAW,GAAG,eAAe;SAC9B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,eAAe,CAAC;SACnC,IAAI,CAAC,MAAM,CAAC,CAAA;IACjB,MAAM,iBAAiB,GAAG,eAAe;SACpC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC;SACtE,IAAI,CAAC,MAAM,CAAC,CAAA;IAEjB,GAAG,CAAC;QACA,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC;QAC7B,EAAE;QACF,QAAQ;QACR,KAAK,OAAO,oBAAoB;QAChC,KAAK,OAAO,iBAAiB;QAC7B,KAAK,OAAO,OAAO;QACnB,KAAK,OAAO,OAAO;QACnB,EAAE;QACF,WAAW;QACX,iEAAiE;QACjE,2CAA2C;QAC3C,+CAA+C;QAC/C,yBAAyB;QACzB,EAAE;QACF,UAAU;QACV,8DAA8D;QAC9D,iEAAiE;QACjE,0EAA0E;QAC1E,mEAAmE;QACnE,yDAAyD;QACzD,kFAAkF;QAClF,4FAA4F;QAC5F,EAAE;QACF,iBAAiB;QACjB,sDAAsD;QACtD,0CAA0C;QAC1C,8CAA8C;QAC9C,qEAAqE;QACrE,EAAE;QACF,WAAW;QACX,KAAK,OAAO,EAAE;QACd,KAAK,OAAO,OAAO;QACnB,KAAK,OAAO,4CAA4C;QACxD,KAAK,OAAO,8BAA8B;QAC1C,KAAK,OAAO,sCAAsC;QAClD,EAAE;QACF,kBAAkB,WAAW,OAAO,iBAAiB,GAAG;QACxD,uBAAuB,OAAO,eAAe,WAAW,GAAG;KAC9D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;AACjB,CAAC","sourcesContent":["import chalk from '../packages/chalk.js'\nimport { packageName, cliName, configFileNames, configLocations } from '../config.js'\nimport { out } from '../output.js'\n\nexport function help(): void {\n const configNames = configFileNames\n .map(name => `${name}.{js,cjs,mjs}`)\n .join(' or ')\n const configDirectories = configLocations\n .map(location => location === '' ? 'the project root' : `${location}/`)\n .join(' or ')\n\n out([\n chalk.brightblue(packageName),\n '',\n 'Usage:',\n ` ${cliName} [watch] [options]`,\n ` ${cliName} once [options]`,\n ` ${cliName} init`,\n ` ${cliName} help`,\n '',\n 'Commands:',\n ' watch Run tests and watch for related file changes (default)',\n ' once Run the test suite once and exit',\n ' init Create an initial configuration file',\n ' help Show this help',\n '',\n 'Options:',\n ' -c, --config <file> Use a specific configuration file',\n ' -t, --test-files <glob> Override the configured test pattern',\n ' -f, --filter <glob> Further constrain the configured test pattern',\n ' -d, --filter-describe <substring> Run matching describe blocks',\n ' -i, --filter-it <substring> Run matching tests',\n ' -s, --status-file <file> Write the latest watch run status to a file',\n ' --agent Report a watch session as single agent readable lines',\n '',\n 'Watch controls:',\n ' r/l + Enter Re-run the last set of tests',\n ' a + Enter Re-run all tests',\n ' f + Enter Re-run failing tests',\n ' Set watch.ttyActionsOnKeypress to true to run these without Enter',\n '',\n 'Examples:',\n ` ${cliName}`,\n ` ${cliName} once`,\n ` ${cliName} watch --test-files \"**/*.focused.test.js\"`,\n ` ${cliName} once --filter \"**/users/**\"`,\n ` ${cliName} once --config config/sxyt.config.js`,\n '',\n `Configuration: ${configNames} in ${configDirectories}.`,\n `Executable aliases: ${cliName}, sxy-test, ${packageName}.`\n ].join('\\n'))\n}\n"]}
|
package/dist/cli/init.js
CHANGED
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,MAAM,sBAAsB,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,MAAM,UAAU,IAAI;IAChB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAEhC,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;QAC3C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC3C,IAAI,GAAG,CAAA;YACP,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,KAAK,CAAC,CAAA;YAC9D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;QACL,CAAC;IACL,CAAC;IAED,SAAS,mBAAmB,CAAC,GAAW;QACpC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAA,IAAI,WAAW,yCAAyC,GAAG,EAAE;cACvE,kEAAkE,OAAO,SAAS,CAAC,CAAA;IAC7F,CAAC;IAED,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,MAAM,CAAA,IAAI,WAAW,2CAA2C;cACpE,gEAAgE;cAChE,iFAAiF,CAAC,CAAA;IAC5F,CAAC;IAED,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAA;IAC1E,mEAAmE;IACnE,MAAM,WAAW,GAAsB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IAEjE,MAAM,gBAAgB,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC;QACpD,aAAa;QACb,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;QACxE,SAAS;QACT,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;IAE7E,MAAM,aAAa,GACvB
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,KAAK,MAAM,sBAAsB,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,MAAM,UAAU,IAAI;IAChB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAEhC,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;QAC3C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC3C,IAAI,GAAG,CAAA;YACP,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,KAAK,CAAC,CAAA;YAC9D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;QACL,CAAC;IACL,CAAC;IAED,SAAS,mBAAmB,CAAC,GAAW;QACpC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAA,IAAI,WAAW,yCAAyC,GAAG,EAAE;cACvE,kEAAkE,OAAO,SAAS,CAAC,CAAA;IAC7F,CAAC;IAED,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,MAAM,CAAA,IAAI,WAAW,2CAA2C;cACpE,gEAAgE;cAChE,iFAAiF,CAAC,CAAA;IAC5F,CAAC;IAED,MAAM,cAAc,GAAG,EAAE,CAAC,YAAY,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAA;IAC1E,mEAAmE;IACnE,MAAM,WAAW,GAAsB,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAA;IAEjE,MAAM,gBAAgB,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC;QACpD,aAAa;QACb,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;QACxE,SAAS;QACT,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;IAE7E,MAAM,aAAa,GACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8FE,CAAA;IAEE,EAAE,CAAC,aAAa,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAA;IAEjD,GAAG,CAAC,KAAK,CAAC,WAAW,CAAA,IAAI,WAAW,sBAAsB,GAAG,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAA;AAC9F,CAAC","sourcesContent":["import { join } from 'path'\nimport chalk from '../packages/chalk.js'\nimport { packageName, cliName, configFileNames, configLocations } from '../config.js'\nimport { out } from '../output.js'\nimport fs from 'fs'\n\nexport function init(): void {\n const projectDir = process.cwd()\n\n for (const configLocation of configLocations) {\n for (const configFileName of configFileNames) {\n let loc\n loc = join(projectDir, configLocation, configFileName + '.js')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.cjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.mjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n }\n }\n\n function configAlreadyExists(loc: string): void {\n out(chalk.orange`[${packageName}] A matching config already exists at ${loc}`\n + `\\nPlease remove this config before generating a new one with \\`${cliName} init\\``)\n }\n\n const projectPackageJsonLocation = join(projectDir, 'package.json')\n if (!fs.existsSync(projectPackageJsonLocation)) {\n out(chalk.orange`[${packageName}] No package.json exists in this location`\n + '\\nPlease initialise the package with `yarn init` on `npm init`'\n + ' before running this command, and run this command from the project root folder')\n }\n\n const packageJsonRaw = fs.readFileSync(projectPackageJsonLocation, 'utf8')\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const packageJson: { type?: string } = JSON.parse(packageJsonRaw)\n\n const targetConfigName = (packageJson.type === 'module')\n // ES6 module\n ? join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.js')\n // common\n : join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.mjs')\n\n const defaultConfig =\n`\n// @ts-check\n\n/** @satisfies {import('sxy-test-runner').Config} */\nexport default {\n\n // base folder for tests match and ignore patterns\n testsBase: '.',\n\n // glob pattern or array of glob patterns of test files\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n\n // glob pattern or array of patterns of test files to ignore\n testsIgnore: ['**/node_modules/**/*'],\n\n // whether to show the full error trace when an error occurs in a test file\n showErrorTrace: false,\n\n // tasks to run on startup\n // accepts a function, file location (relative to project base), or array of functions and or files\n setup: undefined,\n\n // tasks to run before each test file is run\n beforeEachFile: undefined,\n\n // tasks to run after each test file is run\n afterEachFile: undefined,\n\n // tasks to run before each describe block is processed\n // if the function returns an object with named properties, or the file exports named exports,\n // these will be passed to the callback function in argument 0, and can be access by destructruing e.g.\n // describe('thing', ({exportA, exportB}) => { it('should', ...) })\n beforeEachDescribe: undefined,\n\n // tasks to run after each describe block\n afterEachDescribe: undefined,\n\n // tasks to run before each it/test block\n // function returns and file exports will be passed to the it/test block as with describe above, e.g.\n // it('should', ({exportA, exportB}) => { assert('something') })\n beforeEachTest: undefined,\n\n // tasks to run after each it/test block\n afterEachTest: undefined,\n\n // how to execute the different parts of test files\n execution: {\n\n // run test files in 'parallel' or 'sequential'ly\n files: 'sequential',\n\n // run describe blocks within a test file in 'parallel' or 'sequential'ly\n describes: 'sequential',\n\n // run it/test blocks within a describe blocks in 'parallel' or 'sequential'ly\n tests: 'sequential'\n\n },\n\n // watch mode configurations\n watch: {\n\n //// the base directory to watch files in\n watchFilesBase: '.',\n\n //// glob filter or array of glob filters of files to watch\n watchFiles: '**/*.js',\n\n //// glob filter or array of globl filters of files to ignore\n watchFilesIgnore: '**/node_modules/**/*',\n\n // run all tests when starting the watcher\n runAllOnStartup: true,\n\n // re run all previously failed tests on each test run, until they pass\n reRunFailingTests: true,\n\n // run watch mode keyboard commands immediately, without waiting for Enter\n ttyActionsOnKeypress: false,\n\n // file to write the latest run status to, or false for none\n statusFile: false,\n\n // report the session as single agent readable lines instead of the usual output\n agent: false,\n\n },\n\n}`\n\n fs.writeFileSync(targetConfigName, defaultConfig)\n\n out(chalk.mediumgreen`[${packageName}] Created config at ` + chalk.blue(targetConfigName))\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { FinalConfig, TestsResultsSummary } from '../../types.js';
|
|
2
|
+
import type { FailureEntry } from './failureEntries.js';
|
|
3
|
+
import type { RunType } from './reRunManager.js';
|
|
4
|
+
import type { StatusCode } from './statusFile.js';
|
|
5
|
+
export type RunReason = RunType | 'startup';
|
|
6
|
+
export declare function agentEnabled(config: FinalConfig): boolean;
|
|
7
|
+
export declare function agentInit(enabled: boolean): void;
|
|
8
|
+
export declare function agentFound(config: FinalConfig, files: number): void;
|
|
9
|
+
export declare function agentSetRunReason(reason: RunReason): void;
|
|
10
|
+
export declare function agentRunStart(config: FinalConfig, files: number): number;
|
|
11
|
+
export declare function agentFailure(config: FinalConfig, run: number, entry: FailureEntry): void;
|
|
12
|
+
export declare function agentDone(config: FinalConfig, run: number, code: StatusCode, description: string, summary: TestsResultsSummary | null, durationMs: number, failingFiles: number): void;
|
|
13
|
+
export declare function agentWatching(config: FinalConfig, files: number): void;
|
|
14
|
+
export declare function agentChange(config: FinalConfig, file: string, event: string): void;
|
|
15
|
+
export declare function agentSilentOut(): void;
|
|
16
|
+
//# sourceMappingURL=agentReport.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { out as mainOut } from '../../output.js';
|
|
2
|
+
// Agent mode reports a watch session as a stream of single lines, for a reader tailing the
|
|
3
|
+
// process rather than watching the terminal. Every line is one complete event:
|
|
4
|
+
//
|
|
5
|
+
// [sxyt] <event> | <key>=<value> | <key>=<value>
|
|
6
|
+
//
|
|
7
|
+
// Values are never quoted, so free text is flattened to keep the grammar intact - one event
|
|
8
|
+
// can never span two lines, and a stray `|` can never look like a field boundary.
|
|
9
|
+
const prefix = '[sxyt]';
|
|
10
|
+
let runCounter = 0;
|
|
11
|
+
// requestRun sets this before handing a run to runTests; the initial run never goes through
|
|
12
|
+
// the queue, so 'startup' is the right default
|
|
13
|
+
let nextRunReason = 'startup';
|
|
14
|
+
function emit(event, fields = []) {
|
|
15
|
+
mainOut(prefix + ` ${event}` + fields.map(([key, value]) => ` | ${key}=${value}`).join(''));
|
|
16
|
+
}
|
|
17
|
+
// newlines would split one event across two lines, and a `|` would read as a field boundary
|
|
18
|
+
function flatten(text) {
|
|
19
|
+
return text.replace(/\s*[\r\n]+\s*/g, ' ').replace(/\|/g, '/').trim();
|
|
20
|
+
}
|
|
21
|
+
export function agentEnabled(config) {
|
|
22
|
+
return config.watch.agent;
|
|
23
|
+
}
|
|
24
|
+
// the process is up, before the config is even loaded - so a slow startup is
|
|
25
|
+
// distinguishable from one that never began. Takes the flag rather than the config for that
|
|
26
|
+
// reason, and resets the counters so repeated in process sessions start from 1.
|
|
27
|
+
export function agentInit(enabled) {
|
|
28
|
+
runCounter = 0;
|
|
29
|
+
nextRunReason = 'startup';
|
|
30
|
+
if (!enabled)
|
|
31
|
+
return;
|
|
32
|
+
emit('init', [['pid', process.pid]]);
|
|
33
|
+
}
|
|
34
|
+
export function agentFound(config, files) {
|
|
35
|
+
if (!agentEnabled(config))
|
|
36
|
+
return;
|
|
37
|
+
emit('found', [['files', files]]);
|
|
38
|
+
}
|
|
39
|
+
export function agentSetRunReason(reason) {
|
|
40
|
+
nextRunReason = reason;
|
|
41
|
+
}
|
|
42
|
+
// claims the next run number and announces the run, so a reader knows work is in flight
|
|
43
|
+
// before any result arrives
|
|
44
|
+
export function agentRunStart(config, files) {
|
|
45
|
+
if (!agentEnabled(config))
|
|
46
|
+
return 0;
|
|
47
|
+
runCounter++;
|
|
48
|
+
emit('run', [
|
|
49
|
+
['n', runCounter],
|
|
50
|
+
['files', files],
|
|
51
|
+
['why', nextRunReason],
|
|
52
|
+
['covers', runScope(config)]
|
|
53
|
+
]);
|
|
54
|
+
return runCounter;
|
|
55
|
+
}
|
|
56
|
+
// what the run was built from. A reader needs this to know how far the run's own `code`
|
|
57
|
+
// generalises - only a run covering everything can speak for the whole suite.
|
|
58
|
+
function runScope(config) {
|
|
59
|
+
switch (nextRunReason) {
|
|
60
|
+
case 'startup':
|
|
61
|
+
case 'all': return 'all';
|
|
62
|
+
case 'last': return 'last';
|
|
63
|
+
case 'failed': return 'failing';
|
|
64
|
+
// the reRunFailingTests case is the one worth spelling out: without it a run says
|
|
65
|
+
// nothing about files that were already failing
|
|
66
|
+
case 'auto': return (config.watch.reRunFailingTests) ? 'changed+failing' : 'changed';
|
|
67
|
+
default: return 'changed';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// failures are emitted before the run's done line, so done is a complete end of run marker
|
|
71
|
+
export function agentFailure(config, run, entry) {
|
|
72
|
+
if (!agentEnabled(config))
|
|
73
|
+
return;
|
|
74
|
+
emit('fail', [
|
|
75
|
+
['n', run],
|
|
76
|
+
['file', flatten(entry.file)],
|
|
77
|
+
...(entry.describe === undefined)
|
|
78
|
+
? []
|
|
79
|
+
: [['describe', flatten(entry.describe)]],
|
|
80
|
+
...(entry.test === undefined) ? [] : [['test', flatten(entry.test)]],
|
|
81
|
+
['error', flatten(entry.error)]
|
|
82
|
+
]);
|
|
83
|
+
}
|
|
84
|
+
// `failing` counts the test files still failing across the whole session, not just this
|
|
85
|
+
// run. A watch re-run usually covers only the changed files, so its own tallies can read as
|
|
86
|
+
// a clean pass while the suite is still broken elsewhere.
|
|
87
|
+
export function agentDone(config, run, code, description, summary, durationMs, failingFiles) {
|
|
88
|
+
if (!agentEnabled(config))
|
|
89
|
+
return;
|
|
90
|
+
emit('done', [
|
|
91
|
+
['n', run],
|
|
92
|
+
['code', code],
|
|
93
|
+
['desc', flatten(description)],
|
|
94
|
+
...(summary === null) ? [] : [
|
|
95
|
+
['tests', `${summary.itsPasses}/${summary.itsTotal}`],
|
|
96
|
+
['items', `${summary.describesPasses}/${summary.describesTotal}`],
|
|
97
|
+
['files', `${summary.passes}/${summary.total}`]
|
|
98
|
+
],
|
|
99
|
+
['failing', failingFiles],
|
|
100
|
+
['ms', durationMs]
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
export function agentWatching(config, files) {
|
|
104
|
+
if (!agentEnabled(config))
|
|
105
|
+
return;
|
|
106
|
+
emit('watching', [['files', files]]);
|
|
107
|
+
}
|
|
108
|
+
export function agentChange(config, file, event) {
|
|
109
|
+
if (!agentEnabled(config))
|
|
110
|
+
return;
|
|
111
|
+
emit('change', [['file', flatten(file)], ['event', flatten(event)]]);
|
|
112
|
+
}
|
|
113
|
+
// agent mode owns stdout, so the ordinary coloured output is routed into a sink
|
|
114
|
+
export function agentSilentOut() {
|
|
115
|
+
// deliberately nothing - the agent lines are the whole stream
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=agentReport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentReport.js","sourceRoot":"","sources":["../../../src/cli/lib/agentReport.ts"],"names":[],"mappings":"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","sourcesContent":["import { out as mainOut } from '../../output.js'\n\nimport type { FinalConfig, TestsResultsSummary } from '../../types.js'\nimport type { FailureEntry } from './failureEntries.js'\nimport type { RunType } from './reRunManager.js'\nimport type { StatusCode } from './statusFile.js'\n\n// Agent mode reports a watch session as a stream of single lines, for a reader tailing the\n// process rather than watching the terminal. Every line is one complete event:\n//\n// [sxyt] <event> | <key>=<value> | <key>=<value>\n//\n// Values are never quoted, so free text is flattened to keep the grammar intact - one event\n// can never span two lines, and a stray `|` can never look like a field boundary.\n\nconst prefix = '[sxyt]'\n\nexport type RunReason = RunType | 'startup'\n\nlet runCounter = 0\n// requestRun sets this before handing a run to runTests; the initial run never goes through\n// the queue, so 'startup' is the right default\nlet nextRunReason: RunReason = 'startup'\n\ntype Field = [string, string | number]\n\nfunction emit(event: string, fields: Field[] = []): void {\n mainOut(prefix + ` ${event}` + fields.map(([key, value]) => ` | ${key}=${value}`).join(''))\n}\n\n// newlines would split one event across two lines, and a `|` would read as a field boundary\nfunction flatten(text: string): string {\n return text.replace(/\\s*[\\r\\n]+\\s*/g, ' ').replace(/\\|/g, '/').trim()\n}\n\nexport function agentEnabled(config: FinalConfig): boolean {\n return config.watch.agent\n}\n\n// the process is up, before the config is even loaded - so a slow startup is\n// distinguishable from one that never began. Takes the flag rather than the config for that\n// reason, and resets the counters so repeated in process sessions start from 1.\nexport function agentInit(enabled: boolean): void {\n runCounter = 0\n nextRunReason = 'startup'\n if (!enabled) return\n emit('init', [['pid', process.pid]])\n}\n\nexport function agentFound(config: FinalConfig, files: number): void {\n if (!agentEnabled(config)) return\n emit('found', [['files', files]])\n}\n\nexport function agentSetRunReason(reason: RunReason): void {\n nextRunReason = reason\n}\n\n// claims the next run number and announces the run, so a reader knows work is in flight\n// before any result arrives\nexport function agentRunStart(config: FinalConfig, files: number): number {\n if (!agentEnabled(config)) return 0\n runCounter++\n emit('run', [\n ['n', runCounter],\n ['files', files],\n ['why', nextRunReason],\n ['covers', runScope(config)]\n ])\n return runCounter\n}\n\n// what the run was built from. A reader needs this to know how far the run's own `code`\n// generalises - only a run covering everything can speak for the whole suite.\nfunction runScope(config: FinalConfig): string {\n switch (nextRunReason) {\n case 'startup':\n case 'all': return 'all'\n case 'last': return 'last'\n case 'failed': return 'failing'\n // the reRunFailingTests case is the one worth spelling out: without it a run says\n // nothing about files that were already failing\n case 'auto': return (config.watch.reRunFailingTests) ? 'changed+failing' : 'changed'\n default: return 'changed'\n }\n}\n\n// failures are emitted before the run's done line, so done is a complete end of run marker\nexport function agentFailure(config: FinalConfig, run: number, entry: FailureEntry): void {\n if (!agentEnabled(config)) return\n emit('fail', [\n ['n', run],\n ['file', flatten(entry.file)],\n ...(entry.describe === undefined)\n ? []\n : [['describe', flatten(entry.describe)] as Field],\n ...(entry.test === undefined) ? [] : [['test', flatten(entry.test)] as Field],\n ['error', flatten(entry.error)]\n ])\n}\n\n// `failing` counts the test files still failing across the whole session, not just this\n// run. A watch re-run usually covers only the changed files, so its own tallies can read as\n// a clean pass while the suite is still broken elsewhere.\nexport function agentDone(\n config: FinalConfig,\n run: number,\n code: StatusCode,\n description: string,\n summary: TestsResultsSummary | null,\n durationMs: number,\n failingFiles: number\n): void {\n if (!agentEnabled(config)) return\n emit('done', [\n ['n', run],\n ['code', code],\n ['desc', flatten(description)],\n ...(summary === null) ? [] : [\n ['tests', `${summary.itsPasses}/${summary.itsTotal}`] as Field,\n ['items', `${summary.describesPasses}/${summary.describesTotal}`] as Field,\n ['files', `${summary.passes}/${summary.total}`] as Field\n ],\n ['failing', failingFiles],\n ['ms', durationMs]\n ])\n}\n\nexport function agentWatching(config: FinalConfig, files: number): void {\n if (!agentEnabled(config)) return\n emit('watching', [['files', files]])\n}\n\nexport function agentChange(config: FinalConfig, file: string, event: string): void {\n if (!agentEnabled(config)) return\n emit('change', [['file', flatten(file)], ['event', flatten(event)]])\n}\n\n// agent mode owns stdout, so the ordinary coloured output is routed into a sink\nexport function agentSilentOut(): void {\n // deliberately nothing - the agent lines are the whole stream\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../../../src/cli/lib/defaultConfig.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACtC,SAAS,EAAE,GAAG;IACd,KAAK,EAAE;QACH,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;QACxB,0BAA0B;QAC1B,8BAA8B;KACjC;IACD,WAAW,EAAE,CAAC,sBAAsB,CAAC;IACrC,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,KAAK;IACd,SAAS,EAAE;QACP,KAAK,EAAE,YAAY;QACnB,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,YAAY;KACtB;IACD,KAAK,EAAE;QACH,cAAc,EAAE,GAAG;QACnB,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,sBAAsB;QACxC,eAAe,EAAE,IAAI;QACrB,iBAAiB,EAAE,IAAI;QACvB,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,KAAK;
|
|
1
|
+
{"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../../../src/cli/lib/defaultConfig.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACtC,SAAS,EAAE,GAAG;IACd,KAAK,EAAE;QACH,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;QACxB,0BAA0B;QAC1B,8BAA8B;KACjC;IACD,WAAW,EAAE,CAAC,sBAAsB,CAAC;IACrC,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,KAAK;IACd,SAAS,EAAE;QACP,KAAK,EAAE,YAAY;QACnB,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,YAAY;KACtB;IACD,KAAK,EAAE;QACH,cAAc,EAAE,GAAG;QACnB,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,sBAAsB;QACxC,eAAe,EAAE,IAAI;QACrB,iBAAiB,EAAE,IAAI;QACvB,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,KAAK;QACjB,KAAK,EAAE,KAAK;KACf;CACJ,CAAA","sourcesContent":["import type { FinalConfig } from '../../types.js'\n\nexport const defaultConfig: FinalConfig = {\n testsBase: '.',\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n testsIgnore: ['**/node_modules/**/*'],\n showErrorTrace: false,\n compact: false,\n execution: {\n files: 'sequential',\n describes: 'sequential',\n tests: 'sequential'\n },\n watch: {\n watchFilesBase: '.',\n watchFiles: '**/*.js',\n watchFilesIgnore: '**/node_modules/**/*',\n runAllOnStartup: true,\n reRunFailingTests: true,\n ttyActionsOnKeypress: false,\n statusFile: false,\n agent: false\n }\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TestFile, TestResult } from '../../types.js';
|
|
2
|
+
export interface FailureEntry {
|
|
3
|
+
file: string;
|
|
4
|
+
describe?: string;
|
|
5
|
+
test?: string;
|
|
6
|
+
error: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function failureEntries(testResults: TestResult[], erroringTests?: Record<string, TestFile>): FailureEntry[];
|
|
9
|
+
//# sourceMappingURL=failureEntries.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function failureEntries(testResults, erroringTests = {}) {
|
|
2
|
+
const entries = [];
|
|
3
|
+
for (const [file, test] of Object.entries(erroringTests)) {
|
|
4
|
+
entries.push({ file, error: `did not load: ${simplifyError(test.error)}` });
|
|
5
|
+
}
|
|
6
|
+
for (const testResult of testResults) {
|
|
7
|
+
for (const describeResult of testResult.describeResults) {
|
|
8
|
+
if (describeResult.error !== undefined) {
|
|
9
|
+
entries.push({
|
|
10
|
+
file: testResult.file,
|
|
11
|
+
describe: describeResult.thing,
|
|
12
|
+
error: `threw: ${simplifyError(describeResult.error)}`
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
for (const itResult of describeResult.itResults) {
|
|
16
|
+
if (itResult.success !== false)
|
|
17
|
+
continue;
|
|
18
|
+
entries.push({
|
|
19
|
+
file: testResult.file,
|
|
20
|
+
describe: describeResult.thing,
|
|
21
|
+
test: itResult.should,
|
|
22
|
+
error: simplifyError(itResult.error)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return entries;
|
|
28
|
+
}
|
|
29
|
+
function simplifyError(error) {
|
|
30
|
+
if (error === undefined || error === null || error === false)
|
|
31
|
+
return 'unknown error';
|
|
32
|
+
if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
|
|
33
|
+
const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error';
|
|
34
|
+
return `${name}: ${firstLine(error.message)}`;
|
|
35
|
+
}
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- only a fallback
|
|
37
|
+
return firstLine(String(error));
|
|
38
|
+
}
|
|
39
|
+
function firstLine(text) {
|
|
40
|
+
return text.split('\n')[0]?.trim() ?? '';
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=failureEntries.js.map
|
|
@@ -0,0 +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,43 +1,33 @@
|
|
|
1
|
+
import { failureEntries } from './failureEntries.js';
|
|
1
2
|
// The failures for the status file, as f: file > d: describe > t: test > error. Errors are
|
|
2
3
|
// reduced to a single line - anything wanting the diff, the logs or the trace reads the
|
|
3
4
|
// terminal.
|
|
4
5
|
export function failureLines(testResults, erroringTests = {}) {
|
|
5
6
|
const lines = [];
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
for (const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
7
|
+
let currentFile;
|
|
8
|
+
let currentDescribe;
|
|
9
|
+
// entries arrive in tree order, so a heading is needed only when its value changes
|
|
10
|
+
for (const entry of failureEntries(testResults, erroringTests)) {
|
|
11
|
+
if (entry.file !== currentFile) {
|
|
12
|
+
lines.push(` f: ${entry.file}`);
|
|
13
|
+
currentFile = entry.file;
|
|
14
|
+
currentDescribe = undefined;
|
|
15
|
+
}
|
|
16
|
+
if (entry.describe === undefined) {
|
|
17
|
+
lines.push(` e: ${entry.error}`);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (entry.describe !== currentDescribe) {
|
|
21
|
+
lines.push(` d: ${entry.describe}`);
|
|
22
|
+
currentDescribe = entry.describe;
|
|
23
|
+
}
|
|
24
|
+
if (entry.test === undefined) {
|
|
25
|
+
lines.push(` e: ${entry.error}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
lines.push(` t: ${entry.test}`, ` e: ${entry.error}`);
|
|
24
29
|
}
|
|
25
|
-
if (fileLines.length > 0)
|
|
26
|
-
lines.push(` f: ${testResult.file}`, ...fileLines);
|
|
27
30
|
}
|
|
28
31
|
return (lines.length > 0) ? ['Failures:', ...lines] : [];
|
|
29
32
|
}
|
|
30
|
-
function simplifyError(error) {
|
|
31
|
-
if (error === undefined || error === null || error === false)
|
|
32
|
-
return 'unknown error';
|
|
33
|
-
if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
|
|
34
|
-
const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error';
|
|
35
|
-
return `${name}: ${firstLine(error.message)}`;
|
|
36
|
-
}
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-base-to-string -- only a fallback
|
|
38
|
-
return firstLine(String(error));
|
|
39
|
-
}
|
|
40
|
-
function firstLine(text) {
|
|
41
|
-
return text.split('\n')[0]?.trim() ?? '';
|
|
42
|
-
}
|
|
43
33
|
//# sourceMappingURL=failureLines.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failureLines.js","sourceRoot":"","sources":["../../../src/cli/lib/failureLines.ts"],"names":[],"mappings":"AAEA,2FAA2F;AAC3F,wFAAwF;AACxF,YAAY;AACZ,MAAM,UAAU,YAAY,CACxB,WAAyB,EACzB,gBAA0C,EAAE;IAE5C,MAAM,KAAK,GAAa,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"failureLines.js","sourceRoot":"","sources":["../../../src/cli/lib/failureLines.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,2FAA2F;AAC3F,wFAAwF;AACxF,YAAY;AACZ,MAAM,UAAU,YAAY,CACxB,WAAyB,EACzB,gBAA0C,EAAE;IAE5C,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,IAAI,WAA+B,CAAA;IACnC,IAAI,eAAmC,CAAA;IAEvC,mFAAmF;IACnF,KAAK,MAAM,KAAK,IAAI,cAAc,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC;QAC7D,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;YAChC,WAAW,GAAG,KAAK,CAAC,IAAI,CAAA;YACxB,eAAe,GAAG,SAAS,CAAA;QAC/B,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;YACnC,SAAQ;QACZ,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,eAAe,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;YACtC,eAAe,GAAG,KAAK,CAAC,QAAQ,CAAA;QACpC,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;QACzC,CAAC;aAAM,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,CAAC,IAAI,EAAE,EAAE,cAAc,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;QACrE,CAAC;IACL,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import type { TestFile, TestResult } from '../../types.js'\n\nimport { failureEntries } from './failureEntries.js'\n\n// The failures for the status file, as f: file > d: describe > t: test > error. Errors are\n// reduced to a single line - anything wanting the diff, the logs or the trace reads the\n// terminal.\nexport function failureLines(\n testResults: TestResult[],\n erroringTests: Record<string, TestFile> = {}\n): string[] {\n const lines: string[] = []\n let currentFile: string | undefined\n let currentDescribe: string | undefined\n\n // entries arrive in tree order, so a heading is needed only when its value changes\n for (const entry of failureEntries(testResults, erroringTests)) {\n if (entry.file !== currentFile) {\n lines.push(` f: ${entry.file}`)\n currentFile = entry.file\n currentDescribe = undefined\n }\n\n if (entry.describe === undefined) {\n lines.push(` e: ${entry.error}`)\n continue\n }\n\n if (entry.describe !== currentDescribe) {\n lines.push(` d: ${entry.describe}`)\n currentDescribe = entry.describe\n }\n\n if (entry.test === undefined) {\n lines.push(` e: ${entry.error}`)\n } else {\n lines.push(` t: ${entry.test}`, ` e: ${entry.error}`)\n }\n }\n\n return (lines.length > 0) ? ['Failures:', ...lines] : []\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReRunManager } from './reRunManager.js';
|
|
2
2
|
import { RunTestsPromise } from './runTests.js';
|
|
3
|
+
import type { OutFn } from '../../types.js';
|
|
3
4
|
export type RerunTestsFactory = (testFiles: Set<string>) => () => RunTestsPromise;
|
|
4
|
-
export declare function listenToUser(rerunManager: ReRunManager, rerunTestsCallback: RerunTestsFactory, ttyActionsOnKeypress: boolean | undefined, initialTestRun: Promise<boolean | null> | null, input?: NodeJS.ReadStream): Promise<void>;
|
|
5
|
+
export declare function listenToUser(rerunManager: ReRunManager, rerunTestsCallback: RerunTestsFactory, ttyActionsOnKeypress: boolean | undefined, initialTestRun: Promise<boolean | null> | null, input?: NodeJS.ReadStream, customOut?: OutFn): Promise<void>;
|
|
5
6
|
//# sourceMappingURL=listenToUser.d.ts.map
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createInterface, emitKeypressEvents } from 'node:readline';
|
|
2
2
|
import { showTtyHelp } from './showTtyHelp.js';
|
|
3
3
|
import { handleUserInput } from './handleUserInput.js';
|
|
4
|
-
import { out } from '../../output.js';
|
|
4
|
+
import { out as mainOut } from '../../output.js';
|
|
5
5
|
export async function listenToUser(
|
|
6
6
|
//allTestFiles: Set<string>,
|
|
7
7
|
//failingTests: Set<string>,
|
|
8
|
-
rerunManager, rerunTestsCallback, ttyActionsOnKeypress = false, initialTestRun, input = process.stdin) {
|
|
8
|
+
rerunManager, rerunTestsCallback, ttyActionsOnKeypress = false, initialTestRun, input = process.stdin, customOut) {
|
|
9
|
+
// an agent spawned session has no TTY, so these notices would otherwise be the only
|
|
10
|
+
// lines in agent mode that are not agent events
|
|
11
|
+
const out = customOut ?? mainOut;
|
|
9
12
|
//console.log('initia test run', initialTestRun)
|
|
10
13
|
//console.log('listen to user')
|
|
11
14
|
//setInterval(() => console.log('active runs: ', rerunManager.state.activeRuns), 10_000)
|
|
@@ -58,7 +61,7 @@ rerunManager, rerunTestsCallback, ttyActionsOnKeypress = false, initialTestRun,
|
|
|
58
61
|
}
|
|
59
62
|
//console.log('initialTestRun 2', initialTestRun)
|
|
60
63
|
await initialTestRun;
|
|
61
|
-
showTtyHelp();
|
|
64
|
+
showTtyHelp(input.isTTY, out);
|
|
62
65
|
return;
|
|
63
66
|
}
|
|
64
67
|
//# sourceMappingURL=listenToUser.js.map
|