testRunner.js 17 KB

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