|
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 testRunner_exports = {}; |
|
30
|
__export(testRunner_exports, { |
|
31
|
TestRunner: () => TestRunner, |
|
32
|
TestRunnerEvent: () => TestRunnerEvent, |
|
33
|
runAllTestsWithConfig: () => runAllTestsWithConfig |
|
34
|
}); |
|
35
|
module.exports = __toCommonJS(testRunner_exports); |
|
36
|
var import_events = __toESM(require("events")); |
|
37
|
var import_fs = __toESM(require("fs")); |
|
38
|
var import_path = __toESM(require("path")); |
|
39
|
var import_server = require("playwright-core/lib/server"); |
|
40
|
var import_utils = require("playwright-core/lib/utils"); |
|
41
|
var import_configLoader = require("../common/configLoader"); |
|
42
|
var import_fsWatcher = require("../fsWatcher"); |
|
43
|
var import_teleReceiver = require("../isomorphic/teleReceiver"); |
|
44
|
var import_gitCommitInfoPlugin = require("../plugins/gitCommitInfoPlugin"); |
|
45
|
var import_webServerPlugin = require("../plugins/webServerPlugin"); |
|
46
|
var import_base = require("../reporters/base"); |
|
47
|
var import_internalReporter = require("../reporters/internalReporter"); |
|
48
|
var import_compilationCache = require("../transform/compilationCache"); |
|
49
|
var import_util = require("../util"); |
|
50
|
var import_reporters = require("./reporters"); |
|
51
|
var import_tasks = require("./tasks"); |
|
52
|
var import_lastRun = require("./lastRun"); |
|
53
|
const TestRunnerEvent = { |
|
54
|
TestFilesChanged: "testFilesChanged", |
|
55
|
TestPaused: "testPaused" |
|
56
|
}; |
|
57
|
class TestRunner extends import_events.default { |
|
58
|
constructor(configLocation, configCLIOverrides) { |
|
59
|
super(); |
|
60
|
this._watchedProjectDirs = /* @__PURE__ */ new Set(); |
|
61
|
this._ignoredProjectOutputs = /* @__PURE__ */ new Set(); |
|
62
|
this._watchedTestDependencies = /* @__PURE__ */ new Set(); |
|
63
|
this._queue = Promise.resolve(); |
|
64
|
this._watchTestDirs = false; |
|
65
|
this._populateDependenciesOnList = false; |
|
66
|
this._startingEnv = {}; |
|
67
|
this.configLocation = configLocation; |
|
68
|
this._configCLIOverrides = configCLIOverrides; |
|
69
|
this._watcher = new import_fsWatcher.Watcher((events) => { |
|
70
|
const collector = /* @__PURE__ */ new Set(); |
|
71
|
events.forEach((f) => (0, import_compilationCache.collectAffectedTestFiles)(f.file, collector)); |
|
72
|
this.emit(TestRunnerEvent.TestFilesChanged, [...collector]); |
|
73
|
}); |
|
74
|
} |
|
75
|
async initialize(params) { |
|
76
|
(0, import_utils.setPlaywrightTestProcessEnv)(); |
|
77
|
this._watchTestDirs = !!params.watchTestDirs; |
|
78
|
this._populateDependenciesOnList = !!params.populateDependenciesOnList; |
|
79
|
this._startingEnv = { ...process.env }; |
|
80
|
} |
|
81
|
resizeTerminal(params) { |
|
82
|
process.stdout.columns = params.cols; |
|
83
|
process.stdout.rows = params.rows; |
|
84
|
process.stderr.columns = params.cols; |
|
85
|
process.stderr.rows = params.rows; |
|
86
|
} |
|
87
|
hasSomeBrowsers() { |
|
88
|
for (const browserName of ["chromium", "webkit", "firefox"]) { |
|
89
|
try { |
|
90
|
import_server.registry.findExecutable(browserName).executablePathOrDie("javascript"); |
|
91
|
return true; |
|
92
|
} catch { |
|
93
|
} |
|
94
|
} |
|
95
|
return false; |
|
96
|
} |
|
97
|
async installBrowsers() { |
|
98
|
const executables = import_server.registry.defaultExecutables(); |
|
99
|
await import_server.registry.install(executables); |
|
100
|
} |
|
101
|
async loadConfig() { |
|
102
|
const { config, error } = await this._loadConfig(this._configCLIOverrides); |
|
103
|
if (config) |
|
104
|
return config; |
|
105
|
throw new Error("Failed to load config: " + (error ? error.message : "Unknown error")); |
|
106
|
} |
|
107
|
async runGlobalSetup(userReporters) { |
|
108
|
await this.runGlobalTeardown(); |
|
109
|
const reporter = new import_internalReporter.InternalReporter(userReporters); |
|
110
|
const config = await this._loadConfigOrReportError(reporter, this._configCLIOverrides); |
|
111
|
if (!config) |
|
112
|
return { status: "failed", env: [] }; |
|
113
|
const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [ |
|
114
|
...(0, import_tasks.createGlobalSetupTasks)(config) |
|
115
|
]); |
|
116
|
const env = []; |
|
117
|
for (const key of /* @__PURE__ */ new Set([...Object.keys(process.env), ...Object.keys(this._startingEnv)])) { |
|
118
|
if (this._startingEnv[key] !== process.env[key]) |
|
119
|
env.push([key, process.env[key] ?? null]); |
|
120
|
} |
|
121
|
if (status !== "passed") |
|
122
|
await cleanup(); |
|
123
|
else |
|
124
|
this._globalSetup = { cleanup }; |
|
125
|
return { status, env }; |
|
126
|
} |
|
127
|
async runGlobalTeardown() { |
|
128
|
const globalSetup = this._globalSetup; |
|
129
|
const status = await globalSetup?.cleanup(); |
|
130
|
this._globalSetup = void 0; |
|
131
|
return { status }; |
|
132
|
} |
|
133
|
async startDevServer(userReporter, mode) { |
|
134
|
await this.stopDevServer(); |
|
135
|
const reporter = new import_internalReporter.InternalReporter([userReporter]); |
|
136
|
const config = await this._loadConfigOrReportError(reporter); |
|
137
|
if (!config) |
|
138
|
return { status: "failed" }; |
|
139
|
const { status, cleanup } = await (0, import_tasks.runTasksDeferCleanup)(new import_tasks.TestRun(config, reporter), [ |
|
140
|
...(0, import_tasks.createPluginSetupTasks)(config), |
|
141
|
(0, import_tasks.createLoadTask)(mode, { failOnLoadErrors: true, filterOnly: false }), |
|
142
|
(0, import_tasks.createStartDevServerTask)() |
|
143
|
]); |
|
144
|
if (status !== "passed") |
|
145
|
await cleanup(); |
|
146
|
else |
|
147
|
this._devServer = { cleanup }; |
|
148
|
return { status }; |
|
149
|
} |
|
150
|
async stopDevServer() { |
|
151
|
const devServer = this._devServer; |
|
152
|
const status = await devServer?.cleanup(); |
|
153
|
this._devServer = void 0; |
|
154
|
return { status }; |
|
155
|
} |
|
156
|
async clearCache(userReporter) { |
|
157
|
const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter] : []); |
|
158
|
const config = await this._loadConfigOrReportError(reporter); |
|
159
|
if (!config) |
|
160
|
return { status: "failed" }; |
|
161
|
const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ |
|
162
|
...(0, import_tasks.createPluginSetupTasks)(config), |
|
163
|
(0, import_tasks.createClearCacheTask)(config) |
|
164
|
]); |
|
165
|
return { status }; |
|
166
|
} |
|
167
|
async listFiles(userReporter, projects) { |
|
168
|
const reporter = new import_internalReporter.InternalReporter([userReporter]); |
|
169
|
const config = await this._loadConfigOrReportError(reporter); |
|
170
|
if (!config) |
|
171
|
return { status: "failed" }; |
|
172
|
config.cliProjectFilter = projects?.length ? projects : void 0; |
|
173
|
const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ |
|
174
|
(0, import_tasks.createListFilesTask)(), |
|
175
|
(0, import_tasks.createReportBeginTask)() |
|
176
|
]); |
|
177
|
return { status }; |
|
178
|
} |
|
179
|
async listTests(userReporter, params) { |
|
180
|
let result; |
|
181
|
this._queue = this._queue.then(async () => { |
|
182
|
const { config, status } = await this._innerListTests(userReporter, params); |
|
183
|
if (config) |
|
184
|
await this._updateWatchedDirs(config); |
|
185
|
result = { status }; |
|
186
|
}).catch(printInternalError); |
|
187
|
await this._queue; |
|
188
|
return result; |
|
189
|
} |
|
190
|
async _innerListTests(userReporter, params) { |
|
191
|
const overrides = { |
|
192
|
...this._configCLIOverrides, |
|
193
|
repeatEach: 1, |
|
194
|
retries: 0 |
|
195
|
}; |
|
196
|
const reporter = new import_internalReporter.InternalReporter([userReporter]); |
|
197
|
const config = await this._loadConfigOrReportError(reporter, overrides); |
|
198
|
if (!config) |
|
199
|
return { status: "failed" }; |
|
200
|
config.cliArgs = params.locations || []; |
|
201
|
config.cliGrep = params.grep; |
|
202
|
config.cliGrepInvert = params.grepInvert; |
|
203
|
config.cliProjectFilter = params.projects?.length ? params.projects : void 0; |
|
204
|
config.cliListOnly = true; |
|
205
|
const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ |
|
206
|
(0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: false, filterOnly: false, populateDependencies: this._populateDependenciesOnList }), |
|
207
|
(0, import_tasks.createReportBeginTask)() |
|
208
|
]); |
|
209
|
return { config, status }; |
|
210
|
} |
|
211
|
async _updateWatchedDirs(config) { |
|
212
|
this._watchedProjectDirs = /* @__PURE__ */ new Set(); |
|
213
|
this._ignoredProjectOutputs = /* @__PURE__ */ new Set(); |
|
214
|
for (const p of config.projects) { |
|
215
|
this._watchedProjectDirs.add(p.project.testDir); |
|
216
|
this._ignoredProjectOutputs.add(p.project.outputDir); |
|
217
|
} |
|
218
|
const result = await resolveCtDirs(config); |
|
219
|
if (result) { |
|
220
|
this._watchedProjectDirs.add(result.templateDir); |
|
221
|
this._ignoredProjectOutputs.add(result.outDir); |
|
222
|
} |
|
223
|
if (this._watchTestDirs) |
|
224
|
await this._updateWatcher(false); |
|
225
|
} |
|
226
|
async _updateWatcher(reportPending) { |
|
227
|
await this._watcher.update([...this._watchedProjectDirs, ...this._watchedTestDependencies], [...this._ignoredProjectOutputs], reportPending); |
|
228
|
} |
|
229
|
async runTests(userReporter, params) { |
|
230
|
let result = { status: "passed" }; |
|
231
|
this._queue = this._queue.then(async () => { |
|
232
|
result = await this._innerRunTests(userReporter, params).catch((e) => { |
|
233
|
printInternalError(e); |
|
234
|
return { status: "failed" }; |
|
235
|
}); |
|
236
|
}); |
|
237
|
await this._queue; |
|
238
|
return result; |
|
239
|
} |
|
240
|
async _innerRunTests(userReporter, params) { |
|
241
|
await this.stopTests(); |
|
242
|
const overrides = { |
|
243
|
...this._configCLIOverrides, |
|
244
|
repeatEach: 1, |
|
245
|
retries: 0, |
|
246
|
timeout: params.timeout, |
|
247
|
preserveOutputDir: true, |
|
248
|
reporter: params.reporters ? params.reporters.map((r) => [r]) : void 0, |
|
249
|
use: { |
|
250
|
...this._configCLIOverrides.use, |
|
251
|
...params.trace === "on" ? { trace: { mode: "on", sources: false, _live: true } } : {}, |
|
252
|
...params.trace === "off" ? { trace: "off" } : {}, |
|
253
|
...params.video === "on" || params.video === "off" ? { video: params.video } : {}, |
|
254
|
...params.headed !== void 0 ? { headless: !params.headed } : {}, |
|
255
|
_optionContextReuseMode: params.reuseContext ? "when-possible" : void 0, |
|
256
|
_optionConnectOptions: params.connectWsEndpoint ? { wsEndpoint: params.connectWsEndpoint } : void 0, |
|
257
|
actionTimeout: params.actionTimeout |
|
258
|
}, |
|
259
|
...params.updateSnapshots ? { updateSnapshots: params.updateSnapshots } : {}, |
|
260
|
...params.updateSourceMethod ? { updateSourceMethod: params.updateSourceMethod } : {}, |
|
261
|
...params.runAgents ? { runAgents: params.runAgents } : {}, |
|
262
|
...params.workers ? { workers: params.workers } : {} |
|
263
|
}; |
|
264
|
const config = await this._loadConfigOrReportError(new import_internalReporter.InternalReporter([userReporter]), overrides); |
|
265
|
if (!config) |
|
266
|
return { status: "failed" }; |
|
267
|
config.cliListOnly = false; |
|
268
|
config.cliPassWithNoTests = true; |
|
269
|
config.cliArgs = params.locations; |
|
270
|
config.cliGrep = params.grep; |
|
271
|
config.cliGrepInvert = params.grepInvert; |
|
272
|
config.cliProjectFilter = params.projects?.length ? params.projects : void 0; |
|
273
|
config.preOnlyTestFilters = []; |
|
274
|
if (params.testIds) { |
|
275
|
const testIdSet = new Set(params.testIds); |
|
276
|
config.preOnlyTestFilters.push((test) => testIdSet.has(test.id)); |
|
277
|
} |
|
278
|
const configReporters = params.disableConfigReporters ? [] : await (0, import_reporters.createReporters)(config, "test"); |
|
279
|
const reporter = new import_internalReporter.InternalReporter([...configReporters, userReporter]); |
|
280
|
const stop = new import_utils.ManualPromise(); |
|
281
|
const tasks = [ |
|
282
|
(0, import_tasks.createApplyRebaselinesTask)(), |
|
283
|
(0, import_tasks.createLoadTask)("out-of-process", { filterOnly: true, failOnLoadErrors: !!params.failOnLoadErrors, doNotRunDepsOutsideProjectFilter: params.doNotRunDepsOutsideProjectFilter }), |
|
284
|
...(0, import_tasks.createRunTestsTasks)(config) |
|
285
|
]; |
|
286
|
const testRun = new import_tasks.TestRun(config, reporter, { pauseOnError: params.pauseOnError, pauseAtEnd: params.pauseAtEnd }); |
|
287
|
testRun.failureTracker.onTestPaused = (params2) => this.emit(TestRunnerEvent.TestPaused, params2); |
|
288
|
const run = (0, import_tasks.runTasks)(testRun, tasks, 0, stop).then(async (status) => { |
|
289
|
this._testRun = void 0; |
|
290
|
return status; |
|
291
|
}); |
|
292
|
this._testRun = { run, stop }; |
|
293
|
return { status: await run }; |
|
294
|
} |
|
295
|
async watch(fileNames) { |
|
296
|
this._watchedTestDependencies = /* @__PURE__ */ new Set(); |
|
297
|
for (const fileName of fileNames) { |
|
298
|
this._watchedTestDependencies.add(fileName); |
|
299
|
(0, import_compilationCache.dependenciesForTestFile)(fileName).forEach((file) => this._watchedTestDependencies.add(file)); |
|
300
|
} |
|
301
|
await this._updateWatcher(true); |
|
302
|
} |
|
303
|
async findRelatedTestFiles(files, userReporter) { |
|
304
|
const errorReporter = (0, import_reporters.createErrorCollectingReporter)(import_base.internalScreen); |
|
305
|
const reporter = new import_internalReporter.InternalReporter(userReporter ? [userReporter, errorReporter] : [errorReporter]); |
|
306
|
const config = await this._loadConfigOrReportError(reporter); |
|
307
|
if (!config) |
|
308
|
return { errors: errorReporter.errors(), testFiles: [] }; |
|
309
|
const status = await (0, import_tasks.runTasks)(new import_tasks.TestRun(config, reporter), [ |
|
310
|
...(0, import_tasks.createPluginSetupTasks)(config), |
|
311
|
(0, import_tasks.createLoadTask)("out-of-process", { failOnLoadErrors: true, filterOnly: false, populateDependencies: true }) |
|
312
|
]); |
|
313
|
if (status !== "passed") |
|
314
|
return { errors: errorReporter.errors(), testFiles: [] }; |
|
315
|
return { testFiles: (0, import_compilationCache.affectedTestFiles)(files) }; |
|
316
|
} |
|
317
|
async stopTests() { |
|
318
|
this._testRun?.stop?.resolve(); |
|
319
|
await this._testRun?.run; |
|
320
|
} |
|
321
|
async closeGracefully() { |
|
322
|
(0, import_utils.gracefullyProcessExitDoNotHang)(0); |
|
323
|
} |
|
324
|
async stop() { |
|
325
|
await this.runGlobalTeardown(); |
|
326
|
} |
|
327
|
async _loadConfig(overrides) { |
|
328
|
try { |
|
329
|
const config = await (0, import_configLoader.loadConfig)(this.configLocation, overrides); |
|
330
|
if (!this._plugins) { |
|
331
|
(0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p })); |
|
332
|
(0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config); |
|
333
|
this._plugins = config.plugins || []; |
|
334
|
} else { |
|
335
|
config.plugins.splice(0, config.plugins.length, ...this._plugins); |
|
336
|
} |
|
337
|
return { config }; |
|
338
|
} catch (e) { |
|
339
|
return { config: null, error: (0, import_util.serializeError)(e) }; |
|
340
|
} |
|
341
|
} |
|
342
|
async _loadConfigOrReportError(reporter, overrides) { |
|
343
|
const { config, error } = await this._loadConfig(overrides); |
|
344
|
if (config) |
|
345
|
return config; |
|
346
|
reporter.onConfigure(import_teleReceiver.baseFullConfig); |
|
347
|
reporter.onError(error); |
|
348
|
await reporter.onEnd({ status: "failed" }); |
|
349
|
await reporter.onExit(); |
|
350
|
return null; |
|
351
|
} |
|
352
|
} |
|
353
|
function printInternalError(e) { |
|
354
|
console.error("Internal error:", e); |
|
355
|
} |
|
356
|
async function resolveCtDirs(config) { |
|
357
|
const use = config.config.projects[0].use; |
|
358
|
const relativeTemplateDir = use.ctTemplateDir || "playwright"; |
|
359
|
const templateDir = await import_fs.default.promises.realpath(import_path.default.normalize(import_path.default.join(config.configDir, relativeTemplateDir))).catch(() => void 0); |
|
360
|
if (!templateDir) |
|
361
|
return null; |
|
362
|
const outDir = use.ctCacheDir ? import_path.default.resolve(config.configDir, use.ctCacheDir) : import_path.default.resolve(templateDir, ".cache"); |
|
363
|
return { |
|
364
|
outDir, |
|
365
|
templateDir |
|
366
|
}; |
|
367
|
} |
|
368
|
async function runAllTestsWithConfig(config) { |
|
369
|
(0, import_utils.setPlaywrightTestProcessEnv)(); |
|
370
|
const listOnly = config.cliListOnly; |
|
371
|
(0, import_gitCommitInfoPlugin.addGitCommitInfoPlugin)(config); |
|
372
|
(0, import_webServerPlugin.webServerPluginsForConfig)(config).forEach((p) => config.plugins.push({ factory: p })); |
|
373
|
const reporters = await (0, import_reporters.createReporters)(config, listOnly ? "list" : "test"); |
|
374
|
const lastRun = new import_lastRun.LastRunReporter(config); |
|
375
|
if (config.cliLastFailed) |
|
376
|
await lastRun.filterLastFailed(); |
|
377
|
const reporter = new import_internalReporter.InternalReporter([...reporters, lastRun]); |
|
378
|
const tasks = listOnly ? [ |
|
379
|
(0, import_tasks.createLoadTask)("in-process", { failOnLoadErrors: true, filterOnly: false }), |
|
380
|
(0, import_tasks.createReportBeginTask)() |
|
381
|
] : [ |
|
382
|
(0, import_tasks.createApplyRebaselinesTask)(), |
|
383
|
...(0, import_tasks.createGlobalSetupTasks)(config), |
|
384
|
(0, import_tasks.createLoadTask)("in-process", { filterOnly: true, failOnLoadErrors: true }), |
|
385
|
...(0, import_tasks.createRunTestsTasks)(config) |
|
386
|
]; |
|
387
|
const testRun = new import_tasks.TestRun(config, reporter, { pauseAtEnd: config.configCLIOverrides.pause, pauseOnError: config.configCLIOverrides.pause }); |
|
388
|
const status = await (0, import_tasks.runTasks)(testRun, tasks, config.config.globalTimeout); |
|
389
|
await new Promise((resolve) => process.stdout.write("", () => resolve())); |
|
390
|
await new Promise((resolve) => process.stderr.write("", () => resolve())); |
|
391
|
return status; |
|
392
|
} |
|
393
|
// Annotate the CommonJS export names for ESM import in node: |
|
394
|
0 && (module.exports = { |
|
395
|
TestRunner, |
|
396
|
TestRunnerEvent, |
|
397
|
runAllTestsWithConfig |
|
398
|
}); |
|
399
|
|