test-inventory 0.4.0 → 0.4.1
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/CHANGELOG.md +15 -0
- package/dist/extract.js +8 -1
- package/dist/frameworks/common.d.ts +2 -0
- package/dist/frameworks/common.js +4 -0
- package/dist/frameworks/playwright.js +2 -2
- package/dist/frameworks/vitest.js +5 -5
- package/dist/resolve.js +3 -3
- package/dist/scan.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# test-inventory
|
|
2
2
|
|
|
3
|
+
## 0.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#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
|
|
8
|
+
like one written in place: the skips, timeouts and other settings it sets apply to the tests it runs before, and its
|
|
9
|
+
fixtures are in their `fixtures`. Before, they were missed. A test or hook function passed by name no longer gets a
|
|
10
|
+
`state-in-helper` warning saying no test, hook or describe in the file calls it.
|
|
11
|
+
|
|
12
|
+
- [#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
|
|
13
|
+
`"#desks/*": "./desks/*/*-fixtures.ts"`, is followed to the file Node.js loads, with every `*` filled in. Only the
|
|
14
|
+
first was filled in before, so the import wasn't followed and its tests were missing, with an `unresolved-test-import`
|
|
15
|
+
error. A folder given as the pattern with a long run of slashes, as in `tests////unit`, no longer slows the scan down
|
|
16
|
+
while the hint for it is written.
|
|
17
|
+
|
|
3
18
|
## 0.4.0
|
|
4
19
|
|
|
5
20
|
### Minor Changes
|
package/dist/extract.js
CHANGED
|
@@ -110,7 +110,10 @@ class Walker {
|
|
|
110
110
|
declaredIn = new Map();
|
|
111
111
|
deferred = [];
|
|
112
112
|
isDeferred = new Set();
|
|
113
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Functions walked at one of their calls, or as a test or hook body passed by name, so that their tests are recorded
|
|
115
|
+
* where they are registered and what they set isn't reported as unapplied.
|
|
116
|
+
*/
|
|
114
117
|
walkedAtCall = new Set();
|
|
115
118
|
/** The functions being walked at their calls, innermost last, to stop recursion. */
|
|
116
119
|
callStack = [];
|
|
@@ -413,6 +416,8 @@ class Walker {
|
|
|
413
416
|
context.scope.hookFixtures.push(...api.fixtures);
|
|
414
417
|
}
|
|
415
418
|
if (api.fn) {
|
|
419
|
+
if (!call.arguments.includes(api.fn))
|
|
420
|
+
this.walkedAtCall.add(api.fn);
|
|
416
421
|
this.visitCallback(api.fn, {
|
|
417
422
|
...context,
|
|
418
423
|
hookScope: context.scope,
|
|
@@ -938,6 +943,8 @@ class Walker {
|
|
|
938
943
|
// A function declared in the file and passed by name runs as the test body too.
|
|
939
944
|
const body = declaration.fn ?? declaration.hashed;
|
|
940
945
|
if (body) {
|
|
946
|
+
if (!declaration.fn)
|
|
947
|
+
this.walkedAtCall.add(body);
|
|
941
948
|
this.visitCallback(body, {
|
|
942
949
|
...context,
|
|
943
950
|
test,
|
|
@@ -75,6 +75,8 @@ export declare function mayDeclareTests(name: string, links: Link[]): boolean;
|
|
|
75
75
|
/** A function declared in the file and passed by name, as in `test('fills an order', checkFill)`; null otherwise. */
|
|
76
76
|
export declare function namedFunction(file: FileContext, scopes: readonly Node[], node: Argument | undefined): FunctionNode | null;
|
|
77
77
|
export declare function findFunction(args: readonly Argument[], from?: number): FunctionNode | null;
|
|
78
|
+
/** A hook's function: written in the call, or declared in the file and passed by name, as in `beforeEach(signIn)`. */
|
|
79
|
+
export declare function hookFunction(file: FileContext, scopes: readonly Node[], args: readonly Argument[]): FunctionNode | null;
|
|
78
80
|
/** Reads a title. A title that cannot be worked out keeps its source text and gets a `dynamic-title` warning. */
|
|
79
81
|
export declare function readTitle(file: FileContext, scopes: readonly Node[], node: Argument, options: {
|
|
80
82
|
stripTags: boolean;
|
|
@@ -166,6 +166,10 @@ export function findFunction(args, from = 0) {
|
|
|
166
166
|
}
|
|
167
167
|
return null;
|
|
168
168
|
}
|
|
169
|
+
/** A hook's function: written in the call, or declared in the file and passed by name, as in `beforeEach(signIn)`. */
|
|
170
|
+
export function hookFunction(file, scopes, args) {
|
|
171
|
+
return findFunction(args) ?? args.map((arg) => namedFunction(file, scopes, arg)).find((fn) => fn !== null) ?? null;
|
|
172
|
+
}
|
|
169
173
|
/** Reads a title. A title that cannot be worked out keeps its source text and gets a `dynamic-title` warning. */
|
|
170
174
|
export function readTitle(file, scopes, node, options) {
|
|
171
175
|
const expression = node.type === 'SpreadElement' ? null : node;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { childNodes, isFunctionNode, propertyKey, propertyName, unwrap } from "../ast.js";
|
|
2
2
|
import { snippet } from "../source.js";
|
|
3
3
|
import { allDeclarators, bindAll, exportName, followImport, followModule, moduleImports, ModuleFunctions, requiredModule, } from "./bindings.js";
|
|
4
|
-
import { atLeast, findFunction, isTestName, isTitleLiteral, localTestObject, looksLikeDeclaration, mayDeclareTests, mayHoldTestObject, namedFunction, origin, problem, readChain, destructuredNames, readCondition, readCount, readOptions, readReason, readStrings, readTitle, refused, returnedFunction, returnedTestObject, text, unresolvedOption, unresolvedOptions, } from "./common.js";
|
|
4
|
+
import { atLeast, findFunction, hookFunction, isTestName, isTitleLiteral, localTestObject, looksLikeDeclaration, mayDeclareTests, mayHoldTestObject, namedFunction, origin, problem, readChain, destructuredNames, readCondition, readCount, readOptions, readReason, readStrings, readTitle, refused, returnedFunction, returnedTestObject, text, unresolvedOption, unresolvedOptions, } from "./common.js";
|
|
5
5
|
const MISMATCHED_SOURCES = new Set(['vitest', '@jest/globals']);
|
|
6
6
|
// Maps rather than object literals, so names such as `constructor` don't match inherited properties.
|
|
7
7
|
const DESCRIBES = new Map([
|
|
@@ -257,7 +257,7 @@ export class PlaywrightAdapter {
|
|
|
257
257
|
if (key === 'describe.configure')
|
|
258
258
|
return this.configure(call, context);
|
|
259
259
|
if (HOOKS.has(key)) {
|
|
260
|
-
const fn =
|
|
260
|
+
const fn = hookFunction(this.file, context.scopes, call.arguments);
|
|
261
261
|
return { kind: 'hook', name: key, fn, testContext: null, fixtures: destructuredNames(fn, 0) };
|
|
262
262
|
}
|
|
263
263
|
if (RUN_MEMBERS.has(key))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { childNodes, isFunctionNode, isImportMetaVitest, propertyKey, propertyName, unwrap, } from "../ast.js";
|
|
2
2
|
import { snippet } from "../source.js";
|
|
3
3
|
import { allDeclarators, bindAll, exportName, followImport, followModule, moduleImports, ModuleFunctions, requiredModule, } from "./bindings.js";
|
|
4
|
-
import { atLeast,
|
|
4
|
+
import { atLeast, hookFunction, isTestName, localTestObject, looksLikeDeclaration, mayDeclareTests, mayHoldTestObject, origin, problem, readChain, readCondition, returnedFunction, returnedTestObject, destructuredNames, readCount, readOptions, readStrings, isTitleLiteral, readReason, namedFunction, unresolvedOption, unresolvedOptions, readTitle, refused, text, } from "./common.js";
|
|
5
5
|
/** What a test function chosen at run time, as in `isCI ? it.skip : it`, is either way: only the links both have. */
|
|
6
6
|
function shared(a, b) {
|
|
7
7
|
if (!a || a.kind !== b?.kind)
|
|
@@ -328,7 +328,7 @@ export class VitestAdapter {
|
|
|
328
328
|
return null;
|
|
329
329
|
const { kind, links } = resolved;
|
|
330
330
|
if (kind === 'test' && links.length === 1 && isHook(links[0].name))
|
|
331
|
-
return this.hook(call, links[0].name);
|
|
331
|
+
return this.hook(call, links[0].name, context);
|
|
332
332
|
if (links.length === 1 && DERIVATIONS.has(links[0].name))
|
|
333
333
|
return { kind: 'other' };
|
|
334
334
|
const last = links.at(-1);
|
|
@@ -514,10 +514,10 @@ export class VitestAdapter {
|
|
|
514
514
|
const hook = declaration === null
|
|
515
515
|
? isHook(callee.name) && callee.name
|
|
516
516
|
: this.bindings.declarations.has(declaration) && this.bindings.hooks.get(callee.name);
|
|
517
|
-
return hook ? this.hook(call, hook) : null;
|
|
517
|
+
return hook ? this.hook(call, hook, context) : null;
|
|
518
518
|
}
|
|
519
|
-
hook(call, name) {
|
|
520
|
-
const fn =
|
|
519
|
+
hook(call, name, context) {
|
|
520
|
+
const fn = hookFunction(this.file, context.scopes, call.arguments);
|
|
521
521
|
const index = HOOKS.get(name);
|
|
522
522
|
return {
|
|
523
523
|
kind: 'hook',
|
package/dist/resolve.js
CHANGED
|
@@ -180,7 +180,7 @@ function aliasFiles(specifier, options) {
|
|
|
180
180
|
const match = options.paths ? matchPattern(Object.keys(options.paths), specifier) : null;
|
|
181
181
|
if (!match || !options.paths)
|
|
182
182
|
return null;
|
|
183
|
-
return targetStrings(options.paths[match.key]).map((target) => path.resolve(options.pathsBase, target.replaceAll('${configDir}', options.configDir).
|
|
183
|
+
return targetStrings(options.paths[match.key]).map((target) => path.resolve(options.pathsBase, target.replaceAll('${configDir}', options.configDir).replaceAll('*', match.star)));
|
|
184
184
|
}
|
|
185
185
|
/** Files a `#name` import may stand for, from `imports` in the nearest package.json. */
|
|
186
186
|
function subpathImportFiles(specifier, directory) {
|
|
@@ -191,7 +191,7 @@ function subpathImportFiles(specifier, directory) {
|
|
|
191
191
|
const match = imports ? matchPattern(Object.keys(imports), specifier) : null;
|
|
192
192
|
if (!match || !imports)
|
|
193
193
|
return [];
|
|
194
|
-
return targetStrings(imports[match.key]).map((target) => path.resolve(folder, target.
|
|
194
|
+
return targetStrings(imports[match.key]).map((target) => path.resolve(folder, target.replaceAll('*', match.star)));
|
|
195
195
|
}
|
|
196
196
|
if (path.dirname(folder) === folder)
|
|
197
197
|
return [];
|
|
@@ -223,7 +223,7 @@ function packageEntry(folder, subpath) {
|
|
|
223
223
|
let targets;
|
|
224
224
|
if (isSubpathMap) {
|
|
225
225
|
const match = matchPattern(Object.keys(map), subpath);
|
|
226
|
-
targets = match ? targetStrings(map[match.key]).map((target) => target.
|
|
226
|
+
targets = match ? targetStrings(map[match.key]).map((target) => target.replaceAll('*', match.star)) : [];
|
|
227
227
|
}
|
|
228
228
|
else {
|
|
229
229
|
targets = subpath === '.' ? targetStrings(exports) : [];
|
package/dist/scan.js
CHANGED
|
@@ -39,7 +39,11 @@ function folderHint(root, patterns, framework) {
|
|
|
39
39
|
});
|
|
40
40
|
if (folder === undefined)
|
|
41
41
|
return '';
|
|
42
|
-
|
|
42
|
+
// A loop, as a regular expression for trailing slashes takes quadratic time on a long run of slashes.
|
|
43
|
+
let end = folder.length;
|
|
44
|
+
while (end > 0 && '/\\'.includes(folder[end - 1]))
|
|
45
|
+
end -= 1;
|
|
46
|
+
const example = `${folder.slice(0, end)}/**/*.${framework === 'playwright' ? 'spec' : 'test'}.ts`;
|
|
43
47
|
return ` "${folder}" is a folder; to scan the test files in it, use a pattern such as "${example}".`;
|
|
44
48
|
}
|
|
45
49
|
/**
|