test-inventory 0.4.0 → 0.5.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 (45) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +4 -1
  3. package/dist/ast.d.ts +3 -3
  4. package/dist/ast.js +4 -4
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.js +4 -5
  7. package/dist/config-runs.d.ts +97 -0
  8. package/dist/config-runs.js +784 -0
  9. package/dist/config.d.ts +90 -0
  10. package/dist/config.js +652 -0
  11. package/dist/diagnostics.js +5 -1
  12. package/dist/extract.d.ts +8 -0
  13. package/dist/extract.js +14 -1
  14. package/dist/filter.js +7 -0
  15. package/dist/frameworks/bindings.d.ts +22 -0
  16. package/dist/frameworks/bindings.js +19 -9
  17. package/dist/frameworks/common.d.ts +2 -0
  18. package/dist/frameworks/common.js +4 -0
  19. package/dist/frameworks/playwright.d.ts +2 -1
  20. package/dist/frameworks/playwright.js +3 -2
  21. package/dist/frameworks/vitest.d.ts +2 -1
  22. package/dist/frameworks/vitest.js +11 -7
  23. package/dist/index.d.ts +1 -1
  24. package/dist/inventory.d.ts +2 -2
  25. package/dist/inventory.js +2 -1
  26. package/dist/json.js +2 -0
  27. package/dist/model.d.ts +6 -1
  28. package/dist/options.d.ts +2 -2
  29. package/dist/records.d.ts +54 -1
  30. package/dist/records.js +192 -28
  31. package/dist/resolve.js +3 -3
  32. package/dist/scan-file.d.ts +5 -1
  33. package/dist/scan-file.js +5 -2
  34. package/dist/scan-source.js +1 -1
  35. package/dist/scan.d.ts +2 -1
  36. package/dist/scan.js +63 -17
  37. package/dist/types.d.ts +73 -7
  38. package/dist/values.d.ts +5 -0
  39. package/dist/values.js +5 -0
  40. package/dist/vitest-config.d.ts +6 -4
  41. package/dist/vitest-config.js +32 -28
  42. package/dist/vitest-defaults.d.ts +6 -0
  43. package/dist/vitest-defaults.js +16 -0
  44. package/package.json +10 -8
  45. package/schema/v1.json +180 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,90 @@
