test-inventory 0.8.0 → 0.9.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +16 -11
  3. package/dist/case-titles.d.ts +9 -0
  4. package/dist/case-titles.js +82 -0
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.js +4 -2
  7. package/dist/config-dependencies.js +19 -0
  8. package/dist/config-runs.d.ts +95 -3
  9. package/dist/config-runs.js +752 -16
  10. package/dist/config.d.ts +24 -0
  11. package/dist/config.js +139 -19
  12. package/dist/diagnostics.js +14 -1
  13. package/dist/extract.d.ts +2 -0
  14. package/dist/extract.js +36 -10
  15. package/dist/files.js +2 -1
  16. package/dist/filter.js +1 -0
  17. package/dist/framework-version.d.ts +4 -2
  18. package/dist/framework-version.js +170 -15
  19. package/dist/frameworks/bun.d.ts +42 -0
  20. package/dist/frameworks/bun.js +272 -0
  21. package/dist/frameworks/common.d.ts +3 -1
  22. package/dist/frameworks/common.js +11 -9
  23. package/dist/frameworks/cypress.d.ts +31 -0
  24. package/dist/frameworks/cypress.js +70 -0
  25. package/dist/frameworks/jasmine.d.ts +28 -0
  26. package/dist/frameworks/jasmine.js +218 -0
  27. package/dist/frameworks/jest.js +1 -1
  28. package/dist/frameworks/mocha.d.ts +50 -0
  29. package/dist/frameworks/mocha.js +306 -0
  30. package/dist/frameworks/node.d.ts +30 -0
  31. package/dist/frameworks/node.js +268 -0
  32. package/dist/frameworks/playwright.d.ts +13 -0
  33. package/dist/frameworks/playwright.js +57 -0
  34. package/dist/frameworks/test-functions.d.ts +26 -1
  35. package/dist/frameworks/test-functions.js +64 -22
  36. package/dist/frameworks/vitest.d.ts +9 -1
  37. package/dist/frameworks/vitest.js +36 -5
  38. package/dist/frameworks/webdriverio.d.ts +27 -0
  39. package/dist/frameworks/webdriverio.js +37 -0
  40. package/dist/model.d.ts +4 -0
  41. package/dist/node-scripts.d.ts +6 -0
  42. package/dist/node-scripts.js +85 -0
  43. package/dist/options.d.ts +13 -4
  44. package/dist/options.js +22 -3
  45. package/dist/records.d.ts +8 -2
  46. package/dist/records.js +50 -13
  47. package/dist/resolve.d.ts +21 -4
  48. package/dist/resolve.js +52 -16
  49. package/dist/scan-file.d.ts +8 -0
  50. package/dist/scan-file.js +16 -2
  51. package/dist/scan.js +42 -7
  52. package/dist/toml.d.ts +13 -0
  53. package/dist/toml.js +172 -0
  54. package/dist/types.d.ts +19 -9
  55. package/dist/values.d.ts +5 -0
  56. package/dist/values.js +13 -0
  57. package/dist/yaml.d.ts +12 -0
  58. package/dist/yaml.js +148 -0
  59. package/package.json +7 -6
  60. package/schema/v1.json +26 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,134 @@
