test-inventory 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +3 -1
- package/dist/ast.d.ts +11 -0
- package/dist/ast.js +30 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +59 -12
- package/dist/extract.js +55 -10
- package/dist/files.d.ts +1 -1
- package/dist/files.js +5 -3
- package/dist/filter.d.ts +37 -0
- package/dist/filter.js +153 -0
- package/dist/frameworks/bindings.d.ts +37 -6
- package/dist/frameworks/bindings.js +213 -74
- package/dist/frameworks/common.d.ts +17 -2
- package/dist/frameworks/common.js +46 -2
- package/dist/frameworks/playwright.d.ts +24 -4
- package/dist/frameworks/playwright.js +132 -63
- package/dist/frameworks/vitest.d.ts +23 -2
- package/dist/frameworks/vitest.js +166 -84
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/json.d.ts +7 -1
- package/dist/json.js +30 -9
- package/dist/model.d.ts +41 -2
- package/dist/options.d.ts +5 -2
- package/dist/records.js +5 -3
- package/dist/resolve.js +6 -1
- package/dist/types.d.ts +17 -1
- package/package.json +2 -2
- package/schema/v1.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { childNodes, isFunctionNode, propertyKey, propertyName, unwrap } from "../ast.js";
|
|
2
2
|
import { snippet } from "../source.js";
|
|
3
|
-
import { allDeclarators, bindAll, exportName, followImport, followModule, moduleImports, requiredModule, } from "./bindings.js";
|
|
4
|
-
import { atLeast, findFunction, isTestName, isTitleLiteral, localTestObject, looksLikeDeclaration, mayDeclareTests, mayHoldTestObject, namedFunction, origin, problem, readChain, readCondition, readOptions, readReason, readStrings, readTitle, text, unresolvedOption, unresolvedOptions, } from "./common.js";
|
|
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, readCondition, 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([
|
|
@@ -24,8 +24,17 @@ const STATES = new Map([
|
|
|
24
24
|
]);
|
|
25
25
|
/** Members of `test` that are known but don't declare or modify tests. */
|
|
26
26
|
const OTHER_MEMBERS = new Set(['use', 'slow', 'setTimeout', 'extend']);
|
|
27
|
+
/** First members of chains that never declare a test, even when Playwright refuses the rest of the chain. */
|
|
28
|
+
const NOT_DECLARING = new Set(['step', 'abort', ...HOOKS, ...OTHER_MEMBERS]);
|
|
27
29
|
/** First members that show a name imported from a local file is used as Playwright's `test`. */
|
|
28
30
|
const TEST_MEMBERS = new Set(['describe', 'only', 'skip', 'fixme', 'fail', 'step', ...HOOKS]);
|
|
31
|
+
/** The members of `test` a value chosen at run time holds either way: those both have, if both are the same kind. */
|
|
32
|
+
function sharedMembers(a, b) {
|
|
33
|
+
if (!a || !b || (a[0] === 'describe') !== (b[0] === 'describe'))
|
|
34
|
+
return null;
|
|
35
|
+
const differs = a.findIndex((name, index) => b[index] !== name);
|
|
36
|
+
return differs === -1 ? a : a.slice(0, differs);
|
|
37
|
+
}
|
|
29
38
|
/** Calls that need a newer Playwright than the oldest supported one. */
|
|
30
39
|
const ADDED_IN = new Map([
|
|
31
40
|
['fail.only', [1, 49]],
|
|
@@ -50,9 +59,12 @@ class PlaywrightBindings {
|
|
|
50
59
|
/** Names imported from other modules, which may be test objects there. */
|
|
51
60
|
imported;
|
|
52
61
|
follow;
|
|
62
|
+
/** The module's functions and the variables they may build test objects in; see `ModuleFunctions`. */
|
|
63
|
+
functions;
|
|
53
64
|
constructor(program, follow) {
|
|
54
65
|
this.imported = moduleImports(program, (source) => isPlaywrightSource(source) || MISMATCHED_SOURCES.has(source));
|
|
55
66
|
this.follow = follow;
|
|
67
|
+
this.functions = new ModuleFunctions(program, this.imported, follow);
|
|
56
68
|
for (const statement of program.body) {
|
|
57
69
|
if (statement.type !== 'ImportDeclaration' || statement.importKind === 'type')
|
|
58
70
|
continue;
|
|
@@ -78,41 +90,59 @@ class PlaywrightBindings {
|
|
|
78
90
|
}
|
|
79
91
|
}
|
|
80
92
|
}
|
|
81
|
-
|
|
93
|
+
const declarators = allDeclarators(program);
|
|
94
|
+
bindAll(declarators, (declarator) => {
|
|
82
95
|
const bound = this.bind(declarator);
|
|
83
96
|
if (bound)
|
|
84
97
|
this.declarations.add(declarator);
|
|
85
98
|
return bound;
|
|
86
99
|
});
|
|
100
|
+
this.functions.keep(declarators.filter((declarator) => !this.declarations.has(declarator)));
|
|
87
101
|
}
|
|
88
102
|
isTestName(name) {
|
|
89
|
-
return this.testNames.has(name) || this.importsTestObject(name);
|
|
103
|
+
return this.testNames.has(name) || this.importsTestObject(name) || this.holdsTestObject(name);
|
|
90
104
|
}
|
|
91
|
-
|
|
105
|
+
/**
|
|
106
|
+
* `followsCalls` is cleared where a call is not followed to what it returns: while the names are bound, so that only
|
|
107
|
+
* a value asked about is followed, as following reads other modules, and in the scanned file, whose calls the walk
|
|
108
|
+
* follows in their scopes.
|
|
109
|
+
*/
|
|
110
|
+
isTestObject(expression, followsCalls = true) {
|
|
92
111
|
const node = unwrap(expression);
|
|
93
112
|
switch (node.type) {
|
|
94
113
|
case 'Identifier':
|
|
95
|
-
return this.testNames.has(node.name) ||
|
|
114
|
+
return (this.testNames.has(node.name) ||
|
|
115
|
+
this.importsTestObject(node.name) ||
|
|
116
|
+
(followsCalls && this.holdsTestObject(node.name)));
|
|
96
117
|
case 'MemberExpression':
|
|
97
118
|
return propertyName(node) === 'test' && this.isModule(node.object);
|
|
98
119
|
case 'ConditionalExpression':
|
|
99
120
|
// `process.env.CI ? ciTest : test` is a test object either way.
|
|
100
|
-
return this.isTestObject(node.consequent) || this.isTestObject(node.alternate);
|
|
121
|
+
return this.isTestObject(node.consequent, followsCalls) || this.isTestObject(node.alternate, followsCalls);
|
|
101
122
|
case 'CallExpression': {
|
|
102
123
|
const callee = unwrap(node.callee);
|
|
103
124
|
if (callee.type === 'MemberExpression' && propertyName(callee) === 'extend') {
|
|
104
|
-
return this.isTestObject(callee.object);
|
|
125
|
+
return this.isTestObject(callee.object, followsCalls);
|
|
105
126
|
}
|
|
127
|
+
// `mergeTests(a, b)`, or `defineBrokerTest()`, a function that returns a test object.
|
|
106
128
|
return callee.type === 'Identifier'
|
|
107
|
-
? this.mergeTestsNames.has(callee.name)
|
|
129
|
+
? this.mergeTestsNames.has(callee.name) || (followsCalls && this.returnsTestObject(callee.name))
|
|
108
130
|
: callee.type === 'MemberExpression' && propertyName(callee) === 'mergeTests' && this.isModule(callee.object);
|
|
109
131
|
}
|
|
110
132
|
default:
|
|
111
133
|
return false;
|
|
112
134
|
}
|
|
113
135
|
}
|
|
114
|
-
|
|
115
|
-
return
|
|
136
|
+
returnsTestObject(name) {
|
|
137
|
+
return this.functions.returnsTestObject(name, (expression) => this.isTestObject(expression));
|
|
138
|
+
}
|
|
139
|
+
holdsTestObject(name) {
|
|
140
|
+
return this.functions.holdsTestObject(name, (expression) => this.isTestObject(expression));
|
|
141
|
+
}
|
|
142
|
+
reexports(source, imported, exported) {
|
|
143
|
+
if (!isPlaywrightSource(source))
|
|
144
|
+
return this.follow(source, imported, exported);
|
|
145
|
+
return exported === 'test object' && (imported === 'test' || imported === 'default');
|
|
116
146
|
}
|
|
117
147
|
/** Whether a name is imported from another module that exports it as a test object, as `base` from './base'. */
|
|
118
148
|
importsTestObject(name) {
|
|
@@ -141,7 +171,7 @@ class PlaywrightBindings {
|
|
|
141
171
|
}
|
|
142
172
|
return true;
|
|
143
173
|
}
|
|
144
|
-
if (this.isTestObject(init)) {
|
|
174
|
+
if (this.isTestObject(init, false)) {
|
|
145
175
|
this.testNames.add(id.name);
|
|
146
176
|
return true;
|
|
147
177
|
}
|
|
@@ -176,6 +206,7 @@ export class PlaywrightAdapter {
|
|
|
176
206
|
imports;
|
|
177
207
|
followed = new Map();
|
|
178
208
|
reported = new Set();
|
|
209
|
+
/** Variables and names of functions read as test objects on trust, each reported once. */
|
|
179
210
|
assumed = new Set();
|
|
180
211
|
aliases = new Map();
|
|
181
212
|
constructor(program, file, readModule) {
|
|
@@ -184,10 +215,11 @@ export class PlaywrightAdapter {
|
|
|
184
215
|
// A test object the file builds from an imported one, as in `const test = base.extend({...})`, is followed
|
|
185
216
|
// quietly: a call through the import itself is what reports an import that can't be followed.
|
|
186
217
|
const followed = new Map();
|
|
187
|
-
this.bindings = new PlaywrightBindings(program, (source, imported) => {
|
|
188
|
-
const key = `${source}\0${imported}`;
|
|
189
|
-
if (!followed.has(key))
|
|
190
|
-
followed.set(key, followModule(source, imported, readModule, PLAYWRIGHT).exported);
|
|
218
|
+
this.bindings = new PlaywrightBindings(program, (source, imported, exported = 'test object') => {
|
|
219
|
+
const key = `${source}\0${exported}\0${imported}`;
|
|
220
|
+
if (!followed.has(key)) {
|
|
221
|
+
followed.set(key, followModule(source, imported, readModule, PLAYWRIGHT, exported).exported);
|
|
222
|
+
}
|
|
191
223
|
return followed.get(key) === true;
|
|
192
224
|
});
|
|
193
225
|
this.problems = [...this.bindings.mismatches];
|
|
@@ -195,21 +227,22 @@ export class PlaywrightAdapter {
|
|
|
195
227
|
}
|
|
196
228
|
classify(call, context) {
|
|
197
229
|
const chain = readChain(call.callee);
|
|
198
|
-
const links = chain ? this.testLinks(chain, call.arguments, context
|
|
230
|
+
const links = chain ? this.testLinks(chain, call.arguments, context) : null;
|
|
199
231
|
if (!links)
|
|
200
232
|
return null;
|
|
201
233
|
const names = links.map((link) => link.name);
|
|
202
234
|
const key = names.join('.');
|
|
203
235
|
const addedIn = ADDED_IN.get(key);
|
|
204
236
|
if (addedIn && !atLeast(this.file.version, ...addedIn)) {
|
|
205
|
-
return this.invalid(call, `needs Playwright ${addedIn.join('.')} or newer
|
|
237
|
+
return this.invalid(call, names, `needs Playwright ${addedIn.join('.')} or newer`, context);
|
|
206
238
|
}
|
|
207
239
|
if (names[0] === 'info')
|
|
208
240
|
return this.annotationsPush(call, links, context);
|
|
209
241
|
if (names[0] === 'expect' || names[0] === 'step' || key === 'abort')
|
|
210
242
|
return { kind: 'other' };
|
|
211
|
-
if (links.some((link) => link.args ?? link.template))
|
|
212
|
-
return this.invalid(call, "is not part of Playwright's API");
|
|
243
|
+
if (links.some((link) => link.args ?? link.template)) {
|
|
244
|
+
return this.invalid(call, names, "is not part of Playwright's API", context);
|
|
245
|
+
}
|
|
213
246
|
if (key === '' || key === 'only' || key === 'skip' || key === 'fixme' || key === 'fail' || key === 'fail.only') {
|
|
214
247
|
return this.testCall(call, key, context);
|
|
215
248
|
}
|
|
@@ -222,54 +255,74 @@ export class PlaywrightAdapter {
|
|
|
222
255
|
return { kind: 'hook', fn: findFunction(call.arguments), testContext: null };
|
|
223
256
|
if (OTHER_MEMBERS.has(key))
|
|
224
257
|
return { kind: 'other' };
|
|
225
|
-
return this.invalid(call, "is not part of Playwright's API");
|
|
258
|
+
return this.invalid(call, names, "is not part of Playwright's API", context);
|
|
226
259
|
}
|
|
227
260
|
/**
|
|
228
261
|
* The links after the `test` object, or null when the chain doesn't start from Playwright's `test`. An import that
|
|
229
262
|
* can't be followed is reported only outside test and hook bodies, where a call can declare tests.
|
|
230
263
|
*/
|
|
231
|
-
testLinks(chain, args,
|
|
264
|
+
testLinks(chain, args, lookup) {
|
|
265
|
+
const links = this.linksFromTest(chain, args, lookup);
|
|
266
|
+
if (!links)
|
|
267
|
+
return null;
|
|
268
|
+
// A test object derived inline, as in `base.extend({ ... }).describe(...)`.
|
|
269
|
+
return links.slice(links.findLastIndex((link) => link.name === 'extend' && link.args) + 1);
|
|
270
|
+
}
|
|
271
|
+
linksFromTest(chain, args, lookup) {
|
|
232
272
|
const { base, links } = chain;
|
|
233
|
-
|
|
273
|
+
const { scopes, inBody } = lookup;
|
|
274
|
+
const withMembers = (members) => [
|
|
275
|
+
...members.map((name) => ({ name, args: null, template: null })),
|
|
276
|
+
...links,
|
|
277
|
+
];
|
|
234
278
|
if (base.type === 'Identifier') {
|
|
235
279
|
// A local variable or parameter with the same name, such as `(test) => test.result()`, is not Playwright's.
|
|
236
280
|
const declaration = this.file.values.declarationOf(base.name, scopes);
|
|
237
281
|
const bound = declaration !== null && this.bindings.declarations.has(declaration);
|
|
238
282
|
const local = declaration !== null && this.imports.get(base.name)?.node === declaration;
|
|
239
|
-
const alias = declaration?.type === 'VariableDeclarator' && !bound && !local
|
|
240
|
-
? this.describeAlias(declaration, scopes, inBody)
|
|
241
|
-
: null;
|
|
283
|
+
const alias = declaration?.type === 'VariableDeclarator' && !bound && !local ? this.alias(declaration, lookup) : null;
|
|
242
284
|
// The module from `require()` is both the namespace and `test`, so `pw.test(...)` reads as the namespace.
|
|
243
285
|
if (bound && this.bindings.namespaces.has(base.name) && links[0]?.name === 'test' && !links[0].args) {
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
else if (bound && this.bindings.testNames.has(base.name)) {
|
|
247
|
-
start = 0;
|
|
286
|
+
return links.slice(1);
|
|
248
287
|
}
|
|
249
|
-
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
288
|
+
if (bound && this.bindings.testNames.has(base.name))
|
|
289
|
+
return links;
|
|
290
|
+
if (alias)
|
|
291
|
+
return withMembers(alias);
|
|
292
|
+
if (local &&
|
|
293
|
+
this.worthFollowing(base.name, links, args) &&
|
|
254
294
|
this.follow(base.name, !inBody && mayDeclareTests(base.name, links))) {
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
else if (declaration !== null && !bound && this.assumeLocal(declaration, base.name, links, args)) {
|
|
258
|
-
start = 0;
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
return null;
|
|
295
|
+
return links;
|
|
262
296
|
}
|
|
297
|
+
return declaration !== null && !bound && this.assumeLocal(declaration, base.name, links, args) ? links : null;
|
|
263
298
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
299
|
+
if (this.bindings.isModule(base) && links[0]?.name === 'test' && !links[0].args)
|
|
300
|
+
return links.slice(1);
|
|
301
|
+
const members = base.type === 'CallExpression' ? this.returned(base, links, args, lookup) : null;
|
|
302
|
+
return members && withMembers(members);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The members of `test` a call returns: none for a test object, as `conditionalTest({ min: 22 })` may return, or
|
|
306
|
+
* those of a describe function. As for a variable chosen by a condition, only the members all its values have apply.
|
|
307
|
+
* When the function can't be found, a call named like a test object is read as one on trust, with a warning.
|
|
308
|
+
*/
|
|
309
|
+
returned(call, links, args, lookup) {
|
|
310
|
+
const callee = unwrap(call.callee);
|
|
311
|
+
if (callee.type !== 'Identifier')
|
|
268
312
|
return null;
|
|
313
|
+
const returns = lookup.returnsOf(callee.name, lookup.scopes);
|
|
314
|
+
if (returns)
|
|
315
|
+
return returnedFunction(returns, sharedMembers);
|
|
316
|
+
if (!isTestName(callee.name) || !this.looksLikeTest(links, args))
|
|
317
|
+
return null;
|
|
318
|
+
if (!this.assumed.has(callee.name)) {
|
|
319
|
+
this.assumed.add(callee.name);
|
|
320
|
+
this.problems.push(returnedTestObject(this.file, call));
|
|
269
321
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
testFunction(expression, scopes, returnsOf) {
|
|
325
|
+
return this.members(expression, { scopes, inBody: false, returnsOf });
|
|
273
326
|
}
|
|
274
327
|
/** Whether a local variable is read as a test object it could be; see `localTestObject`. */
|
|
275
328
|
assumeLocal(declaration, name, links, args) {
|
|
@@ -286,33 +339,42 @@ export class PlaywrightAdapter {
|
|
|
286
339
|
looksLikeTest(links, args) {
|
|
287
340
|
return links.length === 0 ? looksLikeDeclaration(args) : TEST_MEMBERS.has(links[0].name);
|
|
288
341
|
}
|
|
289
|
-
/**
|
|
290
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Whether a call through an imported name is worth following to the module: shaped like a test, or any call with a
|
|
344
|
+
* title and a body through a name that reads like a test function, as `it.effect('title', fn)` on a test object that
|
|
345
|
+
* a package extends.
|
|
346
|
+
*/
|
|
347
|
+
worthFollowing(name, links, args) {
|
|
348
|
+
return this.looksLikeTest(links, args) || (isTestName(name) && looksLikeDeclaration(args));
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* The members of `test` a variable holds: those of a describe function, as in `const serialDescribe =
|
|
352
|
+
* test.describe.serial`, or none for a test object a function returns.
|
|
353
|
+
*/
|
|
354
|
+
alias(declarator, lookup) {
|
|
291
355
|
let members = this.aliases.get(declarator);
|
|
292
356
|
if (members === undefined) {
|
|
293
357
|
// Set first, so a variable defined through itself ends the lookup.
|
|
294
358
|
this.aliases.set(declarator, null);
|
|
295
|
-
members = declarator.init ? this.
|
|
359
|
+
members = declarator.init ? this.members(declarator.init, lookup) : null;
|
|
296
360
|
this.aliases.set(declarator, members);
|
|
297
361
|
}
|
|
298
362
|
return members;
|
|
299
363
|
}
|
|
300
|
-
|
|
364
|
+
/** The members of `test` a value holds: none for a test object, or those of a describe function. */
|
|
365
|
+
members(expression, lookup) {
|
|
301
366
|
const node = unwrap(expression);
|
|
302
367
|
if (node.type === 'ConditionalExpression') {
|
|
303
368
|
// `IS_PLATFORM ? test.describe.serial : test.describe`: only the members both branches have apply.
|
|
304
|
-
|
|
305
|
-
const alternate = this.describeMembers(node.alternate, scopes, inBody);
|
|
306
|
-
if (!consequent || !alternate)
|
|
307
|
-
return null;
|
|
308
|
-
const shared = consequent.findIndex((name, index) => alternate[index] !== name);
|
|
309
|
-
return shared === -1 ? consequent : consequent.slice(0, shared);
|
|
369
|
+
return sharedMembers(this.members(node.consequent, lookup), this.members(node.alternate, lookup));
|
|
310
370
|
}
|
|
371
|
+
if (this.bindings.isTestObject(node, false))
|
|
372
|
+
return [];
|
|
311
373
|
const chain = readChain(node);
|
|
312
374
|
if (!chain || chain.links.some((link) => link.args ?? link.template))
|
|
313
375
|
return null;
|
|
314
|
-
const members = this.testLinks(chain, [],
|
|
315
|
-
return members && DESCRIBES.has(members.join('.')) ? members : null;
|
|
376
|
+
const members = this.testLinks(chain, [], lookup)?.map((link) => link.name);
|
|
377
|
+
return members && (members.length === 0 || DESCRIBES.has(members.join('.'))) ? members : null;
|
|
316
378
|
}
|
|
317
379
|
/** Follows the import of `name` once, and reports it once when it can't be followed and `report` is set. */
|
|
318
380
|
follow(name, report) {
|
|
@@ -339,9 +401,15 @@ export class PlaywrightAdapter {
|
|
|
339
401
|
const resolved = this.file.values.resolve(node, context.scopes);
|
|
340
402
|
return resolved.ok && typeof resolved.value === 'string';
|
|
341
403
|
}
|
|
342
|
-
|
|
404
|
+
/** A chain Playwright refuses; one that starts like a declaration also declares a plain test or describe. */
|
|
405
|
+
invalid(call, names, reason, context) {
|
|
343
406
|
const callee = snippet(text(this.file, call.callee));
|
|
344
|
-
|
|
407
|
+
const refusal = problem('invalid-chain', call, `${callee} ${reason}.`, callee);
|
|
408
|
+
return refused(call, refusal, context, () => {
|
|
409
|
+
if (NOT_DECLARING.has(names[0]))
|
|
410
|
+
return { kind: 'other' };
|
|
411
|
+
return names.includes('describe') ? this.describeCall(call, {}, context) : this.testCall(call, '', context);
|
|
412
|
+
});
|
|
345
413
|
}
|
|
346
414
|
testCall(call, key, context) {
|
|
347
415
|
const args = call.arguments;
|
|
@@ -442,6 +510,7 @@ export class PlaywrightAdapter {
|
|
|
442
510
|
isOnly: kind.only === true,
|
|
443
511
|
mode: kind.mode ?? null,
|
|
444
512
|
isShuffled: false,
|
|
513
|
+
passesDown: null,
|
|
445
514
|
details: this.details(!anonymous && args.length === 3 ? args[1] : undefined, context, problems, title?.text ?? ''),
|
|
446
515
|
fn,
|
|
447
516
|
hashed: fn,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type { CallExpression } from 'oxc-parser';
|
|
1
|
+
import type { CallExpression, Expression, Node } from 'oxc-parser';
|
|
2
2
|
import type { Adapter, ApiCall, CallContext, ModuleReader, Problem } from '../model.ts';
|
|
3
3
|
import type { ParsedFile } from '../parse.ts';
|
|
4
|
-
import { type FileContext } from './common.ts';
|
|
4
|
+
import { type FileContext, type Link } from './common.ts';
|
|
5
|
+
type Kind = 'test' | 'describe';
|
|
6
|
+
/** A test or describe function and the links after it, as in `test.skipIf(isCI)`. */
|
|
7
|
+
interface TestFunction {
|
|
8
|
+
kind: Kind;
|
|
9
|
+
links: Link[];
|
|
10
|
+
}
|
|
5
11
|
export declare class VitestAdapter implements Adapter {
|
|
6
12
|
readonly problems: Problem[];
|
|
7
13
|
readonly fileTags: string[];
|
|
@@ -12,6 +18,7 @@ export declare class VitestAdapter implements Adapter {
|
|
|
12
18
|
private readonly imports;
|
|
13
19
|
private readonly followed;
|
|
14
20
|
private readonly reported;
|
|
21
|
+
/** Variables and names of functions read as test objects on trust, each reported once. */
|
|
15
22
|
private readonly assumed;
|
|
16
23
|
private readonly aliases;
|
|
17
24
|
/** Describe callbacks and the name of their first parameter, which Vitest calls with its `test`. */
|
|
@@ -19,6 +26,7 @@ export declare class VitestAdapter implements Adapter {
|
|
|
19
26
|
private warnedAboutTags;
|
|
20
27
|
constructor(parsed: ParsedFile, file: FileContext, readModule: ModuleReader | null);
|
|
21
28
|
classify(call: CallExpression, context: CallContext): ApiCall | null;
|
|
29
|
+
testFunction(expression: Expression, scopes: readonly Node[], returnsOf: CallContext['returnsOf']): TestFunction | null;
|
|
22
30
|
/** A call to `bench` imported from Vitest 5, which is undefined there, so the call throws. */
|
|
23
31
|
private benchCall;
|
|
24
32
|
private chainCall;
|
|
@@ -31,9 +39,21 @@ export declare class VitestAdapter implements Adapter {
|
|
|
31
39
|
/** The function and modifiers a variable holds, as in `const skipOnCi = test.skipIf(isCI)`. */
|
|
32
40
|
private alias;
|
|
33
41
|
private aliasOf;
|
|
42
|
+
/**
|
|
43
|
+
* The function a call returns, as `conditionalTest({ min: 22 })` returns `it` or `it.skip`: what all the values the
|
|
44
|
+
* function returns have in common, as for a variable chosen by a condition. When the function can't be found, a call
|
|
45
|
+
* named like a test object is read as one on trust, with a warning; see `returnedTestObject`.
|
|
46
|
+
*/
|
|
47
|
+
private returned;
|
|
34
48
|
/** Whether a local variable is read as a test object it could be; see `localTestObject`. */
|
|
35
49
|
private assumeLocal;
|
|
36
50
|
private looksLikeTest;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a call through an imported name is worth following to the module: shaped like a test, or any call with a
|
|
53
|
+
* title and a body through a name that reads like a test function, as `it.effect('title', fn)` on a test object that
|
|
54
|
+
* a package extends.
|
|
55
|
+
*/
|
|
56
|
+
private worthFollowing;
|
|
37
57
|
/** Vitest as followed modules are read, with the rules of the scanned version. */
|
|
38
58
|
private framework;
|
|
39
59
|
/** Follows the import of `name` once, and reports it once when it can't be followed and `report` is set. */
|
|
@@ -54,3 +74,4 @@ export declare class VitestAdapter implements Adapter {
|
|
|
54
74
|
private arrayLength;
|
|
55
75
|
private tagsUsed;
|
|
56
76
|
}
|
|
77
|
+
export {};
|