1
1
  # test-inventory
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#51](https://github.com/Huldoser/test-inventory/pull/51) [`acec7e7`](https://github.com/Huldoser/test-inventory/commit/acec7e7fd9bae8301ba3559c3986264ea35c3056) - Each file record has a new `config` key: the config the file uses, as the runner started at the root would pick it.
8
+ That is the outermost config at or above the file that runs it, and for Vitest `projects` the project that does,
9
+ read from Playwright's `testDir`, `testMatch`, `testIgnore` and `.gitignore` files, and from Vitest's `include`,
10
+ `exclude`, `includeSource`, `typecheck.include` and `benchmark.include` with each version's defaults. Before, the
11
+ nearest config was taken, which is wrong for a root config whose `include` covers packages that have their own
12
+ `vite.config.ts`. A file with tests that no config runs gets the new `file-not-run` warning.
13
+
14
+ The globals check follows the same choice, so a test in a folder project without a config of its own gets Vitest's
15
+ defaults, and a test the root config runs takes the root's `test.globals`. Files named by `benchmark.include` are
16
+ marked `isBenchmark`, in place of the default `*.bench.*` and `*.benchmark.*` names. The config reader now reads
17
+ regular expressions and paths built from `__dirname`, `__filename` or `import.meta` with `path.join()`,
18
+ `path.resolve()`, `path.dirname()`, `fileURLToPath()` and `new URL()`.
19
+
20
+ - [#50](https://github.com/Huldoser/test-inventory/pull/50) [`3e55445`](https://github.com/Huldoser/test-inventory/commit/3e55445d0b195941813368b7beeea239ccf8792f) - The output lists the configs the scanned files use in a new top-level `configs` key, and as `config` lines in JSON
21
+ Lines. A file uses the nearest `playwright.config.*`, `vitest.config.*` or `vite.config.*` in its folder or a folder
22
+ above, up to the root, as the runner started in that folder would. A config is read as code, never run, and `extends`
23
+ lists the files it takes settings from through imports, such as a shared base config.
24
+
25
+ Vitest's globals now come from `test.globals` in the config a file uses, read through `defineConfig()`,
26
+ `mergeConfig()`, spreads, constants and imported base configs. Before, any `globals: true` in a Vitest or Vite config
27
+ in the file's folder or a folder above counted, also one Vitest doesn't read for the file, as in a `vite.config.ts`
28
+ next to a `vitest.config.ts`. So `globals-not-enabled` can now appear where it didn't, and go away where a base config
29
+ turns globals on. A `test.globals` the scan can't work out without running the config, and a config it can't read,
30
+ get the new `config-value-unknown` warning.
31
+
32
+ - [#55](https://github.com/Huldoser/test-inventory/pull/55) [`a714612`](https://github.com/Huldoser/test-inventory/commit/a714612dcaf01eaa40d218bae44e7d27e68dbce6) - Run settings now come also from the config a file uses: Playwright's `timeout`, `retries` and `repeatEach`, and
33
+ Vitest's `testTimeout`, `retry` and `repeats`, where the file sets none. New `timeoutSource`, `retriesSource` and `repeatsSource`
34
+ keys say where each value comes from: `test`, `suite`, `file` or `config`, or `null` when nothing sets it. A test
35
+ takes the settings of the projects that run it, after their `grep`. When those projects disagree, the config sets the
36
+ value under a condition, as in `process.env.CI ? 2 : 0`, or the scan can't work out the projects, the value is `null`
37
+ with its source `config`. For a value under a condition, `timeoutConfigText`, `retriesConfigText` and
38
+ `repeatsConfigText` hold the config's text for it, so `retries: process.env.CI ? 2 : 0` reads as written. Playwright's `fullyParallel` and Vitest's `sequence.concurrent` make tests
39
+ `isParallel` where the file sets no mode, when every project that runs the test sets it.
40
+
41
+ A `.only` in a Playwright file whose config sets `forbidOnly` gets the new `forbidden-only` error, since the run then
42
+ fails before any test runs. When `forbidOnly` depends on a condition, as in `!!process.env.CI`, it is a warning that
43
+ quotes the condition.
44
+
45
+ - [#59](https://github.com/Huldoser/test-inventory/pull/59) [`aeb3eb1`](https://github.com/Huldoser/test-inventory/commit/aeb3eb10896ba79b82fb34e5b3dd08cb601a251d) - Vitest tags the config defines in `test.tags` now apply to the tests that carry them, as Vitest applies them. Their
46
+ `skip`, `todo`, `fails` and `only` are merged with the test's own and those a describe passes down, and Vitest's order,
47
+ `only` over `skip` over `todo`, gives the state, with the new `stateSource` value `config` when a tag sets it. Their
48
+ `concurrent`, `timeout`, `retry`, `repeats` and `meta` set those keys where the test sets none: from Vitest 5 over a
49
+ describe's options, and in Vitest 4.1 under them. The lowest `priority` wins between tags, and a Vitest 5 project's
50
+ own `tags` replace those of the config it extends.
51
+
52
+ A tag the config doesn't define fails the file in Vitest unless `strictTags` is off, also when there is no config, so
53
+ it now gets the new `undefined-tag` error, and the file's tests are `notLoaded`; in a function or under a condition it
54
+ is a warning, as that code may not run. The `config-may-change-state` warning now shows only when
55
+ the scan can't work out the config's tags.
56
+
57
+ - [#54](https://github.com/Huldoser/test-inventory/pull/54) [`c610789`](https://github.com/Huldoser/test-inventory/commit/c61078957ce5150fde089a45a444cf5bef41cfe5) - Each test record has a new `projects` key: the names of the projects that run the test, of the config its file uses.
58
+ For Playwright, those whose `testDir`, `testMatch` and `testIgnore` include the file, less those whose `grep` or
59
+ `grepInvert` leaves the test's title and tags out; for Vitest, those whose files include it, with each browser
60
+ instance as a project, named as Vitest names it, such as `unit (chromium)`. It is `[]` when no project runs the test,
61
+ and `null` without a config or when the scan can't tell. Config records list their projects in `projects`, each with
62
+ its `name`, `testDir` and Playwright's `dependencies`.
63
+
64
+ - [#53](https://github.com/Huldoser/test-inventory/pull/53) [`b9471ec`](https://github.com/Huldoser/test-inventory/commit/b9471ecd36f9f6157c21b013c7fa6cc9584250cd) - Glob patterns are optional, in `scan()` and on the command line: without them, the test files are those the
65
+ Playwright or Vitest configs in the root and the folders below it run, each with the config it uses. A config whose
66
+ files the scan can't work out gets a `config-value-unknown` warning that says to give patterns, and a scan without
67
+ patterns and without a config says so in `no-files-matched`. An empty list of patterns is still an error.
68
+
69
+ ### Patch Changes
70
+
71
+ - [#58](https://github.com/Huldoser/test-inventory/pull/58) [`1a34d74`](https://github.com/Huldoser/test-inventory/commit/1a34d74e76faead734afe87c0babc2a23fdbe4b7) - oxc-parser is updated from 0.154.0 to 0.156.0. The scan output doesn't change.
72
+
73
+ ## 0.4.1
74
+
75
+ ### Patch Changes
76
+
77
+ - [#48](https://github.com/Huldoser/test-inventory/pull/48) [`6549682`](https://github.com/Huldoser/test-inventory/commit/65496820548c0980cef2f290f6d54bd89160236b) - A hook given a function the file declares, as in `test.beforeEach(skipOnCI)` or `beforeEach(requireBroker)`, is read
78
+ like one written in place: the skips, timeouts and other settings it sets apply to the tests it runs before, and its
79
+ fixtures are in their `fixtures`. Before, they were missed. A test or hook function passed by name no longer gets a
80
+ `state-in-helper` warning saying no test, hook or describe in the file calls it.
81
+
82
+ - [#47](https://github.com/Huldoser/test-inventory/pull/47) [`a603623`](https://github.com/Huldoser/test-inventory/commit/a603623b2431e51c3e48ae0413964d5a7125de59) - A `#` import or workspace package export whose target repeats the `*` of its pattern, as in
83
+ `"#desks/*": "./desks/*/*-fixtures.ts"`, is followed to the file Node.js loads, with every `*` filled in. Only the
84
+ first was filled in before, so the import wasn't followed and its tests were missing, with an `unresolved-test-import`
85
+ error. A folder given as the pattern with a long run of slashes, as in `tests////unit`, no longer slows the scan down
86
+ while the hint for it is written.
87
+
3
88
  ## 0.4.0
4
89
 
5
90
  ### Minor Changes
package/README.md CHANGED
@@ -25,6 +25,8 @@ Node.js 22.12 or newer, on Linux, macOS or Windows. Playwright 1.42 or newer, Vi
25
25
  npx test-inventory "tests/**/*.spec.ts" --framework playwright --output inventory.json
26
26
  ```
27
27
 
28
+ Without the glob, the test files are those the Playwright or Vitest configs in the project run.
29
+
28
30
  For this spec from a trading app's UI tests, `tests/alerts.spec.ts`:
29
31
 
30
32
  <!-- code: trading-web/tests/alerts.spec.ts -->
@@ -94,7 +96,8 @@ in Claude Code, Cursor, VS Code and other MCP clients ask it which tests a proje
94
96
 
95
97
  ## Limits
96
98
 
97
- - Config files are not read, so `grep`, projects and Vitest tag options in them do not change the output.
99
+ - Config files are read as code, never run, and only in part: settings computed at run time, as from `process.env`,
100
+ can't be worked out, and settings such as `workers` or `use` are not read.
98
101
  - Values imported from other files are not worked out. A tag or title that comes from an import gets a diagnostic
99
102
  that names the module. A custom `test` object is followed through as many files as it takes, through relative
100
103
  paths, tsconfig path aliases, `#` imports in package.json and workspace packages; one that can't be followed gets
package/dist/ast.d.ts CHANGED
@@ -11,10 +11,10 @@ export interface ReturnValue {
11
11
  }
12
12
  /**
13
13
  * The values a function returns; null when it may return no value, as with `return;` or no `return` at all, and for
14
- * an async function or a generator, which returns a promise or an iterator whatever its `return` says. A `return` in a
15
- * function inside it is that function's.
14
+ * an async function or a generator, which returns a promise or an iterator whatever its `return` says. With `awaited`,
15
+ * an async function's values are those its promise resolves to. A `return` in a function inside it is that function's.
16
16
  */
17
- export declare function returnValues(fn: FunctionNode): ReturnValue[] | null;
17
+ export declare function returnValues(fn: FunctionNode, awaited?: boolean): ReturnValue[] | null;
18
18
  export declare function isStringLiteral(node: Node): node is StringLiteral;
19
19
  /** `import.meta.vitest`, which in-source Vitest tests read their functions from. */
20
20
  export declare function isImportMetaVitest(expression: Expression): boolean;
package/dist/ast.js CHANGED
@@ -34,11 +34,11 @@ export function isFunctionNode(node) {
34
34
  }
35
35
  /**
36
36
  * The values a function returns; null when it may return no value, as with `return;` or no `return` at all, and for
37
- * an async function or a generator, which returns a promise or an iterator whatever its `return` says. A `return` in a
38
- * function inside it is that function's.
37
+ * an async function or a generator, which returns a promise or an iterator whatever its `return` says. With `awaited`,
38
+ * an async function's values are those its promise resolves to. A `return` in a function inside it is that function's.
39
39
  */
40
- export function returnValues(fn) {
41
- if (fn.async || fn.generator)
40
+ export function returnValues(fn, awaited = false) {
41
+ if ((fn.async && !awaited) || fn.generator)
42
42
  return null;
43
43
  // Only declarations of overloads have no body.
44
44
  const body = fn.body;
package/dist/cli.d.ts CHANGED
@@ -4,6 +4,6 @@ export interface Io {
4
4
  stderr: Writable;
5
5
  cwd: string;
6
6
  }
7
- export declare const HELP = "Usage: test-inventory <glob...> --framework <playwright|vitest> [options]\n\nLists the tests in the matching files as JSON, read from the source code without running them.\n\nOptions:\n --framework <name> playwright or vitest (required)\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 --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 --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";
7
+ export declare const HELP = "Usage: test-inventory [glob...] --framework <playwright|vitest> [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 or Vitest configs in the root and the folders below it run.\n\nOptions:\n --framework <name> playwright or vitest (required)\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 --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 --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";
8
8
  /** Runs the command line with the given arguments and streams. Resolves to the exit code. */
9
9
  export declare function run(argv: string[], io: Io): Promise<number>;
package/dist/cli.js CHANGED
@@ -8,9 +8,10 @@ import { writeInventory, writeInventoryLines } from "./json.js";
8
8
  import { OptionsError } from "./options.js";
9
9
  import { scan } from "./scan.js";
10
10
  import { toolVersion } from "./version.js";
11
- export const HELP = `Usage: test-inventory <glob...> --framework <playwright|vitest> [options]
11
+ export const HELP = `Usage: test-inventory [glob...] --framework <playwright|vitest> [options]
12
12
 
13
- Lists the tests in the matching files as JSON, read from the source code without running them.
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 or Vitest configs in the root and the folders below it run.
14
15
 
15
16
  Options:
16
17
  --framework <name> playwright or vitest (required)
@@ -129,8 +130,6 @@ export async function run(argv, io) {
129
130
  io.stdout.write(`${toolVersion}\n`);
130
131
  return FINISHED;
131
132
  }
132
- if (positionals.length === 0)
133
- return fail('give at least one glob pattern for the test files.');
134
133
  if (values.framework === undefined)
135
134
  return fail('--framework is required: playwright or vitest.');
136
135
  const format = values.format ?? 'json';
@@ -154,7 +153,7 @@ export async function run(argv, io) {
154
153
  if (fields !== undefined)
155
154
  checkFields(fields);
156
155
  inventory = await scan({
157
- patterns: positionals,
156
+ patterns: positionals.length > 0 ? positionals : undefined,
158
157
  framework: values.framework,
159
158
  root: path.resolve(io.cwd, values.root ?? '.'),
160
159
  ignore: values.ignore ?? [],
@@ -0,0 +1,97 @@
1
+ import { type ConfigFile, type Configs, type Setting } from './config.ts';
2
+ import type { FileConfig, VitestTags } from './records.ts';
3
+ import type { Framework, ProjectRecord } from './types.ts';
4
+ /** What makes a file run: as a test, or as a Vitest benchmark, which plain `vitest` doesn't run. */
5
+ type Run = 'test' | 'benchmark';
6
+ /**
7
+ * What the output says of a project: its name, the folder its test files are found from and Playwright's
8
+ * `dependencies`. `grep` is Playwright's filter of its test titles, which start from the path in `grepRoot`.
9
+ */
10
+ export interface ProjectAbout {
11
+ name: string;
12
+ testDir: string;
13
+ dependencies: string[];
14
+ grep: ((title: string) => boolean) | null;
15
+ grepRoot: string;
16
+ defaults: ProjectDefaults;
17
+ }
18
+ /**
19
+ * The settings a project gives its tests, as written; null when unset. Playwright's `timeout`, `retries`, `repeatEach`
20
+ * and `fullyParallel`; Vitest's `testTimeout`, `retry`, `repeats` and `sequence.concurrent`, and the tags it defines.
21
+ */
22
+ interface ProjectDefaults {
23
+ timeout: Setting | null;
24
+ retry: Setting | null;
25
+ repeatEach: Setting | null;
26
+ repeats: Setting | null;
27
+ parallel: Setting | null;
28
+ tags: VitestTags | null;
29
+ }
30
+ /** The config a file uses, the settings of its project, and how that config runs it. */
31
+ export interface Placement {
32
+ config: ConfigFile | null;
33
+ settings: Setting | null;
34
+ /** False when no config runs the file; null when there is no config, or one can't tell. */
35
+ runs: Run | false | null;
36
+ /** The projects of the config that run the file, which a Playwright `grep` may narrow per test; null when unknown. */
37
+ projects: ProjectAbout[] | null;
38
+ }
39
+ /** Finds the configs a file is under and decides which one runs it, as the runner started at the root would. */
40
+ export declare class ConfigRuns {
41
+ private readonly configs;
42
+ private readonly framework;
43
+ private readonly version;
44
+ /** Whether the configs find the test files, as in a scan without patterns, rather than the patterns. */
45
+ private readonly findsFiles;
46
+ private readonly projects;
47
+ private readonly ignoreRules;
48
+ private readonly folderStates;
49
+ /** The Vitest configs whose projects are being found, to stop at one whose projects name itself. */
50
+ private readonly expanding;
51
+ constructor(configs: Configs, framework: Framework, version: [number, number] | null, findsFiles: boolean);
52
+ /**
53
+ * The config a file uses, as the runner started at the root would: the outermost config at or above it that runs it,
54
+ * with the project that does; else the outermost one whose files the scan can't work out, reported then; else the
55
+ * nearest config.
56
+ */
57
+ placementOf(file: string): Placement;
58
+ /** The projects of a config the output lists; null when the scan can't work them out. */
59
+ projectsIn(config: ConfigFile): ProjectRecord[] | null;
60
+ private projectsOf;
61
+ /** Vitest's `retry`: before 4.1, which reads only a count, an object is one the scan can't work out. */
62
+ private vitestRetry;
63
+ /** A project whose files can't be worked out, because of `setting` under `key`: it may run any file. */
64
+ private unknownProject;
65
+ private playwrightProjects;
66
+ /** A Playwright project: its own settings, else the config's, else Playwright's defaults. */
67
+ private playwrightProject;
68
+ /**
69
+ * Whether `.gitignore` files in a project's test folder, and the folders below it on the way to the file, leave the
70
+ * file out, as Playwright reads them.
71
+ */
72
+ private isGitIgnored;
73
+ /** A folder's status and the rules inside it, worked out once per project folder however many files it holds. */
74
+ private folderState;
75
+ private rulesIn;
76
+ private vitestProjects;
77
+ /** A project written in `test.projects`, which inherits the root config as each version of Vitest decides. */
78
+ private vitestEntry;
79
+ /**
80
+ * The projects of a config file or folder `test.projects` names: the config's own, also the projects it lists in
81
+ * Vitest 5, or a project with Vitest's defaults for a folder without a config.
82
+ */
83
+ private vitestPath;
84
+ /**
85
+ * A Vitest project: its files are found from `test.dir`, else `test.root` or `root`, else its folder. With browser
86
+ * mode on, each of `browser.instances` is a project of its own, named as Vitest names it.
87
+ */
88
+ private vitestProject;
89
+ /** The files a Vitest project runs and its settings; `ownTags`, when set, in place of the tags in `settings`. */
90
+ private vitestFiles;
91
+ }
92
+ /**
93
+ * What the config a file uses gives it: Playwright's `forbidOnly`, read from the top level only, as Playwright reads
94
+ * it, and for each test, the projects that run it and what they set; null without a config.
95
+ */
96
+ export declare function fileConfig(placement: Placement, framework: Framework, version: [number, number] | null, file: string): FileConfig | null;
97
+ export {};