1
1
  # test-inventory
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#88](https://github.com/Huldoser/test-inventory/pull/88) [`2d370c9`](https://github.com/Huldoser/test-inventory/commit/2d370c9030c5fafae900d2442d75e70781a5b938) - Bun 1.2.23 and newer: `--framework bun`, also found from a package.json script that runs `bun test` or a
8
+ `bunfig.toml`. Its version comes from `.bun-version`, `packageManager` or `engines.bun`, else `@types/bun` or
9
+ `bun-types`. The scan reads Bun's globals and imports of `bun:test`, `@jest/globals` and `vitest`, which Bun rewrites;
10
+ chains of `.skip`, `.todo`, `.failing`, `.only`, `.concurrent`, `.serial` and `.each` in any order, refusing the
11
+ combinations Bun refuses; the conditions `.if`, `.skipIf`, `.todoIf`, `.failingIf`, `.concurrentIf` and `.serialIf`;
12
+ options second or third, a number as the timeout or `{ timeout, retry, repeats }`; and `.each` titles as Bun formats
13
+ them. The files are Bun's `*.test.*`, `*_test.*`, `*.spec.*` and `*_spec.*` under `bunfig.toml`'s `[test] root`, less
14
+ `pathIgnorePatterns`, with its `retry` and `concurrentTestGlob` applied. A small TOML reader reads `bunfig.toml`.
15
+
16
+ The oldest version a framework supports may now name a patch, as Bun's 1.2.23 does.
17
+
18
+ - [#87](https://github.com/Huldoser/test-inventory/pull/87) [`ae2bd90`](https://github.com/Huldoser/test-inventory/commit/ae2bd909d769ea5e21200fceb3d9f081de7661db) - `dependencyHash` follows imports through the config a file runs with: Vite's `resolve.alias` and Vitest's
19
+ `test.alias`, as an object or a list of `{ find, replacement }` with a name or a regex, and Jest's `moduleNameMapper`,
20
+ `modulePaths` and `moduleDirectories`, with `<rootDir>`. They were read from tsconfig `paths` only, so tests that
21
+ imported through such aliases had `hasUnfollowedDependencies` and a hash that missed those files. A mapping to no file
22
+ is a `dependency-not-followed` warning that names it.
23
+
24
+ - [#83](https://github.com/Huldoser/test-inventory/pull/83) [`9ef9c59`](https://github.com/Huldoser/test-inventory/commit/9ef9c5959b908191e8015c8c880a3c476f43fccd) - Cypress 13 and newer: `--framework cypress`, also found from a `cypress` package or a `cypress.config.*` file. Tests are
25
+ read as Mocha's, with Cypress's test config before the function: `retries`, of which `cypress run` takes `runMode`,
26
+ `browser` as a skip condition, `@cypress/grep`'s `tags`, and from Cypress 16 an error for `env`, which Cypress refuses
27
+ there. `this.timeout()` is not read, as Cypress has only command timeouts; `this.retries()` in a describe and
28
+ `.retries()` after `it()` or `describe()` are errors, as Cypress throws on them while it loads the spec. The config's
29
+ testing types are the projects, `e2e` and `component`, each with its `specPattern`, `excludeSpecPattern` and `retries`.
30
+
31
+ A project whose package.json lists `cypress` next to another runner, as Jest or Vitest for unit tests, now needs
32
+ `--framework`, as the scan asks for one whenever it finds several runners.
33
+
34
+ - [#82](https://github.com/Huldoser/test-inventory/pull/82) [`04e7328`](https://github.com/Huldoser/test-inventory/commit/04e73289dd946bd4c92ec774510ebc369ee80699) - Jasmine 4 and newer: `--framework jasmine`, also found from a `jasmine` or `karma-jasmine` package or a
35
+ `spec/support/jasmine.*` config. The scan reads `describe`, `it` and their `x` and `f` forms with Jasmine's focus, a
36
+ spec without a function as pending, `pending(reason)` in a spec or its `beforeEach` or `afterEach`, from Jasmine 7
37
+ `notApplicable(reason)`, the hooks, and a spec's timeout. The config's `spec_dir` and `spec_files`, with `!`
38
+ exclusions, decide the files; without a config, as in Karma and Angular projects, the files given by glob run. A call
39
+ Jasmine refuses stops the whole run at the top level of a file, and is an error that leaves the file's tests as they
40
+ are inside a describe, which Jasmine catches.
41
+
42
+ - [#81](https://github.com/Huldoser/test-inventory/pull/81) [`85fb8a9`](https://github.com/Huldoser/test-inventory/commit/85fb8a90998f88641400ebb8a6db3b392e2b54d2) - Mocha 10 and newer: `--framework mocha`, also found from a `mocha` package, a `.mocharc.*` file or a `mocha` key in
43
+ `package.json`. The scan reads `describe`, `context`, `it`, `specify`, their `x` forms, `.only` and `.skip`, a test
44
+ without a function as pending, `ui: tdd`'s `suite` and `test`, the hooks, `this.timeout()`, `this.retries()` and
45
+ `this.skip()` where `this` is Mocha's, and `.timeout()` and `.retries()` after `it()` and `describe()`. The config's
46
+ `spec`, `extension`, `recursive`, `ignore` and `file` decide the files, and its `timeout` and `retries` apply to every
47
+ test; a YAML config is read too. A describe without a function, `it()` or `describe()` without a title, and an
48
+ unknown chain such as `it.concurrent` are errors, as Mocha then runs no test.
49
+
50
+ A project whose package.json lists `mocha` next to another runner now needs `--framework`, as the scan asks for one
51
+ whenever it finds several runners.
52
+
53
+ - [#86](https://github.com/Huldoser/test-inventory/pull/86) [`1085fb7`](https://github.com/Huldoser/test-inventory/commit/1085fb74b85eefe64c6bdd217b2c19c2946441d4) - node:test on Node.js 22 and newer: `--framework node`, also found from a package.json script that runs `node --test`
54
+ or a `node.config.json`. Its version is the Node.js version the project pins in `.node-version`, `.nvmrc`, Volta's
55
+ `node` or `engines.node`. The scan reads `test`, `it`, `describe` and `suite` from `node:test`, imported or required,
56
+ with `.skip`, `.todo`, `.only` and from 24.14 `.expectFailure`, and the options `skip`, `todo`, `only`, `timeout`,
57
+ `concurrency`, `expectFailure` and from 24.19 and 26.2 `tags`; `t.skip()` and `t.todo()` in a test's body; and the
58
+ hooks. The files are those the script gives `node --test`, or Node.js's default patterns, with TypeScript files from
59
+ 22.18.
60
+
61
+ A test declared in a test's body, with `t.test()` or `test()`, is a subtest that node:test runs while it runs the test:
62
+ it is listed with the test's title in `suitePath` and the test's id in the new key `parentTestId`, which is `null` for
63
+ every other test.
64
+
65
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - A new `invalid-fixture-argument` error: a rest property among a Playwright function's fixture arguments, as
66
+ `async ({ page, ...fixtures }) => {}` in a test, hook, `test.skip()` condition or `test.extend()` fixture written in
67
+ the test file. Playwright refuses it and runs no tests at all, so the file's tests are now `notLoaded` instead of
68
+ `active`. A nested rest, as `({ page: { mouse, ...pointer } })`, and a `test.step()` callback are allowed, as
69
+ Playwright allows them.
70
+
71
+ - [#85](https://github.com/Huldoser/test-inventory/pull/85) [`90df9aa`](https://github.com/Huldoser/test-inventory/commit/90df9aa07741fab403470e653595b1b644b35dc3) - `scan()` takes a `signal`, an `AbortSignal`: the scan checks it before it starts and between files, and rejects with
72
+ the signal's `reason` once it aborts, so a server can stop a scan its client no longer waits for.
73
+
74
+ - [#85](https://github.com/Huldoser/test-inventory/pull/85) [`90df9aa`](https://github.com/Huldoser/test-inventory/commit/90df9aa07741fab403470e653595b1b644b35dc3) - A Vitest `.only` that `allowOnly` makes fail is a `forbidden-only` diagnostic, as for Playwright's `forbidOnly`: an
75
+ error when the config sets `test.allowOnly` to `false`, and a warning when it is unset, as Vitest then turns it off in
76
+ CI, or depends on a condition. Vitest marks such a test failed instead of running it.
77
+
78
+ - [#84](https://github.com/Huldoser/test-inventory/pull/84) [`c68bac7`](https://github.com/Huldoser/test-inventory/commit/c68bac7442f7d5197c8f67bfbec66a8c9ee3f4d5) - WebdriverIO 9 and newer: `--framework webdriverio`, also found from a `@wdio/cli` package or a `wdio.conf.*` file. The
79
+ config's `config` export, ES or CommonJS, decides the files: `specs`, with the nested lists that group files into one
80
+ worker, less `exclude`, matched as WebdriverIO matches them. Its `framework` decides how the tests are read, Mocha by
81
+ default or Jasmine, with `mochaOpts.timeout`, `retries` and `ui`, or `jasmineOpts.defaultTimeoutInterval`, on every
82
+ test; Cucumber is reported, as its feature files aren't read. A number after a test's function is WebdriverIO's retry
83
+ count, and with Jasmine a second one, after the timeout. Mocha's and Jasmine's version gates follow the versions
84
+ WebdriverIO depends on. With Mocha, a call the runner refuses stops the file loading there, and the tests declared
85
+ above it still run. The config's `rootDir` and each capability's own `specs` and `exclude` are read too.
86
+
87
+ A project whose package.json lists `@wdio/cli` next to another runner now needs `--framework`, as the scan asks for
88
+ one whenever it finds several runners.
89
+
90
+ ### Patch Changes
91
+
92
+ - [#85](https://github.com/Huldoser/test-inventory/pull/85) [`90df9aa`](https://github.com/Huldoser/test-inventory/commit/90df9aa07741fab403470e653595b1b644b35dc3) - `expectCount` counts Playwright's `expect.configure(...)`, called in place as `expect.configure({ timeout: 5_000 })(locator)`
93
+ or kept in a variable as `const slowExpect = expect.configure(...)`; it missed both.
94
+
95
+ - [#83](https://github.com/Huldoser/test-inventory/pull/83) [`9ef9c59`](https://github.com/Huldoser/test-inventory/commit/9ef9c5959b908191e8015c8c880a3c476f43fccd) - The error for an unknown `framework` option or `--framework` value names every runner the scan reads; it still named
96
+ only Playwright, Vitest and Jest.
97
+
98
+ - [#89](https://github.com/Huldoser/test-inventory/pull/89) [`fd4b13a`](https://github.com/Huldoser/test-inventory/commit/fd4b13a45ec2349319b5edaec4dd0cbe1170d663) - `--help` names all nine runners `--framework` takes, and says which files node:test and Bun run without globs.
99
+
100
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - A helper that takes a test's function as a parameter, as `function settlementTest(desk, body) { describe(desk, () =>
101
+ test('settles', body)); }`, no longer gets a false `nested-test` warning. The scan read the parameter as the helper
102
+ itself, and so read the helper's own describe as declared inside the test.
103
+
104
+ - [#78](https://github.com/Huldoser/test-inventory/pull/78) [`1aea8a7`](https://github.com/Huldoser/test-inventory/commit/1aea8a744c955a01f1918447e048046a4eb022b4) - oxc-parser is updated from 0.156.0 to 0.157.0. The scan output doesn't change, except that a `.js` file with
105
+ TypeScript-only syntax, such as `export default abstract class` or an optional class member, now gets a `parse-error`, as
106
+ Node.js would refuse to load it.
107
+
108
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - A test or describe call with a title the runner refuses, written inside a test or hook body, is now a `nested-test`
109
+ warning, not an error that says the runner fails the file. Jest's own suite calls `it('test1')` inside
110
+ `expect(() => ...)` to test that error; the call runs in the test, so the file loads and its tests run.
111
+
112
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - Before Vitest 4.1, a `retry` object in the config, as `test: { retry: { count: 2 } }`, now gets a
113
+ `config-value-unknown` warning that Vitest runs no test with it. The tests already had `retries: null`, but nothing
114
+ said why.
115
+
116
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - A call through a Vitest member that reads the function it is called on, destructured as in
117
+ `const { each } = test; each([1, 2])('sizes lot %i', fn)`, is now an `invalid-chain` error, and the file's tests are
118
+ `notLoaded`: Vitest fails the file. That holds for `each`, `for`, `skipIf` and `runIf` of `test` and `each` of
119
+ `describe` in every version, `extend`, `scoped` and `override` from Vitest 4.1, and `for` of `describe` from Vitest 5.
120
+ Before, the scan said nothing and listed the file's other tests as active.
121
+
122
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - An inline Vitest project in `test.projects` now runs from the config's `test.root` or `root` when it sets none of its
123
+ own, as every Vitest version does; the scan used the config's folder before Vitest 5. A root the project sets is
124
+ resolved from the config's root in Vitest 5, as Vitest 5 does, and from the config's folder before.
125
+
126
+ - [#85](https://github.com/Huldoser/test-inventory/pull/85) [`90df9aa`](https://github.com/Huldoser/test-inventory/commit/90df9aa07741fab403470e653595b1b644b35dc3) - With `expandCases`, a Vitest case title cuts long values at the length the config sets, `test.taskTitleValueFormatTruncate`
127
+ from Vitest 5 and `test.chaiConfig.truncateThreshold` before, as Vitest does; it cut them at Vitest's default, 40, always.
128
+
129
+ - [#80](https://github.com/Huldoser/test-inventory/pull/80) [`cae53c1`](https://github.com/Huldoser/test-inventory/commit/cae53c107fce5a3024f7666c83d9b2582bb9017b) - From Vitest 5, `timeout: undefined`, `retry: undefined` and `repeats: undefined` in a test's options are left out, as
130
+ Vitest leaves them out, instead of giving an `unresolved-option` warning that `undefined` is not a whole number.
131
+
3
132
  ## 0.8.0
4
133
 
5
134
  ### Minor Changes
package/README.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  Test metadata from source code, not from test results.
4
4
 
5
- test-inventory reads Playwright, Vitest and Jest test files and lists every test declaration as JSON: its title, file and
6
- lines, the describes around it, its tags and annotations, and whether it is active, skipped, fixme, todo, expected
7
- to fail or in a file the runner refuses to load, with the condition when the state only applies sometimes. It parses
8
- the code and runs nothing, so it needs no browsers, no build and no runner configuration.
5
+ test-inventory reads Playwright, Vitest, Jest, Mocha, Jasmine, Cypress, WebdriverIO, node:test and Bun test files and
6
+ lists every test declaration as JSON: its title, file and lines, the describes around it, its tags and annotations, and
7
+ whether it is active, skipped, fixme, todo, expected to fail or in a file the runner refuses to load, with the condition
8
+ when the state only applies sometimes. Each test has an id that survives a move to another line and a hash of everything
9
+ it runs, from its hooks and fixtures to its page objects, test data, snapshots and config, so it can also list the tests
10
+ a change can affect. It parses the code and runs nothing, so it needs no browsers, no build and no runner configuration.
9
11
 
10
12
  Documentation: [huldoser.github.io/test-inventory](https://huldoser.github.io/test-inventory/)
11
13
 
@@ -15,7 +17,9 @@ Documentation: [huldoser.github.io/test-inventory](https://huldoser.github.io/te
15
17
  npm install --save-dev test-inventory
16
18
  ```
17
19
 
18
- Node.js 22.12 or newer, on Linux, macOS or Windows. Playwright 1.42 or newer, Vitest 3.0 or newer, Jest 29.0 or newer.
20
+ Node.js 22.12 or newer, on Linux, macOS or Windows. Playwright 1.42 or newer, Vitest 3.0 or newer, Jest 29.0 or newer,
21
+ Mocha 10.0 or newer, Jasmine 4.0 or newer, Cypress 13.0 or newer, WebdriverIO 9.0 or newer, node:test 22.0 or newer,
22
+ Bun 1.2.23 or newer.
19
23
 
20
24
  ## Command line
21
25
 
@@ -25,8 +29,9 @@ Node.js 22.12 or newer, on Linux, macOS or Windows. Playwright 1.42 or newer, Vi
25
29
  npx test-inventory "tests/**/*.spec.ts" --framework playwright --output inventory.json
26
30
  ```
27
31
 
28
- Without the glob, the test files are those the project's Playwright, Vitest or Jest configs run. Without
29
- `--framework`, the scan takes the runner the project uses, and asks for `--framework` when it uses several.
32
+ Without the glob, the test files are those the project's Playwright, Vitest, Jest, Mocha, Jasmine, Cypress or
33
+ WebdriverIO configs run, or for node:test and Bun the files `node --test` and `bun test` run. Without `--framework`, the
34
+ scan takes the runner the project uses, and asks for `--framework` when it uses several.
30
35
 
31
36
  For this spec from a trading app's UI tests, `tests/alerts.spec.ts`:
32
37
 
@@ -99,12 +104,12 @@ in Claude Code, Cursor, VS Code and other MCP clients ask it which tests a proje
99
104
 
100
105
  - Config files are read as code, never run, and only in part: settings computed at run time, as from `process.env`,
101
106
  can't be worked out, and settings such as `workers` or `use` are not read.
102
- - Values imported from other files are not worked out. A tag or title that comes from an import gets a diagnostic
103
- that names the module. A custom `test` object is followed through as many files as it takes, through relative
104
- paths, tsconfig path aliases, `#` imports in package.json and workspace packages; one that can't be followed gets
107
+ - Values from installed packages are not worked out; values from the project's own files are. A tag or title that
108
+ comes from a package gets a diagnostic that names the module. A custom `test` object is followed through as many files as it takes, through relative
109
+ paths, config and tsconfig path aliases, `#` imports in package.json and workspace packages; one that can't be followed gets
105
110
  an error. Tests declared in helper functions are listed at each call, also for helpers imported from other files.
106
111
  - A test in a loop or a `.each` table is one record, with `isInLoop` or `caseCount`; the runner creates one test per
107
- iteration or case.
112
+ iteration or case. `--expand-cases` lists each case whose values the scan can work out as its own record.
108
113
  - Conditions such as `browserName === 'webkit'` are kept as source text, not evaluated.
109
114
 
110
115
  ## Support
@@ -25,3 +25,12 @@ export declare function jestRowTitle(template: string, row: readonly Value[], in
25
25
  * stays as written. Null when Jest fails the table, as when a path goes through `null` before 30.0.
26
26
  */
27
27
  export declare function jestKeysTitle(template: string, item: Record<string, Value>, index: number, version: readonly [number, number] | null, keepsEscapes: boolean): string | null;
28
+ /**
29
+ * The title Bun gives one case of `.each`, read from Bun 1.2.23, 1.3.14 and 1.4.4: for a case whose first value is an
30
+ * object, first `$key` and `$key.path` for its values, then for every case the placeholders, left to right. `%s` takes
31
+ * a string, `%d` and `%f` a number, `%i` an integer, `%j` and `%o` any value as JSON; a value of another type leaves
32
+ * the placeholder as written, but is used up. `%#` is the index, `%%` a `%`, and `%` before any other character is
33
+ * dropped. Once the values are used up, the rest stays as written. Null where Bun pretty-prints: `%p`, and `$key`
34
+ * of an object, `null` or `undefined`.
35
+ */
36
+ export declare function bunCaseTitle(template: string, args: readonly Value[], index: number, version: readonly [number, number] | null): string | null;
@@ -327,3 +327,85 @@ function prettyFormatMin(value, depth = 0) {
327
327
  const keys = Object.keys(value).sort();
328
328
  return `{${keys.map((key) => `${prettyFormatMin(key)}: ${prettyFormatMin(value[key], depth + 1)}`).join(', ')}}`;
329
329
  }
330
+ /**
331
+ * The title Bun gives one case of `.each`, read from Bun 1.2.23, 1.3.14 and 1.4.4: for a case whose first value is an
332
+ * object, first `$key` and `$key.path` for its values, then for every case the placeholders, left to right. `%s` takes
333
+ * a string, `%d` and `%f` a number, `%i` an integer, `%j` and `%o` any value as JSON; a value of another type leaves
334
+ * the placeholder as written, but is used up. `%#` is the index, `%%` a `%`, and `%` before any other character is
335
+ * dropped. Once the values are used up, the rest stays as written. Null where Bun pretty-prints: `%p`, and `$key`
336
+ * of an object, `null` or `undefined`.
337
+ */
338
+ export function bunCaseTitle(template, args, index, version) {
339
+ let text = template;
340
+ const [first] = args;
341
+ if (first !== null && typeof first === 'object' && !Array.isArray(first)) {
342
+ const quotes = version !== null && (version[0] < 1 || (version[0] === 1 && version[1] < 3));
343
+ const shown = { all: true };
344
+ text = text.replace(/\$([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/g, (written, path) => {
345
+ let value = first;
346
+ for (const key of path.split('.')) {
347
+ value = value !== null && typeof value === 'object' && !Array.isArray(value) ? value[key] : undefined;
348
+ }
349
+ if (value === undefined && !hasPath(first, path))
350
+ return written;
351
+ if (typeof value === 'string')
352
+ return quotes ? JSON.stringify(value) : value;
353
+ if (typeof value === 'number')
354
+ return Object.is(value, -0) ? '-0' : String(value);
355
+ if (typeof value === 'boolean')
356
+ return String(value);
357
+ shown.all = false;
358
+ return written;
359
+ });
360
+ if (!shown.all)
361
+ return null;
362
+ }
363
+ let out = '';
364
+ let used = 0;
365
+ for (let at = 0; at < text.length; at++) {
366
+ const char = text[at];
367
+ if (char !== '%' || at === text.length - 1) {
368
+ out += char;
369
+ continue;
370
+ }
371
+ if (used >= args.length)
372
+ return out + text.slice(at);
373
+ const placeholder = text[++at];
374
+ if (placeholder === '#')
375
+ out += String(index);
376
+ else if (placeholder === '%')
377
+ out += '%';
378
+ else if (placeholder === 'p')
379
+ return null;
380
+ else if ('sdfijo'.includes(placeholder)) {
381
+ const formatted = bunValue(placeholder, args[used++]);
382
+ if (formatted === null)
383
+ return null;
384
+ out += formatted;
385
+ }
386
+ else
387
+ out += placeholder;
388
+ }
389
+ return out;
390
+ }
391
+ /** Whether an object has a value at a dotted path, `undefined` included. */
392
+ function hasPath(item, path) {
393
+ let value = item;
394
+ for (const key of path.split('.')) {
395
+ if (value === null || typeof value !== 'object' || Array.isArray(value) || !(key in value))
396
+ return false;
397
+ value = value[key];
398
+ }
399
+ return true;
400
+ }
401
+ /** One placeholder of a Bun title, or the placeholder as written for a value of another type; null for a JSON Bun shows otherwise. */
402
+ function bunValue(placeholder, value) {
403
+ const written = `%${placeholder}`;
404
+ if (placeholder === 's')
405
+ return typeof value === 'string' ? value : written;
406
+ if (placeholder === 'd' || placeholder === 'f')
407
+ return typeof value === 'number' ? String(value) : written;
408
+ if (placeholder === 'i')
409
+ return typeof value === 'number' && Number.isInteger(value) ? String(value) : written;
410
+ return value === undefined ? null : JSON.stringify(value);
411
+ }
package/dist/cli.d.ts CHANGED
@@ -6,6 +6,6 @@ export interface Io {
6
6
  /** Read for `--related -`. */
7
7
  stdin?: Readable;
8
8
  }
9
- export declare const HELP = "Usage: test-inventory [glob...] [options]\n\nLists the tests in the matching files as JSON, read from the source code without running them. Without globs, the\ntest files are those the Playwright, Vitest or Jest configs in the root and the folders below it run.\n\nOptions:\n --framework <name> playwright, vitest or jest (default: the one the project uses)\n --root <dir> project root; paths in the output are relative to it (default: current directory)\n --ignore <glob> leave out matching files; can be repeated (node_modules is always left out)\n --framework-version <x.y.z> apply the rules of this framework version instead of the one in the project\n --comments <mode> comments above tests: all, non-active or none (default: non-active)\n --expand-cases one record per case of a .each or .for table and per pass of a loop, with its title\n --state <state> keep tests in this state, such as skip or fixme; can be repeated\n --tag <tag> keep tests with this tag, such as @smoke; can be repeated\n --grep <regex> keep tests whose full name matches, ignoring case\n --only keep tests that use .only, or are inside a describe that does\n --without-reason keep tests that don't run and give no reason\n --related <file> keep tests a change to this file can affect; can be repeated; - reads files, one per\n line, from standard input, as git diff --name-only writes them\n --fields <keys> write only these keys of each test, separated by commas; id is always kept\n --format <format> json or jsonl, one record per line (default: json)\n -o, --output <file> write to a file instead of standard output\n --pretty indent the JSON\n -h, --help show this help\n -v, --version show the version\n\nExit codes: 0 when the scan finishes, even with diagnostics; 2 for invalid arguments, an output that cannot be\nwritten, or a crash. 1 is reserved for a future release.\n\nExamples:\n test-inventory \"tests/**/*.spec.ts\" --framework playwright --output inventory.json\n test-inventory \"tests/**/*.spec.ts\" --framework playwright --state skip --fields fullName,reason\n";
9
+ export declare const HELP = "Usage: test-inventory [glob...] [options]\n\nLists the tests in the matching files as JSON, read from the source code without running them. Without globs, the\ntest files are those the runner's configs in the root and the folders below it run, or for node:test and Bun those\nnode --test and bun test run.\n\nOptions:\n --framework <name> playwright, vitest, jest, mocha, jasmine, cypress, webdriverio, node or bun\n (default: the one the project uses)\n --root <dir> project root; paths in the output are relative to it (default: current directory)\n --ignore <glob> leave out matching files; can be repeated (node_modules is always left out)\n --framework-version <x.y.z> apply the rules of this framework version instead of the one in the project\n --comments <mode> comments above tests: all, non-active or none (default: non-active)\n --expand-cases one record per case of a .each or .for table and per pass of a loop, with its title\n --state <state> keep tests in this state, such as skip or fixme; can be repeated\n --tag <tag> keep tests with this tag, such as @smoke; can be repeated\n --grep <regex> keep tests whose full name matches, ignoring case\n --only keep tests that use .only, or are inside a describe that does\n --without-reason keep tests that don't run and give no reason\n --related <file> keep tests a change to this file can affect; can be repeated; - reads files, one per\n line, from standard input, as git diff --name-only writes them\n --fields <keys> write only these keys of each test, separated by commas; id is always kept\n --format <format> json or jsonl, one record per line (default: json)\n -o, --output <file> write to a file instead of standard output\n --pretty indent the JSON\n -h, --help show this help\n -v, --version show the version\n\nExit codes: 0 when the scan finishes, even with diagnostics; 2 for invalid arguments, an output that cannot be\nwritten, or a crash. 1 is reserved for a future release.\n\nExamples:\n test-inventory \"tests/**/*.spec.ts\" --framework playwright --output inventory.json\n test-inventory \"tests/**/*.spec.ts\" --framework playwright --state skip --fields fullName,reason\n";
10
10
  /** Runs the command line with the given arguments and streams. Resolves to the exit code. */
11
11
  export declare function run(argv: string[], io: Io): Promise<number>;
package/dist/cli.js CHANGED
@@ -11,10 +11,12 @@ import { toolVersion } from "./version.js";
11
11
  export const HELP = `Usage: test-inventory [glob...] [options]
12
12
 
13
13
  Lists the tests in the matching files as JSON, read from the source code without running them. Without globs, the
14
- test files are those the Playwright, Vitest or Jest configs in the root and the folders below it run.
14
+ test files are those the runner's configs in the root and the folders below it run, or for node:test and Bun those
15
+ node --test and bun test run.
15
16
 
16
17
  Options:
17
- --framework <name> playwright, vitest or jest (default: the one the project uses)
18
+ --framework <name> playwright, vitest, jest, mocha, jasmine, cypress, webdriverio, node or bun
19
+ (default: the one the project uses)
18
20
  --root <dir> project root; paths in the output are relative to it (default: current directory)
19
21
  --ignore <glob> leave out matching files; can be repeated (node_modules is always left out)
20
22
  --framework-version <x.y.z> apply the rules of this framework version instead of the one in the project
@@ -17,6 +17,12 @@ const SNAPSHOT_KEYS = {
17
17
  ['test', 'browser', 'expect', 'toMatchScreenshot', 'screenshotDirectory'],
18
18
  ],
19
19
  jest: [['snapshotResolver']],
20
+ mocha: [],
21
+ jasmine: [],
22
+ cypress: [],
23
+ webdriverio: [],
24
+ node: [],
25
+ bun: [],
20
26
  };
21
27
  /** The keys of a config that name setup files, by framework; Vitest's are a project's own. */
22
28
  const SETUP_KEYS = {
@@ -26,6 +32,19 @@ const SETUP_KEYS = {
26
32
  ['test', 'globalSetup'],
27
33
  ],
28
34
  jest: [['setupFiles'], ['setupFilesAfterEnv'], ['globalSetup'], ['globalTeardown']],
35
+ // Mocha loads both before the tests: `require` for root hooks and global fixtures, `file` as test files.
36
+ mocha: [['require'], ['file']],
37
+ // Jasmine's `helpers` are globs from `spec_dir`, which these paths from the config's folder don't cover.
38
+ jasmine: [],
39
+ // The support file Cypress loads before every spec.
40
+ cypress: [
41
+ ['e2e', 'supportFile'],
42
+ ['component', 'supportFile'],
43
+ ],
44
+ // Mocha's `require` runs before the specs, as root hooks may.
45
+ webdriverio: [['mochaOpts', 'require']],
46
+ node: [],
47
+ bun: [],
29
48
  };
30
49
  const UNKNOWN_PROJECTS = "The projects that run the file can't be worked out, so dependencyHash doesn't cover their setup.";
31
50
  /**
@@ -1,4 +1,7 @@
1
1
  import { type ConfigFile, type Configs, type Setting } from './config.ts';
2
+ import { type MochaInterface } from './frameworks/mocha.ts';
3
+ import type { WebdriverIOFramework } from './frameworks/webdriverio.ts';
4
+ import type { ImportMap } from './resolve.ts';
2
5
  import type { FileConfig, VitestTags } from './records.ts';
3
6
  import type { Framework, ProjectRecord } from './types.ts';
4
7
  /** What makes a file run: as a test, or as a Vitest benchmark, which plain `vitest` doesn't run. */
@@ -60,6 +63,9 @@ export declare class ConfigRuns {
60
63
  private readonly folderStates;
61
64
  /** The project Jest runs with when no config is found up to the root. */
62
65
  private jestDefaults;
66
+ private mochaDefaults;
67
+ private nodeRuns;
68
+ private bunFiles;
63
69
  /** The Vitest configs whose projects are being found, with their names, to stop at one whose projects name itself. */
64
70
  private readonly expanding;
65
71
  constructor(configs: Configs, framework: Framework, version: [number, number] | null, findsFiles: boolean);
@@ -86,6 +92,66 @@ export declare class ConfigRuns {
86
92
  about: ProjectAbout;
87
93
  runs: (file: string) => unknown;
88
94
  } | null;
95
+ /** Mocha runs one set of files, from the config it starts with, or its defaults without one. */
96
+ private placeMocha;
97
+ /**
98
+ * The files Mocha runs, as it looks them up from the root, the folder it starts in, wherever its config is: each
99
+ * `spec` entry a glob, a file, a file without its extension, or a folder whose files with one of the `extension`s it
100
+ * takes, also in folders below with `recursive`, less those `ignore` matches; and the `file` entries, loaded first.
101
+ * With globs, every file they match, less `ignore`. A timeout may be written as `'2s'`.
102
+ */
103
+ private mochaProject;
104
+ /**
105
+ * Jasmine runs one set of files, from its config. Without one it runs none, unless it is given files: Karma and
106
+ * Angular projects give it theirs from their own configs, which the scan doesn't read, so their globs decide.
107
+ */
108
+ private placeJasmine;
109
+ /**
110
+ * The files Jasmine runs: those under `spec_dir`, from the root, that a `spec_files` glob matches, less those a glob
111
+ * starting with `!` matches. With globs, every file they match, as files given to Jasmine replace `spec_files`.
112
+ */
113
+ private jasmineProject;
114
+ /**
115
+ * Cypress runs a file in its e2e project, or in its component project when the config sets up component testing.
116
+ * Without a config it runs nothing, unless it is given files.
117
+ */
118
+ private placeCypress;
119
+ /**
120
+ * Cypress's testing types as projects: `e2e`, and `component` when the config has it. Each runs the files under the
121
+ * config's folder that its `specPattern` matches, dot files left out, without those its `excludeSpecPattern` matches,
122
+ * dot files included, and those in `node_modules` unless a `specPattern` names it; component testing also leaves out
123
+ * the files e2e's `specPattern` matches. `cypress run` matches each pattern against the whole path, as globby does.
124
+ */
125
+ private cypressProjects;
126
+ /** `bun test` runs the files of `bunFilesOf`, with the settings of the `bunfig.toml` in the root, when there is one. */
127
+ private placeBun;
128
+ /**
129
+ * The files `bun test` runs: those under `[test] root`, the root by default, whose name without its extension ends
130
+ * with `.test`, `_test`, `.spec` or `_spec`, with a JavaScript or TypeScript extension, outside dot folders and
131
+ * `node_modules`, and from Bun 1.3.11 outside the folders and files a `pathIgnorePatterns` glob matches. `[test] retry`
132
+ * applies to every test, and the tests of the files a `concurrentTestGlob` glob matches run at once.
133
+ */
134
+ private bunFilesOf;
135
+ /** `node --test` runs the files its arguments name, or without any, those its default patterns match; see `nodeFiles`. */
136
+ private placeNode;
137
+ /**
138
+ * The files `node --test` runs from the root, outside dot folders and `node_modules`: those the arguments of the
139
+ * package.json script that runs it give, the `test` script first, each a glob or a path, and from Node.js 26.8 a folder
140
+ * for the default patterns inside it; without arguments, the default patterns. They take TypeScript files too from
141
+ * Node.js 22.18, which strips types by default.
142
+ */
143
+ private nodeFiles;
144
+ /** WebdriverIO runs the files of the config in the root; without one, it runs nothing, unless it is given files. */
145
+ private placeWebdriverIO;
146
+ /**
147
+ * The files `wdio` runs: those under `rootDir`, the config's folder by default, that a `specs` glob matches, also in
148
+ * the nested lists that group files into one worker, less those `exclude` leaves out. A capability's own `specs` replace the config's for it, and its `exclude` adds to the config's. Globs leave out
149
+ * dot files and folders, and one without a `/` matches a file's name at any depth. When every `exclude` entry holds a
150
+ * `/`, `\` or `*`, they are globs too; otherwise each one leaves out every file whose URL contains it. Files given
151
+ * with `--spec` replace `specs`, so scanned with globs, every file they match counts as run, less the excludes. The
152
+ * config's `mochaOpts.timeout` and `retries`, or `jasmineOpts.defaultTimeoutInterval`, apply to every test.
153
+ */
154
+ private webdriverioProject;
89
155
  private projectsOf;
90
156
  /** Vitest's `retry`: before 4.1, which reads only a count, an object is one the scan can't work out. */
91
157
  private vitestRetry;
@@ -139,6 +205,11 @@ export declare class ConfigRuns {
139
205
  * after `index` when it has no name of its own.
140
206
  */
141
207
  private vitestEntry;
208
+ /**
209
+ * The folder an inline project runs from. Without a root of its own, every version runs it from the config's root;
210
+ * Vitest 5 also resolves its own root from there, where older versions resolve it from the config's folder.
211
+ */
212
+ private inlineRoot;
142
213
  /**
143
214
  * The projects of a config file or folder `test.projects` names: the config's own, also the projects it lists in
144
215
  * Vitest 5, or a project with Vitest's defaults for a folder without a config. Without a name, a project is named
@@ -147,15 +218,36 @@ export declare class ConfigRuns {
147
218
  private vitestPath;
148
219
  /**
149
220
  * A Vitest project, with the name Vitest gives it: its files are found from `test.dir`, else `test.root` or `root`,
150
- * else its folder. With browser mode on, each of `browser.instances` is a project of its own, named as Vitest names it.
221
+ * else its folder; with `isRoot`, `folder` is its root already. With browser mode on, each of `browser.instances` is
222
+ * a project of its own, named as Vitest names it.
151
223
  */
152
224
  private vitestProject;
153
225
  /** The files a Vitest project runs and its settings; `ownTags`, when set, in place of the tags in `settings`. */
154
226
  private vitestFiles;
155
227
  }
156
228
  /**
157
- * What the config a file uses gives it: Playwright's `forbidOnly`, read from the top level only, as Playwright reads
158
- * it, and for each test, the projects that run it and what they set; null without a config.
229
+ * What the config a file uses gives it: Playwright's `forbidOnly` or Vitest's `allowOnly`, read from the top level
230
+ * only, as the runners read them, and for each test, the projects that run it and what they set; null without a config.
159
231
  */
160
232
  export declare function fileConfig(placement: Placement, framework: Framework, version: [number, number] | null, file: string): FileConfig | null;
233
+ /**
234
+ * The interface Mocha's `ui` gives a file's tests: BDD by default, or TDD. Another one, QUnit, exports or a package,
235
+ * declares tests in a way the scan doesn't read, which is reported on the config, and the file is read as BDD.
236
+ */
237
+ export declare function mochaInterface(placement: Placement, configs: Configs): MochaInterface;
238
+ /** The interface WebdriverIO's `mochaOpts.ui` gives a file's tests, read as Mocha's `ui`. */
239
+ export declare function webdriverioInterface(placement: Placement, configs: Configs): MochaInterface | undefined;
240
+ /**
241
+ * The framework a WebdriverIO config runs the specs with: Mocha by default, or Jasmine. Cucumber runs feature files,
242
+ * which the scan doesn't read; that and a value the scan can't work out are reported on the config when `configs` is
243
+ * given, and the files are read as Mocha's.
244
+ */
245
+ export declare function webdriverioFramework(placement: Placement, configs: Configs | null): WebdriverIOFramework;
246
+ /**
247
+ * How the config a file runs with maps its imports, for its dependencies: Vite's `resolve.alias` and Vitest's
248
+ * `test.alias`, from the project and from the config file, and Jest's `moduleNameMapper`, `modulePaths` and
249
+ * `moduleDirectories` other than `node_modules`, with `<rootDir>` in them. Null for the other runners, without a
250
+ * config, or when it maps nothing.
251
+ */
252
+ export declare function importMap(placement: Placement, framework: Framework): ImportMap | null;
161
253
  export {};