sxy-test-runner 2.4.4 → 2.4.6
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 +49 -21
- package/dist/cli/help.js +27 -8
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/lib/agentReport.d.ts +1 -1
- package/dist/cli/lib/agentReport.js +6 -3
- package/dist/cli/lib/agentReport.js.map +1 -1
- package/dist/cli/lib/exitCodes.d.ts +10 -0
- package/dist/cli/lib/exitCodes.js +27 -0
- package/dist/cli/lib/exitCodes.js.map +1 -0
- package/dist/cli/lib/parseCommandLineArguments.d.ts +0 -1
- package/dist/cli/lib/parseCommandLineArguments.js +2 -4
- package/dist/cli/lib/parseCommandLineArguments.js.map +1 -1
- package/dist/cli/lib/resolveCommand.d.ts +1 -1
- package/dist/cli/lib/resolveCommand.js +11 -8
- package/dist/cli/lib/resolveCommand.js.map +1 -1
- package/dist/cli/lib/statusFile.d.ts +1 -0
- package/dist/cli/lib/statusFile.js +10 -0
- package/dist/cli/lib/statusFile.js.map +1 -1
- package/dist/cli/once.js +7 -3
- package/dist/cli/once.js.map +1 -1
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +23 -0
- package/dist/cli/version.js.map +1 -0
- package/package.json +1 -1
- package/readme.md +75 -6
package/AGENTS.md
CHANGED
|
@@ -19,17 +19,34 @@ For introductory human-facing usage, also see `readme.md`.
|
|
|
19
19
|
4. Import only `describe` from `sxy-test-runner`. Obtain `it`, `test`, and local hooks from
|
|
20
20
|
the `describe` callback.
|
|
21
21
|
5. Run `npx sxy-test-runner once` for finite agent or CI work.
|
|
22
|
-
6. Exit codes
|
|
23
|
-
`4` no test files matched the `tests` pattern, `5` test files ran but
|
|
24
|
-
`--filter-it`/`--filter-describe` matched nothing.
|
|
22
|
+
6. Exit codes for `once`:
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
| code | meaning |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| `0` | everything passed |
|
|
27
|
+
| `20` | test errors - a failed assertion, a test file that would not load, or a describe that threw |
|
|
28
|
+
| `21` | no test file matched the `tests` pattern |
|
|
29
|
+
| `22` | test files matched, but `--filter-it`/`--filter-describe` selected nothing in them |
|
|
30
|
+
| `23` | an option was not recognised, or one needing a value was given none |
|
|
31
|
+
| `24` | reserved for a failure in the runner itself - not currently emitted |
|
|
32
|
+
|
|
33
|
+
**These start at 20 deliberately.** Node reserves 1-14 for its own fatal conditions - `1`
|
|
34
|
+
uncaught exception, `2` unused because Bash claims it for builtin misuse, `3`/`4` internal
|
|
35
|
+
parse and evaluation failures, `5` a fatal V8 error, `9` invalid argument, and so on - while
|
|
36
|
+
anything over 128 is a signal exit, and the shell takes 126 and 127. A runner code inside
|
|
37
|
+
that range cannot be told apart from Node itself dying. Above 14 and below 126, every
|
|
38
|
+
non-zero code is unambiguously the runner's own.
|
|
39
|
+
|
|
40
|
+
`--help` (`-h`) and `--version` (`-v`) work on any command and take over from it, so
|
|
41
|
+
`once --help` describes `once` rather than running the suite. Both exit `0`.
|
|
42
|
+
|
|
43
|
+
An unrecognised option exits before anything runs, rather than warning and carrying on -
|
|
27
44
|
otherwise a typo like `--fitler-it` quietly runs the whole suite and reports a pass. Every
|
|
28
45
|
unrecognised option is listed at once, with a suggestion where the intent is obvious. Options
|
|
29
46
|
are per-command, so `watch --fail-fast` and `once --status-file` are usage errors too.
|
|
30
47
|
|
|
31
48
|
These are process exit codes, and are **not** the status codes used by `--status-file` and
|
|
32
|
-
`--agent`,
|
|
49
|
+
`--agent`, which are a separate, smaller scheme. Read the exit code from the process.
|
|
33
50
|
|
|
34
51
|
Do not run a test file directly with `node`. `describe()` requires runner-owned global
|
|
35
52
|
state and deliberately throws if the framework has not initialized it.
|
|
@@ -113,8 +130,8 @@ configured pattern. Both options are resolved relative to `testsBase` and can be
|
|
|
113
130
|
|
|
114
131
|
`--filter-describe` and `--filter-it` apply case-insensitive substring filters to describe
|
|
115
132
|
and test names. They work in both watch and `once` modes and can be combined. If the
|
|
116
|
-
filters select no runnable tests, `once` exits
|
|
117
|
-
|
|
133
|
+
filters select no runnable tests, `once` exits `22` - `21` means no test file matched the
|
|
134
|
+
`tests` pattern in the first place.
|
|
118
135
|
|
|
119
136
|
`--agent` and `--status-file` are watch-only, and are the two ways to follow a watch session
|
|
120
137
|
from outside it: `--agent` streams each run to stdout as single lines, `--status-file` writes
|
|
@@ -139,7 +156,7 @@ the file, or the remaining files. Hooks still unwind normally, so `afterEachTest
|
|
|
139
156
|
Because the run is cut short, the tallies count only what ran - a suite of 500 tests can
|
|
140
157
|
report `Tests: 0/1`. The summary therefore ends with `stopped at the first failure
|
|
141
158
|
(failFast)`, and the status file's description gains `, stopped at the first failure`. Read
|
|
142
|
-
those before treating a tally as the size of the suite. The exit code is unchanged: `
|
|
159
|
+
those before treating a tally as the size of the suite. The exit code is unchanged: `20` for
|
|
143
160
|
a test failure, so CI still fails the job.
|
|
144
161
|
|
|
145
162
|
Under parallel execution it is best-effort: work already in flight finishes, so more than
|
|
@@ -369,14 +386,14 @@ ignored there, so the output format cannot change out from under a caller.
|
|
|
369
386
|
```
|
|
370
387
|
[sxyt] init | pid=16744
|
|
371
388
|
[sxyt] found | files=2
|
|
372
|
-
[sxyt]
|
|
389
|
+
[sxyt] running | n=1 | files=2 | why=startup | covers=all
|
|
373
390
|
[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
|
|
374
|
-
[sxyt] done | n=1 |
|
|
391
|
+
[sxyt] done | n=1 | status=failed | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=150
|
|
375
392
|
[sxyt] watching | files=2
|
|
376
393
|
[sxyt] change | file=src/orders.js | event=update
|
|
377
|
-
[sxyt]
|
|
394
|
+
[sxyt] running | n=2 | files=2 | why=auto | covers=changed+failing
|
|
378
395
|
[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
|
|
379
|
-
[sxyt] done | n=2 |
|
|
396
|
+
[sxyt] done | n=2 | status=failed | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=169
|
|
380
397
|
```
|
|
381
398
|
|
|
382
399
|
The grammar is `[sxyt] <event>` followed by ` | <key>=<value>` pairs. Values are never
|
|
@@ -387,7 +404,7 @@ is always exactly one line and a field boundary is always a real one.
|
|
|
387
404
|
| --- | --- |
|
|
388
405
|
| `init` | the process is up, before the config is read - proof it started at all |
|
|
389
406
|
| `found` | test discovery finished |
|
|
390
|
-
| `
|
|
407
|
+
| `running` | tests have started running. `why` is `once`, `startup`, `auto` (a file changed), or `all`/`last`/`failed` from a keyboard command |
|
|
391
408
|
| `fail` | one failure. `describe` is absent when a file would not load, `test` when the describe itself threw |
|
|
392
409
|
| `done` | the run finished |
|
|
393
410
|
| `change` | a watched file changed |
|
|
@@ -396,10 +413,21 @@ is always exactly one line and a field boundary is always a real one.
|
|
|
396
413
|
`n` ties a run's lines together. Failures are emitted before their `done`, so `done` is a
|
|
397
414
|
complete end-of-run marker: wait for it and everything about that run has already arrived.
|
|
398
415
|
|
|
399
|
-
`
|
|
400
|
-
|
|
416
|
+
`status` on `done` is the state the run ended in, named rather than numbered:
|
|
417
|
+
|
|
418
|
+
| status | meaning | status-file code |
|
|
419
|
+
| --- | --- | --- |
|
|
420
|
+
| `passed` | everything that ran, passed | `0` |
|
|
421
|
+
| `failed` | tests ran and failed | `1` |
|
|
422
|
+
| `aborted` | the run died partway - a test file would not load, or a describe threw | `2` |
|
|
423
|
+
| `could-not-run` | nothing ran - no test files, or the filters matched nothing | `3` |
|
|
424
|
+
|
|
425
|
+
`aborted` is worth handling separately from `failed`: it usually means the suite broke
|
|
426
|
+
rather than the code under test. The right-hand column is the number `--status-file` records
|
|
427
|
+
for the same state, for anything correlating the two. It is **not** the process exit code -
|
|
428
|
+
see the exit codes above.
|
|
401
429
|
|
|
402
|
-
`covers` on the `
|
|
430
|
+
`covers` on the `running` line says what the run was built from, and so how far its result
|
|
403
431
|
generalises:
|
|
404
432
|
|
|
405
433
|
| covers | the run contained |
|
|
@@ -411,7 +439,7 @@ generalises:
|
|
|
411
439
|
| `last` | the previous run's file set |
|
|
412
440
|
| `failing` | only the files still failing |
|
|
413
441
|
|
|
414
|
-
**No incremental run can tell you the suite is green.** `
|
|
442
|
+
**No incremental run can tell you the suite is green.** `status` and the tallies describe
|
|
415
443
|
only the files that run contained. `failing` is broader - it counts the test files still
|
|
416
444
|
known to be failing across the session - but it is still only knowledge from files that
|
|
417
445
|
have actually been run, and a run happens only when the watcher both notices a change and
|
|
@@ -431,7 +459,7 @@ maps it to a test. Either half can miss:
|
|
|
431
459
|
So `failing=0` means "nothing is known to be broken", not "everything passes", and silence
|
|
432
460
|
means "no watched file changed", not "still green". To actually verify the suite, use a run
|
|
433
461
|
with `covers=all` - the startup run, `a` at the keyboard, or a separate `once` - and read
|
|
434
|
-
`
|
|
462
|
+
`status` and `failing` from that. For the ordinary edit-test loop in between, `failing` is the
|
|
435
463
|
right signal: with the default `watch.reRunFailingTests`, a failure keeps being re-run and
|
|
436
464
|
re-reported until it passes.
|
|
437
465
|
|
|
@@ -440,9 +468,9 @@ it matters, at the cost of more reruns.
|
|
|
440
468
|
|
|
441
469
|
Diffs, logs and stack traces are not in the agent stream; errors are reduced to one line.
|
|
442
470
|
|
|
443
|
-
In `once`,
|
|
444
|
-
|
|
445
|
-
|
|
471
|
+
In `once`, read the process's exit code for pass/fail and the `status` on `done` for what
|
|
472
|
+
kind of failure it was - `status=failed` accompanies exit `20`. `watching` and `change`
|
|
473
|
+
never appear, since there is no watcher.
|
|
446
474
|
|
|
447
475
|
```sh
|
|
448
476
|
# a finite, agent readable run: failures and a summary, exit code preserved
|
package/dist/cli/help.js
CHANGED
|
@@ -18,21 +18,30 @@ export function help() {
|
|
|
18
18
|
` ${cliName} help`,
|
|
19
19
|
'',
|
|
20
20
|
'Commands:',
|
|
21
|
-
' watch
|
|
22
|
-
' once
|
|
23
|
-
' init
|
|
24
|
-
' help
|
|
21
|
+
' watch Run tests and watch for related file changes (default)',
|
|
22
|
+
' once Run the test suite once and exit',
|
|
23
|
+
' init Create an initial configuration file',
|
|
24
|
+
' help Show this help',
|
|
25
|
+
' version Show the installed version',
|
|
25
26
|
'',
|
|
26
|
-
'Options:',
|
|
27
|
+
'Options for watch and once:',
|
|
27
28
|
' -c, --config <file> Use a specific configuration file',
|
|
28
29
|
' -t, --test-files <glob> Override the configured test pattern',
|
|
29
30
|
' -f, --filter <glob> Further constrain the configured test pattern',
|
|
30
31
|
' -d, --filter-describe <substring> Run matching describe blocks',
|
|
31
32
|
' -i, --filter-it <substring> Run matching tests',
|
|
32
|
-
' -x, --fail-fast Stop at the first failure (once mode)',
|
|
33
|
-
' -s, --status-file <file> Write the latest watch run status to a file',
|
|
34
33
|
' --agent Report the run as single agent readable lines',
|
|
35
|
-
' -h, --help Show this help
|
|
34
|
+
' -h, --help Show this help instead of running',
|
|
35
|
+
' -v, --version Show the installed version instead of running',
|
|
36
|
+
'',
|
|
37
|
+
'Options for once only:',
|
|
38
|
+
' -x, --fail-fast Stop at the first failure',
|
|
39
|
+
'',
|
|
40
|
+
'Options for watch only:',
|
|
41
|
+
' -s, --status-file <file> Write each run\'s result to a file',
|
|
42
|
+
'',
|
|
43
|
+
'An option not listed for the command it is given to is an error: the command exits',
|
|
44
|
+
'without running anything, rather than ignoring it.',
|
|
36
45
|
'',
|
|
37
46
|
'Watch controls:',
|
|
38
47
|
' r/l + Enter Re-run the last set of tests',
|
|
@@ -40,11 +49,21 @@ export function help() {
|
|
|
40
49
|
' f + Enter Re-run failing tests',
|
|
41
50
|
' Set watch.ttyActionsOnKeypress to true to run these without Enter',
|
|
42
51
|
'',
|
|
52
|
+
'Exit codes for once:',
|
|
53
|
+
' 0 Everything passed',
|
|
54
|
+
' 20 Test errors - a failed assertion, a file that would not load, a describe that threw',
|
|
55
|
+
' 21 No test file matched the tests pattern',
|
|
56
|
+
' 22 Test files matched, but the name filters selected nothing in them',
|
|
57
|
+
' 23 An option was not recognised',
|
|
58
|
+
'',
|
|
43
59
|
'Examples:',
|
|
44
60
|
` ${cliName}`,
|
|
45
61
|
` ${cliName} once`,
|
|
46
62
|
` ${cliName} watch --test-files "**/*.focused.test.js"`,
|
|
47
63
|
` ${cliName} once --filter "**/users/**"`,
|
|
64
|
+
` ${cliName} once --fail-fast`,
|
|
65
|
+
` ${cliName} once --agent`,
|
|
66
|
+
` ${cliName} watch --status-file .sxyt-status`,
|
|
48
67
|
` ${cliName} once --config config/sxyt.config.js`,
|
|
49
68
|
'',
|
|
50
69
|
`Configuration: ${configNames} in ${configDirectories}.`,
|
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,
|
|
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,kEAAkE;QAClE,4CAA4C;QAC5C,gDAAgD;QAChD,0BAA0B;QAC1B,sCAAsC;QACtC,EAAE;QACF,6BAA6B;QAC7B,wEAAwE;QACxE,2EAA2E;QAC3E,oFAAoF;QACpF,mEAAmE;QACnE,yDAAyD;QACzD,oFAAoF;QACpF,wEAAwE;QACxE,oFAAoF;QACpF,EAAE;QACF,wBAAwB;QACxB,gEAAgE;QAChE,EAAE;QACF,yBAAyB;QACzB,yEAAyE;QACzE,EAAE;QACF,oFAAoF;QACpF,oDAAoD;QACpD,EAAE;QACF,iBAAiB;QACjB,sDAAsD;QACtD,0CAA0C;QAC1C,8CAA8C;QAC9C,qEAAqE;QACrE,EAAE;QACF,sBAAsB;QACtB,yBAAyB;QACzB,2FAA2F;QAC3F,8CAA8C;QAC9C,yEAAyE;QACzE,oCAAoC;QACpC,EAAE;QACF,WAAW;QACX,KAAK,OAAO,EAAE;QACd,KAAK,OAAO,OAAO;QACnB,KAAK,OAAO,4CAA4C;QACxD,KAAK,OAAO,8BAA8B;QAC1C,KAAK,OAAO,mBAAmB;QAC/B,KAAK,OAAO,eAAe;QAC3B,KAAK,OAAO,mCAAmC;QAC/C,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 ' version Show the installed version',\n '',\n 'Options for watch and once:',\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 ' --agent Report the run as single agent readable lines',\n ' -h, --help Show this help instead of running',\n ' -v, --version Show the installed version instead of running',\n '',\n 'Options for once only:',\n ' -x, --fail-fast Stop at the first failure',\n '',\n 'Options for watch only:',\n ' -s, --status-file <file> Write each run\\'s result to a file',\n '',\n 'An option not listed for the command it is given to is an error: the command exits',\n 'without running anything, rather than ignoring it.',\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 'Exit codes for once:',\n ' 0 Everything passed',\n ' 20 Test errors - a failed assertion, a file that would not load, a describe that threw',\n ' 21 No test file matched the tests pattern',\n ' 22 Test files matched, but the name filters selected nothing in them',\n ' 23 An option was not recognised',\n '',\n 'Examples:',\n ` ${cliName}`,\n ` ${cliName} once`,\n ` ${cliName} watch --test-files \"**/*.focused.test.js\"`,\n ` ${cliName} once --filter \"**/users/**\"`,\n ` ${cliName} once --fail-fast`,\n ` ${cliName} once --agent`,\n ` ${cliName} watch --status-file .sxyt-status`,\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,7 +1,7 @@
|
|
|
1
1
|
import type { FinalConfig, TestsResultsSummary } from '../../types.js';
|
|
2
2
|
import type { FailureEntry } from './failureEntries.js';
|
|
3
3
|
import type { RunType } from './reRunManager.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type StatusCode } from './statusFile.js';
|
|
5
5
|
export type RunReason = RunType | 'startup' | 'once';
|
|
6
6
|
export declare function agentEnabled(config: FinalConfig): boolean;
|
|
7
7
|
export declare function agentInit(enabled: boolean): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { out as mainOut } from '../../output.js';
|
|
2
|
+
import { statusNames } from './statusFile.js';
|
|
2
3
|
// Agent mode reports a watch session as a stream of single lines, for a reader tailing the
|
|
3
4
|
// process rather than watching the terminal. Every line is one complete event:
|
|
4
5
|
//
|
|
@@ -45,7 +46,7 @@ export function agentRunStart(config, files) {
|
|
|
45
46
|
if (!agentEnabled(config))
|
|
46
47
|
return 0;
|
|
47
48
|
runCounter++;
|
|
48
|
-
emit('
|
|
49
|
+
emit('running', [
|
|
49
50
|
['n', runCounter],
|
|
50
51
|
['files', files],
|
|
51
52
|
['why', nextRunReason],
|
|
@@ -53,7 +54,7 @@ export function agentRunStart(config, files) {
|
|
|
53
54
|
]);
|
|
54
55
|
return runCounter;
|
|
55
56
|
}
|
|
56
|
-
// what the run was built from. A reader needs this to know how far the run's own `
|
|
57
|
+
// what the run was built from. A reader needs this to know how far the run's own `status`
|
|
57
58
|
// generalises - only a run covering everything can speak for the whole suite.
|
|
58
59
|
function runScope(config) {
|
|
59
60
|
// known before the run starts: fail fast means it is built to stop early, so it cannot
|
|
@@ -100,7 +101,9 @@ export function agentDone(config, run, code, description, summary, durationMs, f
|
|
|
100
101
|
return;
|
|
101
102
|
emit('done', [
|
|
102
103
|
['n', run],
|
|
103
|
-
|
|
104
|
+
// named, not numbered: `status=failed` needs no lookup table, and cannot be misread
|
|
105
|
+
// as the process exit code the way a bare `code=1` could
|
|
106
|
+
['status', statusNames[code]],
|
|
104
107
|
['desc', flatten(description)],
|
|
105
108
|
...(summary === null) ? [] : [
|
|
106
109
|
['tests', `${summary.itsPasses}/${summary.itsTotal}`],
|
|
@@ -1 +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' | 'once'\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 // known before the run starts: fail fast means it is built to stop early, so it cannot\n // promise to have covered anything beyond the first failure\n if (config.failFast) return 'until-first-failure'\n\n switch (nextRunReason) {\n case 'startup':\n case 'once':\n case 'all': return 'all'\n case 'last': return 'last'\n case 'failed': return 'failing'\n case 'auto': {\n // with dependency evaluation off a change runs the whole suite, so an auto run\n // carries the same weight as a startup one\n if (!config.watch.dependencyEvaluation) return 'all'\n // the reRunFailingTests case is the one worth spelling out: without it a run says\n // nothing about files that were already failing\n return (config.watch.reRunFailingTests) ? 'changed+failing' : 'changed'\n }\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
|
+
{"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 { statusNames, 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' | 'once'\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('running', [\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 `status`\n// generalises - only a run covering everything can speak for the whole suite.\nfunction runScope(config: FinalConfig): string {\n // known before the run starts: fail fast means it is built to stop early, so it cannot\n // promise to have covered anything beyond the first failure\n if (config.failFast) return 'until-first-failure'\n\n switch (nextRunReason) {\n case 'startup':\n case 'once':\n case 'all': return 'all'\n case 'last': return 'last'\n case 'failed': return 'failing'\n case 'auto': {\n // with dependency evaluation off a change runs the whole suite, so an auto run\n // carries the same weight as a startup one\n if (!config.watch.dependencyEvaluation) return 'all'\n // the reRunFailingTests case is the one worth spelling out: without it a run says\n // nothing about files that were already failing\n return (config.watch.reRunFailingTests) ? 'changed+failing' : 'changed'\n }\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 // named, not numbered: `status=failed` needs no lookup table, and cannot be misread\n // as the process exit code the way a bare `code=1` could\n ['status', statusNames[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"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const exitCodes: {
|
|
2
|
+
readonly passed: 0;
|
|
3
|
+
readonly testErrors: 20;
|
|
4
|
+
readonly noTestFiles: 21;
|
|
5
|
+
readonly noMatchingTests: 22;
|
|
6
|
+
readonly usageError: 23;
|
|
7
|
+
readonly runnerError: 24;
|
|
8
|
+
};
|
|
9
|
+
export type ExitCode = typeof exitCodes[keyof typeof exitCodes];
|
|
10
|
+
//# sourceMappingURL=exitCodes.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Process exit codes for `once`.
|
|
2
|
+
//
|
|
3
|
+
// Node reserves 1-14 for its own fatal conditions, and anything over 128 is a signal exit
|
|
4
|
+
// (128 + signal), with 126 and 127 taken by the shell for "not executable" and "not found".
|
|
5
|
+
// 2 is left unused by Node because Bash uses it for builtin misuse. The whole range this
|
|
6
|
+
// runner used to sit in - 3, 4, 5 - therefore overlapped Node's own codes, so a caller
|
|
7
|
+
// could not tell "the filters matched nothing" from V8 dying. These start at 20 instead,
|
|
8
|
+
// clear of every reserved range, so a non-zero code here is always the runner's own.
|
|
9
|
+
//
|
|
10
|
+
// These are process exit codes. They are NOT the status codes written by --status-file and
|
|
11
|
+
// reported by --agent, which are a separate, smaller scheme.
|
|
12
|
+
export const exitCodes = {
|
|
13
|
+
passed: 0,
|
|
14
|
+
// tests ran and something was wrong with them: a failed assertion, a test file that
|
|
15
|
+
// would not load, or a describe that threw. All are errors in the tests, not the runner
|
|
16
|
+
testErrors: 20,
|
|
17
|
+
// nothing to run: no test file matched the `tests` pattern
|
|
18
|
+
noTestFiles: 21,
|
|
19
|
+
// test files matched, but --filter-it/--filter-describe selected nothing in them
|
|
20
|
+
noMatchingTests: 22,
|
|
21
|
+
// an option was not recognised, or an option needing a value was given none
|
|
22
|
+
usageError: 23,
|
|
23
|
+
// reserved for a failure in the runner itself. Nothing emits it today - every known
|
|
24
|
+
// failure is one of the above - but it keeps the category the original scheme intended
|
|
25
|
+
runnerError: 24
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=exitCodes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitCodes.js","sourceRoot":"","sources":["../../../src/cli/lib/exitCodes.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,0FAA0F;AAC1F,4FAA4F;AAC5F,yFAAyF;AACzF,uFAAuF;AACvF,yFAAyF;AACzF,qFAAqF;AACrF,EAAE;AACF,2FAA2F;AAC3F,6DAA6D;AAC7D,MAAM,CAAC,MAAM,SAAS,GAAG;IACrB,MAAM,EAAE,CAAC;IACT,oFAAoF;IACpF,wFAAwF;IACxF,UAAU,EAAE,EAAE;IACd,2DAA2D;IAC3D,WAAW,EAAE,EAAE;IACf,iFAAiF;IACjF,eAAe,EAAE,EAAE;IACnB,4EAA4E;IAC5E,UAAU,EAAE,EAAE;IACd,oFAAoF;IACpF,uFAAuF;IACvF,WAAW,EAAE,EAAE;CACT,CAAA","sourcesContent":["// Process exit codes for `once`.\n//\n// Node reserves 1-14 for its own fatal conditions, and anything over 128 is a signal exit\n// (128 + signal), with 126 and 127 taken by the shell for \"not executable\" and \"not found\".\n// 2 is left unused by Node because Bash uses it for builtin misuse. The whole range this\n// runner used to sit in - 3, 4, 5 - therefore overlapped Node's own codes, so a caller\n// could not tell \"the filters matched nothing\" from V8 dying. These start at 20 instead,\n// clear of every reserved range, so a non-zero code here is always the runner's own.\n//\n// These are process exit codes. They are NOT the status codes written by --status-file and\n// reported by --agent, which are a separate, smaller scheme.\nexport const exitCodes = {\n passed: 0,\n // tests ran and something was wrong with them: a failed assertion, a test file that\n // would not load, or a describe that threw. All are errors in the tests, not the runner\n testErrors: 20,\n // nothing to run: no test file matched the `tests` pattern\n noTestFiles: 21,\n // test files matched, but --filter-it/--filter-describe selected nothing in them\n noMatchingTests: 22,\n // an option was not recognised, or an option needing a value was given none\n usageError: 23,\n // reserved for a failure in the runner itself. Nothing emits it today - every known\n // failure is one of the above - but it keeps the category the original scheme intended\n runnerError: 24\n} as const\n\nexport type ExitCode = typeof exitCodes[keyof typeof exitCodes]\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { AllowedArguments, ParsedArguments } from '../../types.js';
|
|
2
|
-
export declare const usageErrorExitCode = 2;
|
|
3
2
|
export declare function parseCommandLineArguments(allowedArguments: AllowedArguments): ParsedArguments;
|
|
4
3
|
//# sourceMappingURL=parseCommandLineArguments.d.ts.map
|
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
import { reduce as objectReduce } from 'sxy-lib/objects.js';
|
|
5
5
|
import { packageName, cliName } from '../../config.js';
|
|
6
6
|
import { log } from '../../output.js';
|
|
7
|
+
import { exitCodes } from './exitCodes.js';
|
|
7
8
|
import { getAllowedFlags } from './getAllowedFlags.js';
|
|
8
9
|
import chalk from '../../packages/chalk.js';
|
|
9
|
-
// exit code for a usage error - distinct from the run's own 0/3/4/5, so a caller can tell
|
|
10
|
-
// "you asked for something I do not understand" from "the tests failed"
|
|
11
|
-
export const usageErrorExitCode = 2;
|
|
12
10
|
export function parseCommandLineArguments(allowedArguments) {
|
|
13
11
|
const allowedFlags = getAllowedFlags(allowedArguments);
|
|
14
12
|
const returnArgs = {};
|
|
@@ -75,7 +73,7 @@ export function parseCommandLineArguments(allowedArguments) {
|
|
|
75
73
|
// mean the wrong tests, or a whole suite where a single filtered test was wanted
|
|
76
74
|
if (unknownArgs.length > 0) {
|
|
77
75
|
log(chalk.orange(`Run \`${cliName} help\` to see the available options.`));
|
|
78
|
-
process.exit(
|
|
76
|
+
process.exit(exitCodes.usageError);
|
|
79
77
|
}
|
|
80
78
|
return returnArgs;
|
|
81
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseCommandLineArguments.js","sourceRoot":"","sources":["../../../src/cli/lib/parseCommandLineArguments.ts"],"names":[],"mappings":"AAAA,SAAS;AACT,QAAQ;AACR,6BAA6B;AAG7B,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAGrC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"parseCommandLineArguments.js","sourceRoot":"","sources":["../../../src/cli/lib/parseCommandLineArguments.ts"],"names":[],"mappings":"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","sourcesContent":["// watch:\n// once:\n// // --config configLocation\n\n\nimport { reduce as objectReduce } from 'sxy-lib/objects.js'\nimport { packageName, cliName } from '../../config.js'\nimport { log } from '../../output.js'\nimport type { AllowedArguments, ParsedArguments } from '../../types.js'\n\nimport { exitCodes } from './exitCodes.js'\nimport { getAllowedFlags } from './getAllowedFlags.js'\nimport chalk from '../../packages/chalk.js'\n\nexport function parseCommandLineArguments(allowedArguments: AllowedArguments): ParsedArguments {\n const allowedFlags = getAllowedFlags(allowedArguments)\n\n const returnArgs: ParsedArguments = {}\n // collected rather than reported one at a time, so a mistyped command line is fixed in\n // one go rather than one option per run\n const unknownArgs: string[] = []\n\n for (let position = 0; position < __sxyt_commandLineArguments.length; position++) {\n const arg = __sxyt_commandLineArguments[position]\n if (arg === undefined) continue\n if (arg.slice(0, 2) === '--') {\n const longFlag = arg.slice(2)\n const allowedArgument = allowedArguments[longFlag]\n if (allowedArgument) {\n if (allowedArgument.hasValue) {\n const value = __sxyt_commandLineArguments[++position]\n if (value !== undefined) returnArgs[longFlag] = value\n } else {\n returnArgs[longFlag] = true\n }\n } else {\n unknownArgs.push(`--${longFlag}`)\n log(chalk.mediumred(`[${packageName}] Unknown option --${longFlag}`))\n if (\n longFlag.length === 1\n && objectReduce(\n allowedArguments,\n (prev: boolean, _key: string, value: {\n flag?: string\n hasValue: boolean\n }) => {\n if (value.flag === longFlag) return true\n return prev\n },\n false\n )\n ) {\n log(chalk.mediumred(`I think you meant -${longFlag}`))\n }\n }\n } else if (arg.slice(0, 1) === '-') {\n const shortFlag = arg.slice(1)\n const longFlag = allowedFlags[shortFlag]\n if (longFlag !== undefined) {\n if (longFlag in returnArgs) {\n log(chalk.orange(\n `[${packageName}] Option --${longFlag} already set, being overwritten by -${shortFlag}`\n ))\n }\n\n const allowedArgument = allowedArguments[longFlag]\n if (allowedArgument?.hasValue === true) {\n const value = __sxyt_commandLineArguments[++position]\n if (value !== undefined) returnArgs[longFlag] = value\n } else {\n returnArgs[longFlag] = true\n }\n } else {\n unknownArgs.push(`-${shortFlag}`)\n log(chalk.mediumred(`[${packageName}] Unknown flag -${shortFlag}`))\n if (shortFlag.length > 1 && shortFlag in allowedArguments) {\n log(chalk.mediumred(`I think you meant --${shortFlag}`))\n }\n }\n }\n }\n\n // stop rather than run something other than what was asked for: an ignored option can\n // mean the wrong tests, or a whole suite where a single filtered test was wanted\n if (unknownArgs.length > 0) {\n log(chalk.orange(`Run \\`${cliName} help\\` to see the available options.`))\n process.exit(exitCodes.usageError)\n }\n\n return returnArgs\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const commands = ['init', 'once', 'watch', 'help'];
|
|
1
|
+
export const commands = ['init', 'once', 'watch', 'help', 'version'];
|
|
2
2
|
function isCommand(value) {
|
|
3
3
|
return commands.includes(value);
|
|
4
4
|
}
|
|
@@ -13,7 +13,9 @@ const valueTakingFlags = new Set([
|
|
|
13
13
|
'--filter-describe', '-d',
|
|
14
14
|
'--status-file', '-s'
|
|
15
15
|
]);
|
|
16
|
-
|
|
16
|
+
// --help and --version describe the tool rather than asking it to do anything, so they take
|
|
17
|
+
// over from whatever command they were given to
|
|
18
|
+
function interceptingCommand(commandArgs) {
|
|
17
19
|
for (let position = 0; position < commandArgs.length; position++) {
|
|
18
20
|
const arg = commandArgs[position];
|
|
19
21
|
if (arg === undefined)
|
|
@@ -23,9 +25,11 @@ function wantsHelp(commandArgs) {
|
|
|
23
25
|
continue;
|
|
24
26
|
}
|
|
25
27
|
if (arg === '--help' || arg === '-h')
|
|
26
|
-
return
|
|
28
|
+
return 'help';
|
|
29
|
+
if (arg === '--version' || arg === '-v')
|
|
30
|
+
return 'version';
|
|
27
31
|
}
|
|
28
|
-
return
|
|
32
|
+
return undefined;
|
|
29
33
|
}
|
|
30
34
|
// Routes the raw argv tail to a command. Extracted from the entry module so the routing can
|
|
31
35
|
// be tested without running a command as a side effect of importing it.
|
|
@@ -33,15 +37,14 @@ export function resolveCommand(args) {
|
|
|
33
37
|
const first = args[0];
|
|
34
38
|
if (first !== undefined && isCommand(first)) {
|
|
35
39
|
const commandArgs = args.slice(1);
|
|
36
|
-
|
|
37
|
-
return { command: (wantsHelp(commandArgs)) ? 'help' : first, commandArgs };
|
|
40
|
+
return { command: interceptingCommand(commandArgs) ?? first, commandArgs };
|
|
38
41
|
}
|
|
39
42
|
if (first !== undefined && first.startsWith('--') && isCommand(first.slice(2))) {
|
|
40
43
|
const commandArgs = args.slice(1);
|
|
41
44
|
const command = first.slice(2);
|
|
42
|
-
return { command: (
|
|
45
|
+
return { command: interceptingCommand(commandArgs) ?? command, commandArgs };
|
|
43
46
|
}
|
|
44
47
|
// no command given - everything is an argument to the default
|
|
45
|
-
return { command: (
|
|
48
|
+
return { command: interceptingCommand(args) ?? 'watch', commandArgs: args };
|
|
46
49
|
}
|
|
47
50
|
//# sourceMappingURL=resolveCommand.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveCommand.js","sourceRoot":"","sources":["../../../src/cli/lib/resolveCommand.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"resolveCommand.js","sourceRoot":"","sources":["../../../src/cli/lib/resolveCommand.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAU,CAAA;AAS7E,SAAS,SAAS,CAAC,KAAa;IAC5B,OAAQ,QAA8B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AAC1D,CAAC;AAED,4FAA4F;AAC5F,2FAA2F;AAC3F,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC7B,UAAU,EAAE,IAAI;IAChB,cAAc,EAAE,IAAI;IACpB,UAAU,EAAE,IAAI;IAChB,aAAa,EAAE,IAAI;IACnB,mBAAmB,EAAE,IAAI;IACzB,eAAe,EAAE,IAAI;CACxB,CAAC,CAAA;AAEF,4FAA4F;AAC5F,gDAAgD;AAChD,SAAS,mBAAmB,CAAC,WAAqB;IAC9C,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;QAC/D,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;QACjC,IAAI,GAAG,KAAK,SAAS;YAAE,SAAQ;QAC/B,IAAI,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,QAAQ,EAAE,CAAA,CAAC,yCAAyC;YACpD,SAAQ;QACZ,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,MAAM,CAAA;QACnD,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,SAAS,CAAA;IAC7D,CAAC;IACD,OAAO,SAAS,CAAA;AACpB,CAAC;AAED,4FAA4F;AAC5F,wEAAwE;AACxE,MAAM,UAAU,cAAc,CAAC,IAAc;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;IAErB,IAAI,KAAK,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACjC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,IAAI,KAAK,EAAE,WAAW,EAAE,CAAA;IAC9E,CAAC;IAED,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACjC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAY,CAAA;QACzC,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,WAAW,CAAC,IAAI,OAAO,EAAE,WAAW,EAAE,CAAA;IAChF,CAAC;IAED,8DAA8D;IAC9D,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,IAAI,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAA;AAC/E,CAAC","sourcesContent":["export const commands = ['init', 'once', 'watch', 'help', 'version'] as const\n\nexport type Command = typeof commands[number]\n\nexport interface ResolvedCommand {\n command: Command\n commandArgs: string[]\n}\n\nfunction isCommand(value: string): value is Command {\n return (commands as readonly string[]).includes(value)\n}\n\n// The options that consume the next argument, so `--filter-it -h` is understood as a filter\n// for \"-h\" rather than a request for help. Worst case if this drifts from the option lists\n// in watch.ts and once.ts is that such an argument shows help instead.\nconst valueTakingFlags = new Set([\n '--config', '-c',\n '--test-files', '-t',\n '--filter', '-f',\n '--filter-it', '-i',\n '--filter-describe', '-d',\n '--status-file', '-s'\n])\n\n// --help and --version describe the tool rather than asking it to do anything, so they take\n// over from whatever command they were given to\nfunction interceptingCommand(commandArgs: string[]): Command | undefined {\n for (let position = 0; position < commandArgs.length; position++) {\n const arg = commandArgs[position]\n if (arg === undefined) continue\n if (valueTakingFlags.has(arg)) {\n position++ // skip the value, whatever it looks like\n continue\n }\n if (arg === '--help' || arg === '-h') return 'help'\n if (arg === '--version' || arg === '-v') return 'version'\n }\n return undefined\n}\n\n// Routes the raw argv tail to a command. Extracted from the entry module so the routing can\n// be tested without running a command as a side effect of importing it.\nexport function resolveCommand(args: string[]): ResolvedCommand {\n const first = args[0]\n\n if (first !== undefined && isCommand(first)) {\n const commandArgs = args.slice(1)\n return { command: interceptingCommand(commandArgs) ?? first, commandArgs }\n }\n\n if (first !== undefined && first.startsWith('--') && isCommand(first.slice(2))) {\n const commandArgs = args.slice(1)\n const command = first.slice(2) as Command\n return { command: interceptingCommand(commandArgs) ?? command, commandArgs }\n }\n\n // no command given - everything is an argument to the default\n return { command: interceptingCommand(args) ?? 'watch', commandArgs: args }\n}\n"]}
|
|
@@ -7,6 +7,7 @@ export declare const statusCodes: {
|
|
|
7
7
|
readonly couldNotRun: 3;
|
|
8
8
|
};
|
|
9
9
|
export type StatusCode = typeof statusCodes[keyof typeof statusCodes];
|
|
10
|
+
export declare const statusNames: Record<StatusCode, string>;
|
|
10
11
|
export declare function resetStatusFile(config: FinalConfig): void;
|
|
11
12
|
export declare function writeStatusFile(config: FinalConfig, code: StatusCode, description: string, details?: string[], durationMs?: number): void;
|
|
12
13
|
//# sourceMappingURL=statusFile.d.ts.map
|
|
@@ -13,6 +13,16 @@ export const statusCodes = {
|
|
|
13
13
|
aborted: 2, // the run died partway - a test file failed to load, or a describe threw
|
|
14
14
|
couldNotRun: 3 // nothing ran at all - no test files, or no match for the filters
|
|
15
15
|
};
|
|
16
|
+
// The same states as words, for the agent stream. A number needs the table above to mean
|
|
17
|
+
// anything; a reader of one line should not have to go and find it. Defined here so the two
|
|
18
|
+
// cannot drift apart.
|
|
19
|
+
export const statusNames = {
|
|
20
|
+
[statusCodes.notRunYet]: 'not-run',
|
|
21
|
+
[statusCodes.passed]: 'passed',
|
|
22
|
+
[statusCodes.failed]: 'failed',
|
|
23
|
+
[statusCodes.aborted]: 'aborted',
|
|
24
|
+
[statusCodes.couldNotRun]: 'could-not-run'
|
|
25
|
+
};
|
|
16
26
|
export function resetStatusFile(config) {
|
|
17
27
|
writeStatusFile(config, statusCodes.notRunYet, 'no run has finished yet');
|
|
18
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusFile.js","sourceRoot":"","sources":["../../../src/cli/lib/statusFile.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\n\nimport chalk from '../../packages/chalk.js'\nimport { packageName } from '../../config.js'\nimport { log } from '../../output.js'\n\nimport type { FinalConfig } from '../../types.js'\n\n// The status file holds the result of the latest run: `<code> - <description>` on the first\n// line, then any detail. Watch mode resets it to -1 on startup so a stale result is never\n// mistaken for the current one.\nexport const statusCodes = {\n notRunYet: -1, // watch mode has started, but no run has finished\n passed: 0,\n failed: 1, // tests ran and failed\n aborted: 2, // the run died partway - a test file failed to load, or a describe threw\n couldNotRun: 3 // nothing ran at all - no test files, or no match for the filters\n} as const\n\nexport type StatusCode = typeof statusCodes[keyof typeof statusCodes]\n\nexport function resetStatusFile(config: FinalConfig): void {\n writeStatusFile(config, statusCodes.notRunYet, 'no run has finished yet')\n}\n\nexport function writeStatusFile(\n config: FinalConfig,\n code: StatusCode,\n description: string,\n details: string[] = [],\n durationMs?: number\n): void {\n write(config, [\n `${code} - ${description}`,\n localTime(new Date()),\n ...details,\n ...(durationMs === undefined) ? [] : [`${durationMs.toLocaleString()}ms`]\n ].join('\\n') + '\\n')\n}\n\n// local time, so the file reads the way the terminal beside it does, with the offset so a\n// reader elsewhere can still place it\nfunction localTime(date: Date): string {\n const pad = (value: number): string => String(value).padStart(2, '0')\n // getTimezoneOffset() counts minutes behind UTC, so the sign is the other way round\n const offsetMinutes = -date.getTimezoneOffset()\n const offset = `${(offsetMinutes < 0) ? '-' : '+'}`\n + `${pad(Math.floor(Math.abs(offsetMinutes) / 60))}:${pad(Math.abs(offsetMinutes) % 60)}`\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n + ` ${offset}`\n}\n\nfunction write(config: FinalConfig, contents: string): void {\n const statusFile = config.watch.statusFile\n if (typeof statusFile !== 'string' || statusFile === '') return\n\n const location = path.resolve(process.cwd(), statusFile)\n // written via a temp file and renamed, so a poller never reads a half written status\n const tempLocation = `${location}.${process.pid}.tmp`\n try {\n fs.mkdirSync(path.dirname(location), { recursive: true })\n fs.writeFileSync(tempLocation, contents)\n fs.renameSync(tempLocation, location)\n } catch (error) {\n // never let the status file take the run down with it\n log(chalk.mediumred(\n `[${packageName}] Could not write status file ${location}: ${String(error)}`\n ))\n try { fs.rmSync(tempLocation, { force: true }) } catch { /* nothing more to do */ }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"statusFile.js","sourceRoot":"","sources":["../../../src/cli/lib/statusFile.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs'\nimport path from 'path'\n\nimport chalk from '../../packages/chalk.js'\nimport { packageName } from '../../config.js'\nimport { log } from '../../output.js'\n\nimport type { FinalConfig } from '../../types.js'\n\n// The status file holds the result of the latest run: `<code> - <description>` on the first\n// line, then any detail. Watch mode resets it to -1 on startup so a stale result is never\n// mistaken for the current one.\nexport const statusCodes = {\n notRunYet: -1, // watch mode has started, but no run has finished\n passed: 0,\n failed: 1, // tests ran and failed\n aborted: 2, // the run died partway - a test file failed to load, or a describe threw\n couldNotRun: 3 // nothing ran at all - no test files, or no match for the filters\n} as const\n\nexport type StatusCode = typeof statusCodes[keyof typeof statusCodes]\n\n// The same states as words, for the agent stream. A number needs the table above to mean\n// anything; a reader of one line should not have to go and find it. Defined here so the two\n// cannot drift apart.\nexport const statusNames: Record<StatusCode, string> = {\n [statusCodes.notRunYet]: 'not-run',\n [statusCodes.passed]: 'passed',\n [statusCodes.failed]: 'failed',\n [statusCodes.aborted]: 'aborted',\n [statusCodes.couldNotRun]: 'could-not-run'\n}\n\nexport function resetStatusFile(config: FinalConfig): void {\n writeStatusFile(config, statusCodes.notRunYet, 'no run has finished yet')\n}\n\nexport function writeStatusFile(\n config: FinalConfig,\n code: StatusCode,\n description: string,\n details: string[] = [],\n durationMs?: number\n): void {\n write(config, [\n `${code} - ${description}`,\n localTime(new Date()),\n ...details,\n ...(durationMs === undefined) ? [] : [`${durationMs.toLocaleString()}ms`]\n ].join('\\n') + '\\n')\n}\n\n// local time, so the file reads the way the terminal beside it does, with the offset so a\n// reader elsewhere can still place it\nfunction localTime(date: Date): string {\n const pad = (value: number): string => String(value).padStart(2, '0')\n // getTimezoneOffset() counts minutes behind UTC, so the sign is the other way round\n const offsetMinutes = -date.getTimezoneOffset()\n const offset = `${(offsetMinutes < 0) ? '-' : '+'}`\n + `${pad(Math.floor(Math.abs(offsetMinutes) / 60))}:${pad(Math.abs(offsetMinutes) % 60)}`\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n + ` ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n + ` ${offset}`\n}\n\nfunction write(config: FinalConfig, contents: string): void {\n const statusFile = config.watch.statusFile\n if (typeof statusFile !== 'string' || statusFile === '') return\n\n const location = path.resolve(process.cwd(), statusFile)\n // written via a temp file and renamed, so a poller never reads a half written status\n const tempLocation = `${location}.${process.pid}.tmp`\n try {\n fs.mkdirSync(path.dirname(location), { recursive: true })\n fs.writeFileSync(tempLocation, contents)\n fs.renameSync(tempLocation, location)\n } catch (error) {\n // never let the status file take the run down with it\n log(chalk.mediumred(\n `[${packageName}] Could not write status file ${location}: ${String(error)}`\n ))\n try { fs.rmSync(tempLocation, { force: true }) } catch { /* nothing more to do */ }\n }\n}\n"]}
|
package/dist/cli/once.js
CHANGED
|
@@ -5,6 +5,7 @@ import { setEnv } from './lib/setEnv.js';
|
|
|
5
5
|
import { findingTestsMessage } from './lib/findingTestsMessage.js';
|
|
6
6
|
import { foundTestsMessage } from './lib/foundTestsMessage.js';
|
|
7
7
|
import { isTest } from './lib/isTest.js';
|
|
8
|
+
import { exitCodes } from './lib/exitCodes.js';
|
|
8
9
|
export async function once() {
|
|
9
10
|
const startTime = new Date().getTime();
|
|
10
11
|
const { parseCommandLineArguments } = await import('./lib/parseCommandLineArguments.js');
|
|
@@ -108,7 +109,7 @@ export async function once() {
|
|
|
108
109
|
// effects are left behind
|
|
109
110
|
const { doUserTeardown } = await import('./lib/doUserTeardown.js');
|
|
110
111
|
await doUserTeardown(config);
|
|
111
|
-
process.exit(
|
|
112
|
+
process.exit(exitCodes.noTestFiles);
|
|
112
113
|
}
|
|
113
114
|
agentOut(chalk.brightblue(foundTestsMessage(testFiles.size)));
|
|
114
115
|
// testFiles.size is guaranteed non-zero here - the no-tests case exited above
|
|
@@ -119,8 +120,11 @@ export async function once() {
|
|
|
119
120
|
it: filterIt,
|
|
120
121
|
describe: filterDescribe
|
|
121
122
|
});
|
|
122
|
-
//
|
|
123
|
-
|
|
123
|
+
// a distinct code from noTestFiles, so callers can tell "no test file matched the
|
|
124
|
+
// pattern" from "the files matched but the filters selected nothing in them"
|
|
125
|
+
const exitCode = (testsResult === null)
|
|
126
|
+
? exitCodes.noMatchingTests
|
|
127
|
+
: (testsResult) ? exitCodes.passed : exitCodes.testErrors;
|
|
124
128
|
const time = new Date().getTime() - startTime;
|
|
125
129
|
agentOut(chalk.grey `\n${time.toLocaleString()}ms\n`);
|
|
126
130
|
const { doUserTeardown } = await import('./lib/doUserTeardown.js');
|
package/dist/cli/once.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"once.js","sourceRoot":"","sources":["../../src/cli/once.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport chalk from '../packages/chalk.js'\n\nimport { packageName, configFileNames } from '../config.js'\nimport { out, log } from '../output.js'\n\nimport type { AllowedArguments, OutFn } from '../types.js'\nimport { setEnv } from './lib/setEnv.js'\nimport { findingTestsMessage } from './lib/findingTestsMessage.js'\nimport { foundTestsMessage } from './lib/foundTestsMessage.js'\nimport { isTest } from './lib/isTest.js'\n\n\nexport async function once(): Promise<void> {\n const startTime = new Date().getTime()\n\n const { parseCommandLineArguments } = await import('./lib/parseCommandLineArguments.js')\n const allowedClArguments: AllowedArguments = {\n config: {\n flag: 'c',\n hasValue: true\n },\n 'test-files': {\n flag: 't',\n hasValue: true\n },\n filter: {\n flag: 'f',\n hasValue: true\n },\n 'filter-it': {\n flag: 'i',\n hasValue: true\n },\n 'filter-describe': {\n flag: 'd',\n hasValue: true\n },\n 'fail-fast': {\n flag: 'x',\n hasValue: false\n },\n 'agent': {\n hasValue: false\n }\n }\n const clArguments = parseCommandLineArguments(allowedClArguments)\n //debug('clArguments', clArguments)\n\n // agent mode owns stdout: the usual per-test output is routed into a sink, leaving the\n // failures and the summary. Announced before the config loads, as in watch mode.\n const agentMode = clArguments.agent === true\n const {\n agentInit, agentFound, agentRunStart, agentDone, agentSetRunReason, agentSilentOut\n } = await import('./lib/agentReport.js')\n agentInit(agentMode)\n agentSetRunReason('once')\n const agentOut: OutFn = (agentMode) ? agentSilentOut : out\n const runsOut = (agentMode) ? agentSilentOut : undefined\n\n agentOut(chalk.brightblue`[${packageName}]...`)\n\n setEnv()\n\n const { loadConfig } = await import('./lib/loadConfig.js')\n const userConfig = (typeof clArguments.config === 'string')\n ? await loadConfig(clArguments.config)\n : await loadConfig()\n\n if ('test-files' in clArguments) {\n const testFilesPattern = clArguments['test-files']\n if (typeof testFilesPattern !== 'string' || testFilesPattern === '') {\n throw new Error('No pattern specified for --test-files or -t argument')\n }\n //console.log('Looking for tests using pattern: ' + testFilesPattern) // eslint-disable-line no-console\n userConfig.tests = testFilesPattern\n }\n\n if ('filter' in clArguments) {\n const filter = clArguments.filter\n if (typeof filter !== 'string' || filter === '') {\n throw new Error('No pattern specified for --filter or -f argument')\n }\n //console.log('Filtering tests using pattern: ' + filter) // eslint-disable-line no-console\n userConfig.testsFilter = filter\n }\n\n const filterIt = clArguments['filter-it']\n const filterDescribe = clArguments['filter-describe']\n if (filterIt === true || filterIt === '') throw new Error('No substring specified for --filter-it')\n if (filterDescribe === true || filterDescribe === '') {\n throw new Error('No substring specified for --filter-describe')\n }\n\n const { applyConfigDefaults } = await import('./lib/applyConfigDefaults.js')\n const config = applyConfigDefaults(userConfig)\n // both flags are the only source: assigned rather than or'd, so a config file cannot\n // silently truncate every run, or swap the output format out from under a caller\n config.failFast = clArguments['fail-fast'] === true\n config.watch.agent = agentMode\n\n const { validateConfig } = await import('./lib/validateConfig.js')\n validateConfig(config)\n\n //console.log('run setup')\n //console.log(util.inspect(config))\n const { doUserSetup } = await import('./lib/doUserSetup.js')\n await doUserSetup(config)\n //console.log('ran setup')\n \n agentOut(chalk.brightblue(findingTestsMessage(config)))\n const { findTestFiles } = await import('./lib/findTestFiles.js')\n const testFiles = await findTestFiles(config)\n agentFound(config, testFiles.size)\n\n if (testFiles.size === 0) {\n agentOut(chalk.mediumred(`[${packageName}] Found no tests.`\n + '\\nCreate tests matching the patterns, '\n + `\\nor set new patterns in ${configFileNames.map(x => `${x}.js/cjs/mjs`).join(' or ')}`))\n\n const endTime = new Date().getTime()\n const time = endTime - startTime\n agentOut(chalk.brightblue(`[${packageName}] time: ${time.toLocaleString()}ms`))\n\n // reported as a run, so a reader waiting on a done line always gets one\n const { statusCodes } = await import('./lib/statusFile.js')\n const run = agentRunStart(config, 0)\n agentDone(config, run, statusCodes.couldNotRun, 'no test files found', null, time, 0)\n\n // setup has already run by this point, so teardown must run too, or its side\n // effects are left behind\n const { doUserTeardown } = await import('./lib/doUserTeardown.js')\n await doUserTeardown(config)\n\n process.exit(4)\n }\n\n agentOut(chalk.brightblue(foundTestsMessage(testFiles.size)))\n\n // testFiles.size is guaranteed non-zero here - the no-tests case exited above\n const { runTests } = await import('./lib/runTests.js')\n if (!agentMode) log(chalk.brightblue`running tests...`)\n const testsResult = await runTests(\n config, testFiles,\n undefined,\n (isTest) ? 'refresh' : 'cached',\n runsOut,\n {\n it: filterIt,\n describe: filterDescribe\n }\n )\n // 5 - not 4 - so callers can tell \"no test files matched\" from \"filters matched nothing\"\n const exitCode = testsResult === null ? 5 : testsResult ? 0 : 3\n\n const time = new Date().getTime() - startTime\n agentOut(chalk.grey`\\n${time.toLocaleString()}ms\\n`)\n\n const { doUserTeardown } = await import('./lib/doUserTeardown.js')\n await doUserTeardown(config)\n\n process.exit(exitCode)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"once.js","sourceRoot":"","sources":["../../src/cli/once.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport chalk from '../packages/chalk.js'\n\nimport { packageName, configFileNames } from '../config.js'\nimport { out, log } from '../output.js'\n\nimport type { AllowedArguments, OutFn } from '../types.js'\nimport { setEnv } from './lib/setEnv.js'\nimport { findingTestsMessage } from './lib/findingTestsMessage.js'\nimport { foundTestsMessage } from './lib/foundTestsMessage.js'\nimport { isTest } from './lib/isTest.js'\nimport { exitCodes } from './lib/exitCodes.js'\n\n\nexport async function once(): Promise<void> {\n const startTime = new Date().getTime()\n\n const { parseCommandLineArguments } = await import('./lib/parseCommandLineArguments.js')\n const allowedClArguments: AllowedArguments = {\n config: {\n flag: 'c',\n hasValue: true\n },\n 'test-files': {\n flag: 't',\n hasValue: true\n },\n filter: {\n flag: 'f',\n hasValue: true\n },\n 'filter-it': {\n flag: 'i',\n hasValue: true\n },\n 'filter-describe': {\n flag: 'd',\n hasValue: true\n },\n 'fail-fast': {\n flag: 'x',\n hasValue: false\n },\n 'agent': {\n hasValue: false\n }\n }\n const clArguments = parseCommandLineArguments(allowedClArguments)\n //debug('clArguments', clArguments)\n\n // agent mode owns stdout: the usual per-test output is routed into a sink, leaving the\n // failures and the summary. Announced before the config loads, as in watch mode.\n const agentMode = clArguments.agent === true\n const {\n agentInit, agentFound, agentRunStart, agentDone, agentSetRunReason, agentSilentOut\n } = await import('./lib/agentReport.js')\n agentInit(agentMode)\n agentSetRunReason('once')\n const agentOut: OutFn = (agentMode) ? agentSilentOut : out\n const runsOut = (agentMode) ? agentSilentOut : undefined\n\n agentOut(chalk.brightblue`[${packageName}]...`)\n\n setEnv()\n\n const { loadConfig } = await import('./lib/loadConfig.js')\n const userConfig = (typeof clArguments.config === 'string')\n ? await loadConfig(clArguments.config)\n : await loadConfig()\n\n if ('test-files' in clArguments) {\n const testFilesPattern = clArguments['test-files']\n if (typeof testFilesPattern !== 'string' || testFilesPattern === '') {\n throw new Error('No pattern specified for --test-files or -t argument')\n }\n //console.log('Looking for tests using pattern: ' + testFilesPattern) // eslint-disable-line no-console\n userConfig.tests = testFilesPattern\n }\n\n if ('filter' in clArguments) {\n const filter = clArguments.filter\n if (typeof filter !== 'string' || filter === '') {\n throw new Error('No pattern specified for --filter or -f argument')\n }\n //console.log('Filtering tests using pattern: ' + filter) // eslint-disable-line no-console\n userConfig.testsFilter = filter\n }\n\n const filterIt = clArguments['filter-it']\n const filterDescribe = clArguments['filter-describe']\n if (filterIt === true || filterIt === '') throw new Error('No substring specified for --filter-it')\n if (filterDescribe === true || filterDescribe === '') {\n throw new Error('No substring specified for --filter-describe')\n }\n\n const { applyConfigDefaults } = await import('./lib/applyConfigDefaults.js')\n const config = applyConfigDefaults(userConfig)\n // both flags are the only source: assigned rather than or'd, so a config file cannot\n // silently truncate every run, or swap the output format out from under a caller\n config.failFast = clArguments['fail-fast'] === true\n config.watch.agent = agentMode\n\n const { validateConfig } = await import('./lib/validateConfig.js')\n validateConfig(config)\n\n //console.log('run setup')\n //console.log(util.inspect(config))\n const { doUserSetup } = await import('./lib/doUserSetup.js')\n await doUserSetup(config)\n //console.log('ran setup')\n \n agentOut(chalk.brightblue(findingTestsMessage(config)))\n const { findTestFiles } = await import('./lib/findTestFiles.js')\n const testFiles = await findTestFiles(config)\n agentFound(config, testFiles.size)\n\n if (testFiles.size === 0) {\n agentOut(chalk.mediumred(`[${packageName}] Found no tests.`\n + '\\nCreate tests matching the patterns, '\n + `\\nor set new patterns in ${configFileNames.map(x => `${x}.js/cjs/mjs`).join(' or ')}`))\n\n const endTime = new Date().getTime()\n const time = endTime - startTime\n agentOut(chalk.brightblue(`[${packageName}] time: ${time.toLocaleString()}ms`))\n\n // reported as a run, so a reader waiting on a done line always gets one\n const { statusCodes } = await import('./lib/statusFile.js')\n const run = agentRunStart(config, 0)\n agentDone(config, run, statusCodes.couldNotRun, 'no test files found', null, time, 0)\n\n // setup has already run by this point, so teardown must run too, or its side\n // effects are left behind\n const { doUserTeardown } = await import('./lib/doUserTeardown.js')\n await doUserTeardown(config)\n\n process.exit(exitCodes.noTestFiles)\n }\n\n agentOut(chalk.brightblue(foundTestsMessage(testFiles.size)))\n\n // testFiles.size is guaranteed non-zero here - the no-tests case exited above\n const { runTests } = await import('./lib/runTests.js')\n if (!agentMode) log(chalk.brightblue`running tests...`)\n const testsResult = await runTests(\n config, testFiles,\n undefined,\n (isTest) ? 'refresh' : 'cached',\n runsOut,\n {\n it: filterIt,\n describe: filterDescribe\n }\n )\n // a distinct code from noTestFiles, so callers can tell \"no test file matched the\n // pattern\" from \"the files matched but the filters selected nothing in them\"\n const exitCode = (testsResult === null)\n ? exitCodes.noMatchingTests\n : (testsResult) ? exitCodes.passed : exitCodes.testErrors\n\n const time = new Date().getTime() - startTime\n agentOut(chalk.grey`\\n${time.toLocaleString()}ms\\n`)\n\n const { doUserTeardown } = await import('./lib/doUserTeardown.js')\n await doUserTeardown(config)\n\n process.exit(exitCode)\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { packageName } from '../config.js';
|
|
5
|
+
import { out } from '../output.js';
|
|
6
|
+
// Read at run time rather than baked in at build time, so it cannot drift from the version
|
|
7
|
+
// actually installed. dist/cli/version.js sits two levels under the package root, both in
|
|
8
|
+
// this repo and once published.
|
|
9
|
+
export function version() {
|
|
10
|
+
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
11
|
+
let versionNumber = 'unknown';
|
|
12
|
+
try {
|
|
13
|
+
const raw = readFileSync(join(packageRoot, 'package.json'), 'utf8');
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
15
|
+
const packageJson = JSON.parse(raw);
|
|
16
|
+
versionNumber = packageJson.version ?? 'unknown';
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// reporting "unknown" beats failing to answer `--version` at all
|
|
20
|
+
}
|
|
21
|
+
out(`${packageName} ${versionNumber}`);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/cli/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAElC,2FAA2F;AAC3F,0FAA0F;AAC1F,gCAAgC;AAChC,MAAM,UAAU,OAAO;IACnB,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IAE7E,IAAI,aAAa,GAAG,SAAS,CAAA;IAC7B,IAAI,CAAC;QACD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAA;QACnE,mEAAmE;QACnE,MAAM,WAAW,GAAyB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACzD,aAAa,GAAG,WAAW,CAAC,OAAO,IAAI,SAAS,CAAA;IACpD,CAAC;IAAC,MAAM,CAAC;QACL,iEAAiE;IACrE,CAAC;IAED,GAAG,CAAC,GAAG,WAAW,IAAI,aAAa,EAAE,CAAC,CAAA;AAC1C,CAAC","sourcesContent":["import { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { packageName } from '../config.js'\nimport { out } from '../output.js'\n\n// Read at run time rather than baked in at build time, so it cannot drift from the version\n// actually installed. dist/cli/version.js sits two levels under the package root, both in\n// this repo and once published.\nexport function version(): void {\n const packageRoot = join(dirname(fileURLToPath(import.meta.url)), '..', '..')\n\n let versionNumber = 'unknown'\n try {\n const raw = readFileSync(join(packageRoot, 'package.json'), 'utf8')\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const packageJson: { version?: string } = JSON.parse(raw)\n versionNumber = packageJson.version ?? 'unknown'\n } catch {\n // reporting \"unknown\" beats failing to answer `--version` at all\n }\n\n out(`${packageName} ${versionNumber}`)\n}\n"]}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -173,6 +173,61 @@ To specific an alternate config, use -c or --config
|
|
|
173
173
|
npx sxy-test-runner --config sxy-test-runner.alternate.config.js
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
+
## Reporting for an agent
|
|
177
|
+
|
|
178
|
+
`--agent` replaces the usual output with one line per event, for something reading the
|
|
179
|
+
process's stdout rather than a terminal. It works in both modes. In `once` it leaves only
|
|
180
|
+
the failures and the summary - passing tests are not listed - and the exit code is
|
|
181
|
+
unchanged, so the run stays usable in CI:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
npx sxy-test-runner once --agent
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
[sxyt] init | pid=16744
|
|
189
|
+
[sxyt] found | files=2
|
|
190
|
+
[sxyt] running | n=1 | files=2 | why=once | covers=all
|
|
191
|
+
[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
|
|
192
|
+
[sxyt] done | n=1 | status=failed | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=150
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Every line is one complete event: `[sxyt] <event>` followed by ` | <key>=<value>` pairs.
|
|
196
|
+
Newlines in free text are collapsed and any `|` becomes `/`, so one event never spans two
|
|
197
|
+
lines. In watch mode the stream continues with `change`, further `run`/`done` pairs, and a
|
|
198
|
+
`watching` line once the watcher is live.
|
|
199
|
+
|
|
200
|
+
`failing` counts the test files still known to be failing across the whole session, and
|
|
201
|
+
`covers` says what the run was built from, so a partial re-run cannot be mistaken for a
|
|
202
|
+
clean suite. `status` is one of `passed`, `failed`, `aborted` (a test file would not load, or
|
|
203
|
+
a describe threw) or `could-not-run` - the same states as the status file table above, named
|
|
204
|
+
rather than numbered. It is not the process exit code: read that from the process.
|
|
205
|
+
|
|
206
|
+
## Stopping at the first failure
|
|
207
|
+
|
|
208
|
+
`-x` or `--fail-fast` stops a `once` run at the first failing test, erroring describe, or
|
|
209
|
+
test file that will not load. The failure is reported in full and nothing further starts;
|
|
210
|
+
`afterEach*` hooks and `teardown` still run:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
npx sxy-test-runner once --fail-fast
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The tallies then count only what ran, so the summary ends with `stopped at the first
|
|
217
|
+
failure (failFast)`. There is no watch equivalent, and no config option - it describes a
|
|
218
|
+
single run rather than a project.
|
|
219
|
+
|
|
220
|
+
## Unrecognised options
|
|
221
|
+
|
|
222
|
+
`--help` (`-h`) and `--version` (`-v`) work on any command and take over from it, so
|
|
223
|
+
`once --help` describes `once` rather than running the suite.
|
|
224
|
+
|
|
225
|
+
An option that the command does not accept is an error: the command exits without running
|
|
226
|
+
anything, rather than warning and carrying on. A typo like `--fitler-it` would otherwise
|
|
227
|
+
quietly run the whole suite and report a pass. Options are per-command, so `--fail-fast`
|
|
228
|
+
belongs to `once` and `--status-file` to `watch`. Run `npx sxy-test-runner help`, or add
|
|
229
|
+
`--help` to any command, for the current list.
|
|
230
|
+
|
|
176
231
|
# How it works
|
|
177
232
|
|
|
178
233
|
sxy-test-runner runs all tests inside one node process, to improve performance (this framework far exceeds mocha in parallel mode, which is needed to test ESM files, due to this).
|
|
@@ -215,7 +270,11 @@ If your code is isolated, and asyncronous, you can use parallel exeecution modes
|
|
|
215
270
|
|
|
216
271
|
Module dependencies are identified using dependency-tree. Dynamic imports will not be detected and will not trigger test re-runs.
|
|
217
272
|
|
|
218
|
-
|
|
273
|
+
To re-run every test on any watched change instead, set `config.watch.dependencyEvaluation`
|
|
274
|
+
to `false`. The dependency analysis is then skipped entirely - no trees are built at startup,
|
|
275
|
+
which is the slowest part of it on a large suite - and the blind spot above stops mattering,
|
|
276
|
+
because nothing is being predicted. On a small or fast suite this is often the better
|
|
277
|
+
setting; on a large one it trades a slower re-run for a complete one.
|
|
219
278
|
|
|
220
279
|
## Failing test re-running
|
|
221
280
|
|
|
@@ -293,8 +352,18 @@ describe('something', ({it, beforeEachTest, afterEachTest, afterDescribe}) => {
|
|
|
293
352
|
|
|
294
353
|
# Exit codes for `once`
|
|
295
354
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
355
|
+
```
|
|
356
|
+
0: success
|
|
357
|
+
20: test errors - a failed assertion, a test file that would not load,
|
|
358
|
+
or a describe that threw
|
|
359
|
+
21: no test file matched the `tests` pattern
|
|
360
|
+
22: test files matched, but --filter-it/--filter-describe selected nothing
|
|
361
|
+
23: an option was not recognised, or one needing a value was given none
|
|
362
|
+
24: reserved for an error in sxy-test itself - not currently emitted
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
These start at 20 to stay clear of Node's reserved codes (1-14) and of signal exits
|
|
366
|
+
(over 128), so a non-zero code is always sxy-test's own.
|
|
367
|
+
|
|
368
|
+
It may also crash due to exceptions without giving any of these codes, in which case Node
|
|
369
|
+
supplies the code - usually `1` for an uncaught exception.
|