ScuttleBot

scuttlebot / tests / e2e / node_modules / playwright / lib / runner / loadUtils.js
Blame History Raw 335 lines
1
"use strict";
2
var __create = Object.create;
3
var __defProp = Object.defineProperty;
4
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
var __getOwnPropNames = Object.getOwnPropertyNames;
6
var __getProtoOf = Object.getPrototypeOf;
7
var __hasOwnProp = Object.prototype.hasOwnProperty;
8
var __export = (target, all) => {
9
for (var name in all)
10
__defProp(target, name, { get: all[name], enumerable: true });
11
};
12
var __copyProps = (to, from, except, desc) => {
13
if (from && typeof from === "object" || typeof from === "function") {
14
for (let key of __getOwnPropNames(from))
15
if (!__hasOwnProp.call(to, key) && key !== except)
16
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
}
18
return to;
19
};
20
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
// If the importer is in node compatibility mode or this is not an ESM
22
// file that has been converted to a CommonJS file using a Babel-
23
// compatible transform (i.e. "__esModule" has not been set), then set
24
// "default" to the CommonJS "module.exports" for node compatibility.
25
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
mod
27
));
28
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
var loadUtils_exports = {};
30
__export(loadUtils_exports, {
31
collectProjectsAndTestFiles: () => collectProjectsAndTestFiles,
32
createRootSuite: () => createRootSuite,
33
loadFileSuites: () => loadFileSuites,
34
loadGlobalHook: () => loadGlobalHook,
35
loadReporter: () => loadReporter,
36
loadTestList: () => loadTestList
37
});
38
module.exports = __toCommonJS(loadUtils_exports);
39
var import_path = __toESM(require("path"));
40
var import_fs = __toESM(require("fs"));
41
var import_utils = require("playwright-core/lib/utils");
42
var import_loaderHost = require("./loaderHost");
43
var import_util = require("../util");
44
var import_projectUtils = require("./projectUtils");
45
var import_testGroups = require("./testGroups");
46
var import_suiteUtils = require("../common/suiteUtils");
47
var import_test = require("../common/test");
48
var import_compilationCache = require("../transform/compilationCache");
49
var import_transform = require("../transform/transform");
50
var import_utilsBundle = require("../utilsBundle");
51
async function collectProjectsAndTestFiles(testRun, doNotRunTestsOutsideProjectFilter) {
52
const config = testRun.config;
53
const fsCache = /* @__PURE__ */ new Map();
54
const sourceMapCache = /* @__PURE__ */ new Map();
55
const cliFileMatcher = config.cliArgs.length ? (0, import_util.createFileMatcherFromArguments)(config.cliArgs) : null;
56
const allFilesForProject = /* @__PURE__ */ new Map();
57
const filteredProjects = (0, import_projectUtils.filterProjects)(config.projects, config.cliProjectFilter);
58
for (const project of filteredProjects) {
59
const files = await (0, import_projectUtils.collectFilesForProject)(project, fsCache);
60
allFilesForProject.set(project, files);
61
}
62
const filesToRunByProject = /* @__PURE__ */ new Map();
63
for (const [project, files] of allFilesForProject) {
64
const matchedFiles = files.filter((file) => {
65
const hasMatchingSources = sourceMapSources(file, sourceMapCache).some((source) => {
66
if (cliFileMatcher && !cliFileMatcher(source))
67
return false;
68
return true;
69
});
70
return hasMatchingSources;
71
});
72
const filteredFiles = matchedFiles.filter(Boolean);
73
filesToRunByProject.set(project, filteredFiles);
74
}
75
const projectClosure = (0, import_projectUtils.buildProjectsClosure)([...filesToRunByProject.keys()]);
76
for (const [project, type] of projectClosure) {
77
if (type === "dependency") {
78
const treatProjectAsEmpty = doNotRunTestsOutsideProjectFilter && !filteredProjects.includes(project);
79
const files = treatProjectAsEmpty ? [] : allFilesForProject.get(project) || await (0, import_projectUtils.collectFilesForProject)(project, fsCache);
80
filesToRunByProject.set(project, files);
81
}
82
}
83
testRun.projectFiles = filesToRunByProject;
84
testRun.projectSuites = /* @__PURE__ */ new Map();
85
}
86
async function loadFileSuites(testRun, mode, errors) {
87
const config = testRun.config;
88
const allTestFiles = /* @__PURE__ */ new Set();
89
for (const files of testRun.projectFiles.values())
90
files.forEach((file) => allTestFiles.add(file));
91
const fileSuiteByFile = /* @__PURE__ */ new Map();
92
const loaderHost = mode === "out-of-process" ? new import_loaderHost.OutOfProcessLoaderHost(config) : new import_loaderHost.InProcessLoaderHost(config);
93
if (await loaderHost.start(errors)) {
94
for (const file of allTestFiles) {
95
const fileSuite = await loaderHost.loadTestFile(file, errors);
96
fileSuiteByFile.set(file, fileSuite);
97
errors.push(...createDuplicateTitlesErrors(config, fileSuite));
98
}
99
await loaderHost.stop();
100
}
101
for (const file of allTestFiles) {
102
for (const dependency of (0, import_compilationCache.dependenciesForTestFile)(file)) {
103
if (allTestFiles.has(dependency)) {
104
const importer = import_path.default.relative(config.config.rootDir, file);
105
const importee = import_path.default.relative(config.config.rootDir, dependency);
106
errors.push({
107
message: `Error: test file "${importer}" should not import test file "${importee}"`,
108
location: { file, line: 1, column: 1 }
109
});
110
}
111
}
112
}
113
for (const [project, files] of testRun.projectFiles) {
114
const suites = files.map((file) => fileSuiteByFile.get(file)).filter(Boolean);
115
testRun.projectSuites.set(project, suites);
116
}
117
}
118
async function createRootSuite(testRun, errors, shouldFilterOnly) {
119
const config = testRun.config;
120
const rootSuite = new import_test.Suite("", "root");
121
const projectSuites = /* @__PURE__ */ new Map();
122
const filteredProjectSuites = /* @__PURE__ */ new Map();
123
{
124
const cliFileFilters = (0, import_util.createFileFiltersFromArguments)(config.cliArgs);
125
const grepMatcher = config.cliGrep ? (0, import_util.createTitleMatcher)((0, import_util.forceRegExp)(config.cliGrep)) : () => true;
126
const grepInvertMatcher = config.cliGrepInvert ? (0, import_util.createTitleMatcher)((0, import_util.forceRegExp)(config.cliGrepInvert)) : () => false;
127
const cliTitleMatcher = (title) => !grepInvertMatcher(title) && grepMatcher(title);
128
for (const [project, fileSuites] of testRun.projectSuites) {
129
const projectSuite = createProjectSuite(project, fileSuites);
130
projectSuites.set(project, projectSuite);
131
const filteredProjectSuite = filterProjectSuite(projectSuite, { cliFileFilters, cliTitleMatcher, testFilters: config.preOnlyTestFilters });
132
filteredProjectSuites.set(project, filteredProjectSuite);
133
}
134
}
135
if (shouldFilterOnly) {
136
const filteredRoot = new import_test.Suite("", "root");
137
for (const filteredProjectSuite of filteredProjectSuites.values())
138
filteredRoot._addSuite(filteredProjectSuite);
139
(0, import_suiteUtils.filterOnly)(filteredRoot);
140
for (const [project, filteredProjectSuite] of filteredProjectSuites) {
141
if (!filteredRoot.suites.includes(filteredProjectSuite))
142
filteredProjectSuites.delete(project);
143
}
144
}
145
const projectClosure = (0, import_projectUtils.buildProjectsClosure)([...filteredProjectSuites.keys()], (project) => filteredProjectSuites.get(project)._hasTests());
146
for (const [project, type] of projectClosure) {
147
if (type === "top-level") {
148
project.project.repeatEach = project.fullConfig.configCLIOverrides.repeatEach ?? project.project.repeatEach;
149
rootSuite._addSuite(buildProjectSuite(project, filteredProjectSuites.get(project)));
150
}
151
}
152
if (config.config.forbidOnly) {
153
const onlyTestsAndSuites = rootSuite._getOnlyItems();
154
if (onlyTestsAndSuites.length > 0) {
155
const configFilePath = config.config.configFile ? import_path.default.relative(config.config.rootDir, config.config.configFile) : void 0;
156
errors.push(...createForbidOnlyErrors(onlyTestsAndSuites, config.configCLIOverrides.forbidOnly, configFilePath));
157
}
158
}
159
if (config.config.shard) {
160
const testGroups = [];
161
for (const projectSuite of rootSuite.suites) {
162
for (const group of (0, import_testGroups.createTestGroups)(projectSuite, config.config.shard.total))
163
testGroups.push(group);
164
}
165
const testGroupsInThisShard = (0, import_testGroups.filterForShard)(config.config.shard, config.configCLIOverrides.shardWeights, testGroups);
166
const testsInThisShard = /* @__PURE__ */ new Set();
167
for (const group of testGroupsInThisShard) {
168
for (const test of group.tests)
169
testsInThisShard.add(test);
170
}
171
(0, import_suiteUtils.filterTestsRemoveEmptySuites)(rootSuite, (test) => testsInThisShard.has(test));
172
}
173
if (config.postShardTestFilters.length)
174
(0, import_suiteUtils.filterTestsRemoveEmptySuites)(rootSuite, (test) => config.postShardTestFilters.every((filter) => filter(test)));
175
const topLevelProjects = [];
176
{
177
const projectClosure2 = new Map((0, import_projectUtils.buildProjectsClosure)(rootSuite.suites.map((suite) => suite._fullProject)));
178
for (const [project, level] of projectClosure2.entries()) {
179
if (level === "dependency")
180
rootSuite._prependSuite(buildProjectSuite(project, projectSuites.get(project)));
181
else
182
topLevelProjects.push(project);
183
}
184
}
185
return { rootSuite, topLevelProjects };
186
}
187
function createProjectSuite(project, fileSuites) {
188
const projectSuite = new import_test.Suite(project.project.name, "project");
189
for (const fileSuite of fileSuites)
190
projectSuite._addSuite((0, import_suiteUtils.bindFileSuiteToProject)(project, fileSuite));
191
const grepMatcher = (0, import_util.createTitleMatcher)(project.project.grep);
192
const grepInvertMatcher = project.project.grepInvert ? (0, import_util.createTitleMatcher)(project.project.grepInvert) : null;
193
(0, import_suiteUtils.filterTestsRemoveEmptySuites)(projectSuite, (test) => {
194
const grepTitle = test._grepTitleWithTags();
195
if (grepInvertMatcher?.(grepTitle))
196
return false;
197
return grepMatcher(grepTitle);
198
});
199
return projectSuite;
200
}
201
function filterProjectSuite(projectSuite, options) {
202
if (!options.cliFileFilters.length && !options.cliTitleMatcher && !options.testFilters.length)
203
return projectSuite;
204
const result = projectSuite._deepClone();
205
if (options.cliFileFilters.length)
206
(0, import_suiteUtils.filterByFocusedLine)(result, options.cliFileFilters);
207
(0, import_suiteUtils.filterTestsRemoveEmptySuites)(result, (test) => {
208
if (!options.testFilters.every((filter) => filter(test)))
209
return false;
210
if (options.cliTitleMatcher && !options.cliTitleMatcher(test._grepTitleWithTags()))
211
return false;
212
return true;
213
});
214
return result;
215
}
216
function buildProjectSuite(project, projectSuite) {
217
const result = new import_test.Suite(project.project.name, "project");
218
result._fullProject = project;
219
if (project.fullyParallel)
220
result._parallelMode = "parallel";
221
for (const fileSuite of projectSuite.suites) {
222
result._addSuite(fileSuite);
223
for (let repeatEachIndex = 1; repeatEachIndex < project.project.repeatEach; repeatEachIndex++) {
224
const clone = fileSuite._deepClone();
225
(0, import_suiteUtils.applyRepeatEachIndex)(project, clone, repeatEachIndex);
226
result._addSuite(clone);
227
}
228
}
229
return result;
230
}
231
function createForbidOnlyErrors(onlyTestsAndSuites, forbidOnlyCLIFlag, configFilePath) {
232
const errors = [];
233
for (const testOrSuite of onlyTestsAndSuites) {
234
const title = testOrSuite.titlePath().slice(2).join(" ");
235
const configFilePathName = configFilePath ? `'${configFilePath}'` : "the Playwright configuration file";
236
const forbidOnlySource = forbidOnlyCLIFlag ? `'--forbid-only' CLI flag` : `'forbidOnly' option in ${configFilePathName}`;
237
const error = {
238
message: `Error: item focused with '.only' is not allowed due to the ${forbidOnlySource}: "${title}"`,
239
location: testOrSuite.location
240
};
241
errors.push(error);
242
}
243
return errors;
244
}
245
function createDuplicateTitlesErrors(config, fileSuite) {
246
const errors = [];
247
const testsByFullTitle = /* @__PURE__ */ new Map();
248
for (const test of fileSuite.allTests()) {
249
const fullTitle = test.titlePath().slice(1).join(" \u203A ");
250
const existingTest = testsByFullTitle.get(fullTitle);
251
if (existingTest) {
252
const error = {
253
message: `Error: duplicate test title "${fullTitle}", first declared in ${buildItemLocation(config.config.rootDir, existingTest)}`,
254
location: test.location
255
};
256
errors.push(error);
257
}
258
testsByFullTitle.set(fullTitle, test);
259
}
260
return errors;
261
}
262
function buildItemLocation(rootDir, testOrSuite) {
263
if (!testOrSuite.location)
264
return "";
265
return `${import_path.default.relative(rootDir, testOrSuite.location.file)}:${testOrSuite.location.line}`;
266
}
267
async function requireOrImportDefaultFunction(file, expectConstructor) {
268
let func = await (0, import_transform.requireOrImport)(file);
269
if (func && typeof func === "object" && "default" in func)
270
func = func["default"];
271
if (typeof func !== "function")
272
throw (0, import_util.errorWithFile)(file, `file must export a single ${expectConstructor ? "class" : "function"}.`);
273
return func;
274
}
275
function loadGlobalHook(config, file) {
276
return requireOrImportDefaultFunction(import_path.default.resolve(config.config.rootDir, file), false);
277
}
278
function loadReporter(config, file) {
279
return requireOrImportDefaultFunction(config ? import_path.default.resolve(config.config.rootDir, file) : file, true);
280
}
281
function sourceMapSources(file, cache) {
282
let sources = [file];
283
if (!file.endsWith(".js"))
284
return sources;
285
if (cache.has(file))
286
return cache.get(file);
287
try {
288
const sourceMap = import_utilsBundle.sourceMapSupport.retrieveSourceMap(file);
289
const sourceMapData = typeof sourceMap?.map === "string" ? JSON.parse(sourceMap.map) : sourceMap?.map;
290
if (sourceMapData?.sources)
291
sources = sourceMapData.sources.map((source) => import_path.default.resolve(import_path.default.dirname(file), source));
292
} finally {
293
cache.set(file, sources);
294
return sources;
295
}
296
}
297
async function loadTestList(config, filePath) {
298
try {
299
const content = await import_fs.default.promises.readFile(filePath, "utf-8");
300
const lines = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
301
const descriptions = lines.map((line) => {
302
const delimiter = line.includes("\u203A") ? "\u203A" : ">";
303
const tokens = line.split(delimiter).map((token) => token.trim());
304
let project;
305
if (tokens[0].startsWith("[")) {
306
if (!tokens[0].endsWith("]"))
307
throw new Error(`Malformed test description: ${line}`);
308
project = tokens[0].substring(1, tokens[0].length - 1);
309
tokens.shift();
310
}
311
return { project, file: (0, import_utils.toPosixPath)((0, import_util.parseLocationArg)(tokens[0]).file), titlePath: tokens.slice(1) };
312
});
313
return (test) => descriptions.some((d) => {
314
const [projectName, , ...titles] = test.titlePath();
315
if (d.project !== void 0 && d.project !== projectName)
316
return false;
317
const relativeFile = (0, import_utils.toPosixPath)(import_path.default.relative(config.config.rootDir, test.location.file));
318
if (relativeFile !== d.file)
319
return false;
320
return d.titlePath.length <= titles.length && d.titlePath.every((_, index) => titles[index] === d.titlePath[index]);
321
});
322
} catch (e) {
323
throw (0, import_util.errorWithFile)(filePath, "Cannot read test list file: " + e.message);
324
}
325
}
326
// Annotate the CommonJS export names for ESM import in node:
327
0 && (module.exports = {
328
collectProjectsAndTestFiles,
329
createRootSuite,
330
loadFileSuites,
331
loadGlobalHook,
332
loadReporter,
333
loadTestList
334
});
335

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button