workerMain.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. "use strict";
  2. var __defProp = Object.defineProperty;
  3. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  4. var __getOwnPropNames = Object.getOwnPropertyNames;
  5. var __hasOwnProp = Object.prototype.hasOwnProperty;
  6. var __export = (target, all) => {
  7. for (var name in all)
  8. __defProp(target, name, { get: all[name], enumerable: true });
  9. };
  10. var __copyProps = (to, from, except, desc) => {
  11. if (from && typeof from === "object" || typeof from === "function") {
  12. for (let key of __getOwnPropNames(from))
  13. if (!__hasOwnProp.call(to, key) && key !== except)
  14. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  15. }
  16. return to;
  17. };
  18. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  19. var workerMain_exports = {};
  20. __export(workerMain_exports, {
  21. WorkerMain: () => WorkerMain,
  22. create: () => create
  23. });
  24. module.exports = __toCommonJS(workerMain_exports);
  25. var import_utils = require("playwright-core/lib/utils");
  26. var import_utils2 = require("playwright-core/lib/utils");
  27. var import_configLoader = require("../common/configLoader");
  28. var import_globals = require("../common/globals");
  29. var import_ipc = require("../common/ipc");
  30. var import_util = require("../util");
  31. var import_fixtureRunner = require("./fixtureRunner");
  32. var import_testInfo = require("./testInfo");
  33. var import_util2 = require("./util");
  34. var import_fixtures = require("../common/fixtures");
  35. var import_poolBuilder = require("../common/poolBuilder");
  36. var import_process = require("../common/process");
  37. var import_suiteUtils = require("../common/suiteUtils");
  38. var import_testLoader = require("../common/testLoader");
  39. class WorkerMain extends import_process.ProcessRunner {
  40. constructor(params) {
  41. super();
  42. // Accumulated fatal errors that cannot be attributed to a test.
  43. this._fatalErrors = [];
  44. // The stage of the full cleanup. Once "finished", we can safely stop running anything.
  45. this._didRunFullCleanup = false;
  46. // Whether the worker was stopped due to an unhandled error in a test marked with test.fail().
  47. // This should force dispatcher to use a new worker instead.
  48. this._stoppedDueToUnhandledErrorInTestFail = false;
  49. // Whether the worker was requested to stop.
  50. this._isStopped = false;
  51. // This promise resolves once the single "run test group" call finishes.
  52. this._runFinished = new import_utils.ManualPromise();
  53. this._currentTest = null;
  54. this._lastRunningTests = [];
  55. this._totalRunningTests = 0;
  56. // Suites that had their beforeAll hooks, but not afterAll hooks executed.
  57. // These suites still need afterAll hooks to be executed for the proper cleanup.
  58. // Contains dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`.
  59. this._activeSuites = /* @__PURE__ */ new Map();
  60. process.env.TEST_WORKER_INDEX = String(params.workerIndex);
  61. process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
  62. (0, import_globals.setIsWorkerProcess)();
  63. this._params = params;
  64. this._fixtureRunner = new import_fixtureRunner.FixtureRunner();
  65. this._runFinished.resolve();
  66. process.on("unhandledRejection", (reason) => this.unhandledError(reason));
  67. process.on("uncaughtException", (error) => this.unhandledError(error));
  68. process.stdout.write = (chunk, cb) => {
  69. this.dispatchEvent("stdOut", (0, import_ipc.stdioChunkToParams)(chunk));
  70. this._currentTest?._tracing.appendStdioToTrace("stdout", chunk);
  71. if (typeof cb === "function")
  72. process.nextTick(cb);
  73. return true;
  74. };
  75. if (!process.env.PW_RUNNER_DEBUG) {
  76. process.stderr.write = (chunk, cb) => {
  77. this.dispatchEvent("stdErr", (0, import_ipc.stdioChunkToParams)(chunk));
  78. this._currentTest?._tracing.appendStdioToTrace("stderr", chunk);
  79. if (typeof cb === "function")
  80. process.nextTick(cb);
  81. return true;
  82. };
  83. }
  84. }
  85. _stop() {
  86. if (!this._isStopped) {
  87. this._isStopped = true;
  88. this._currentTest?._interrupt();
  89. }
  90. return this._runFinished;
  91. }
  92. async gracefullyClose() {
  93. try {
  94. await this._stop();
  95. if (!this._config) {
  96. return;
  97. }
  98. const fakeTestInfo = new import_testInfo.TestInfoImpl(this._config, this._project, this._params, void 0, 0, () => {
  99. }, () => {
  100. }, () => {
  101. }, () => {
  102. });
  103. const runnable = { type: "teardown" };
  104. await fakeTestInfo._runWithTimeout(runnable, () => this._loadIfNeeded()).catch(() => {
  105. });
  106. await this._fixtureRunner.teardownScope("test", fakeTestInfo, runnable).catch(() => {
  107. });
  108. await this._fixtureRunner.teardownScope("worker", fakeTestInfo, runnable).catch(() => {
  109. });
  110. await fakeTestInfo._runWithTimeout(runnable, () => (0, import_utils.gracefullyCloseAll)()).catch(() => {
  111. });
  112. this._fatalErrors.push(...fakeTestInfo.errors);
  113. } catch (e) {
  114. this._fatalErrors.push((0, import_util2.testInfoError)(e));
  115. }
  116. if (this._fatalErrors.length) {
  117. this._appendProcessTeardownDiagnostics(this._fatalErrors[this._fatalErrors.length - 1]);
  118. const payload = { fatalErrors: this._fatalErrors };
  119. this.dispatchEvent("teardownErrors", payload);
  120. }
  121. }
  122. _appendProcessTeardownDiagnostics(error) {
  123. if (!this._lastRunningTests.length)
  124. return;
  125. const count = this._totalRunningTests === 1 ? "1 test" : `${this._totalRunningTests} tests`;
  126. let lastMessage = "";
  127. if (this._lastRunningTests.length < this._totalRunningTests)
  128. lastMessage = `, last ${this._lastRunningTests.length} tests were`;
  129. const message = [
  130. "",
  131. "",
  132. import_utils2.colors.red(`Failed worker ran ${count}${lastMessage}:`),
  133. ...this._lastRunningTests.map((test) => formatTestTitle(test, this._project.project.name))
  134. ].join("\n");
  135. if (error.message) {
  136. if (error.stack) {
  137. let index = error.stack.indexOf(error.message);
  138. if (index !== -1) {
  139. index += error.message.length;
  140. error.stack = error.stack.substring(0, index) + message + error.stack.substring(index);
  141. }
  142. }
  143. error.message += message;
  144. } else if (error.value) {
  145. error.value += message;
  146. }
  147. }
  148. unhandledError(error) {
  149. if (!this._currentTest) {
  150. if (!this._fatalErrors.length)
  151. this._fatalErrors.push((0, import_util2.testInfoError)(error));
  152. void this._stop();
  153. return;
  154. }
  155. if (!this._currentTest._hasUnhandledError) {
  156. this._currentTest._hasUnhandledError = true;
  157. this._currentTest._failWithError(error);
  158. }
  159. const isExpectError = error instanceof Error && !!error.matcherResult;
  160. const shouldContinueInThisWorker = this._currentTest.expectedStatus === "failed" && isExpectError;
  161. if (!shouldContinueInThisWorker) {
  162. this._stoppedDueToUnhandledErrorInTestFail = true;
  163. void this._stop();
  164. }
  165. }
  166. async _loadIfNeeded() {
  167. if (this._config)
  168. return;
  169. const config = await (0, import_configLoader.deserializeConfig)(this._params.config);
  170. const project = config.projects.find((p) => p.id === this._params.projectId);
  171. if (!project)
  172. throw new Error(`Project "${this._params.projectId}" not found in the worker process. Make sure project name does not change.`);
  173. this._config = config;
  174. this._project = project;
  175. this._poolBuilder = import_poolBuilder.PoolBuilder.createForWorker(this._project);
  176. }
  177. async runTestGroup(runPayload) {
  178. this._runFinished = new import_utils.ManualPromise();
  179. const entries = new Map(runPayload.entries.map((e) => [e.testId, e]));
  180. let fatalUnknownTestIds;
  181. try {
  182. await this._loadIfNeeded();
  183. const fileSuite = await (0, import_testLoader.loadTestFile)(runPayload.file, this._config);
  184. const suite = (0, import_suiteUtils.bindFileSuiteToProject)(this._project, fileSuite);
  185. if (this._params.repeatEachIndex)
  186. (0, import_suiteUtils.applyRepeatEachIndex)(this._project, suite, this._params.repeatEachIndex);
  187. const hasEntries = (0, import_suiteUtils.filterTestsRemoveEmptySuites)(suite, (test) => entries.has(test.id));
  188. if (hasEntries) {
  189. this._poolBuilder.buildPools(suite);
  190. this._activeSuites = /* @__PURE__ */ new Map();
  191. this._didRunFullCleanup = false;
  192. const tests = suite.allTests();
  193. for (let i = 0; i < tests.length; i++) {
  194. if (this._isStopped && this._didRunFullCleanup)
  195. break;
  196. const entry = entries.get(tests[i].id);
  197. entries.delete(tests[i].id);
  198. (0, import_util.debugTest)(`test started "${tests[i].title}"`);
  199. await this._runTest(tests[i], entry.retry, tests[i + 1]);
  200. (0, import_util.debugTest)(`test finished "${tests[i].title}"`);
  201. }
  202. } else {
  203. fatalUnknownTestIds = runPayload.entries.map((e) => e.testId);
  204. void this._stop();
  205. }
  206. } catch (e) {
  207. this._fatalErrors.push((0, import_util2.testInfoError)(e));
  208. void this._stop();
  209. } finally {
  210. const donePayload = {
  211. fatalErrors: this._fatalErrors,
  212. skipTestsDueToSetupFailure: [],
  213. fatalUnknownTestIds,
  214. stoppedDueToUnhandledErrorInTestFail: this._stoppedDueToUnhandledErrorInTestFail
  215. };
  216. for (const test of this._skipRemainingTestsInSuite?.allTests() || []) {
  217. if (entries.has(test.id))
  218. donePayload.skipTestsDueToSetupFailure.push(test.id);
  219. }
  220. this.dispatchEvent("done", donePayload);
  221. this._fatalErrors = [];
  222. this._skipRemainingTestsInSuite = void 0;
  223. this._runFinished.resolve();
  224. }
  225. }
  226. async customMessage(payload) {
  227. try {
  228. if (this._currentTest?.testId !== payload.testId)
  229. throw new Error("Test has already stopped");
  230. const response = await this._currentTest._onCustomMessageCallback?.(payload.request);
  231. return { response };
  232. } catch (error) {
  233. return { response: {}, error: (0, import_util2.testInfoError)(error) };
  234. }
  235. }
  236. async _runTest(test, retry, nextTest) {
  237. const testInfo = new import_testInfo.TestInfoImpl(
  238. this._config,
  239. this._project,
  240. this._params,
  241. test,
  242. retry,
  243. (stepBeginPayload) => this.dispatchEvent("stepBegin", stepBeginPayload),
  244. (stepEndPayload) => this.dispatchEvent("stepEnd", stepEndPayload),
  245. (attachment) => this.dispatchEvent("attach", attachment),
  246. (testPausedPayload) => this.dispatchEvent("testPaused", testPausedPayload)
  247. );
  248. const processAnnotation = (annotation) => {
  249. testInfo.annotations.push(annotation);
  250. switch (annotation.type) {
  251. case "fixme":
  252. case "skip":
  253. testInfo.expectedStatus = "skipped";
  254. break;
  255. case "fail":
  256. if (testInfo.expectedStatus !== "skipped")
  257. testInfo.expectedStatus = "failed";
  258. break;
  259. case "slow":
  260. testInfo._timeoutManager.slow();
  261. break;
  262. }
  263. };
  264. if (!this._isStopped)
  265. this._fixtureRunner.setPool(test._pool);
  266. const suites = getSuites(test);
  267. const reversedSuites = suites.slice().reverse();
  268. const nextSuites = new Set(getSuites(nextTest));
  269. testInfo._timeoutManager.setTimeout(test.timeout);
  270. for (const annotation of test.annotations)
  271. processAnnotation(annotation);
  272. for (const suite of suites) {
  273. const extraAnnotations = this._activeSuites.get(suite) || [];
  274. for (const annotation of extraAnnotations)
  275. processAnnotation(annotation);
  276. }
  277. this._currentTest = testInfo;
  278. (0, import_globals.setCurrentTestInfo)(testInfo);
  279. this.dispatchEvent("testBegin", buildTestBeginPayload(testInfo));
  280. const isSkipped = testInfo.expectedStatus === "skipped";
  281. const hasAfterAllToRunBeforeNextTest = reversedSuites.some((suite) => {
  282. return this._activeSuites.has(suite) && !nextSuites.has(suite) && suite._hooks.some((hook) => hook.type === "afterAll");
  283. });
  284. if (isSkipped && nextTest && !hasAfterAllToRunBeforeNextTest) {
  285. testInfo.status = "skipped";
  286. this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
  287. return;
  288. }
  289. this._totalRunningTests++;
  290. this._lastRunningTests.push(test);
  291. if (this._lastRunningTests.length > 10)
  292. this._lastRunningTests.shift();
  293. let shouldRunAfterEachHooks = false;
  294. testInfo._allowSkips = true;
  295. await (async () => {
  296. await testInfo._runWithTimeout({ type: "test" }, async () => {
  297. const traceFixtureRegistration = test._pool.resolve("trace");
  298. if (!traceFixtureRegistration)
  299. return;
  300. if (typeof traceFixtureRegistration.fn === "function")
  301. throw new Error(`"trace" option cannot be a function`);
  302. await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn);
  303. });
  304. if (this._isStopped || isSkipped) {
  305. testInfo.status = "skipped";
  306. return;
  307. }
  308. await (0, import_utils.removeFolders)([testInfo.outputDir]);
  309. let testFunctionParams = null;
  310. await testInfo._runAsStep({ title: "Before Hooks", category: "hook" }, async () => {
  311. for (const suite of suites)
  312. await this._runBeforeAllHooksForSuite(suite, testInfo);
  313. shouldRunAfterEachHooks = true;
  314. await this._runEachHooksForSuites(suites, "beforeEach", testInfo);
  315. testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, "test", { type: "test" });
  316. });
  317. if (testFunctionParams === null) {
  318. return;
  319. }
  320. await testInfo._runWithTimeout({ type: "test" }, async () => {
  321. const fn = test.fn;
  322. await fn(testFunctionParams, testInfo);
  323. });
  324. })().catch(() => {
  325. });
  326. testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed | 0;
  327. testInfo._allowSkips = true;
  328. const afterHooksTimeout = calculateMaxTimeout(this._project.project.timeout, testInfo.timeout);
  329. const afterHooksSlot = { timeout: afterHooksTimeout, elapsed: 0 };
  330. await testInfo._runAsStep({ title: "After Hooks", category: "hook" }, async () => {
  331. let firstAfterHooksError;
  332. try {
  333. await testInfo._runWithTimeout({ type: "test", slot: afterHooksSlot }, () => testInfo._didFinishTestFunction());
  334. } catch (error) {
  335. firstAfterHooksError = firstAfterHooksError ?? error;
  336. }
  337. try {
  338. if (shouldRunAfterEachHooks)
  339. await this._runEachHooksForSuites(reversedSuites, "afterEach", testInfo, afterHooksSlot);
  340. } catch (error) {
  341. firstAfterHooksError = firstAfterHooksError ?? error;
  342. }
  343. testInfo._tracing.didFinishTestFunctionAndAfterEachHooks();
  344. try {
  345. await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: afterHooksSlot });
  346. } catch (error) {
  347. firstAfterHooksError = firstAfterHooksError ?? error;
  348. }
  349. for (const suite of reversedSuites) {
  350. if (!nextSuites.has(suite) || testInfo._isFailure()) {
  351. try {
  352. await this._runAfterAllHooksForSuite(suite, testInfo);
  353. } catch (error) {
  354. firstAfterHooksError = firstAfterHooksError ?? error;
  355. }
  356. }
  357. }
  358. if (firstAfterHooksError)
  359. throw firstAfterHooksError;
  360. }).catch(() => {
  361. });
  362. if (testInfo._isFailure())
  363. this._isStopped = true;
  364. if (this._isStopped) {
  365. this._didRunFullCleanup = true;
  366. await testInfo._runAsStep({ title: "Worker Cleanup", category: "hook" }, async () => {
  367. let firstWorkerCleanupError;
  368. const teardownSlot = { timeout: this._project.project.timeout, elapsed: 0 };
  369. try {
  370. await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: teardownSlot });
  371. } catch (error) {
  372. firstWorkerCleanupError = firstWorkerCleanupError ?? error;
  373. }
  374. for (const suite of reversedSuites) {
  375. try {
  376. await this._runAfterAllHooksForSuite(suite, testInfo);
  377. } catch (error) {
  378. firstWorkerCleanupError = firstWorkerCleanupError ?? error;
  379. }
  380. }
  381. try {
  382. await this._fixtureRunner.teardownScope("worker", testInfo, { type: "teardown", slot: teardownSlot });
  383. } catch (error) {
  384. firstWorkerCleanupError = firstWorkerCleanupError ?? error;
  385. }
  386. if (firstWorkerCleanupError)
  387. throw firstWorkerCleanupError;
  388. }).catch(() => {
  389. });
  390. }
  391. const tracingSlot = { timeout: this._project.project.timeout, elapsed: 0 };
  392. await testInfo._runWithTimeout({ type: "test", slot: tracingSlot }, async () => {
  393. await testInfo._tracing.stopIfNeeded();
  394. }).catch(() => {
  395. });
  396. testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed + afterHooksSlot.elapsed | 0;
  397. this._currentTest = null;
  398. (0, import_globals.setCurrentTestInfo)(null);
  399. this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
  400. const preserveOutput = this._config.config.preserveOutput === "always" || this._config.config.preserveOutput === "failures-only" && testInfo._isFailure();
  401. if (!preserveOutput)
  402. await (0, import_utils.removeFolders)([testInfo.outputDir]);
  403. }
  404. _collectHooksAndModifiers(suite, type, testInfo) {
  405. const runnables = [];
  406. for (const modifier of suite._modifiers) {
  407. const modifierType = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? "beforeAll" : "beforeEach";
  408. if (modifierType !== type)
  409. continue;
  410. const fn = async (fixtures) => {
  411. const result = await modifier.fn(fixtures);
  412. testInfo._modifier(modifier.type, modifier.location, [!!result, modifier.description]);
  413. };
  414. (0, import_fixtures.inheritFixtureNames)(modifier.fn, fn);
  415. runnables.push({
  416. title: `${modifier.type} modifier`,
  417. location: modifier.location,
  418. type: modifier.type,
  419. fn
  420. });
  421. }
  422. runnables.push(...suite._hooks.filter((hook) => hook.type === type));
  423. return runnables;
  424. }
  425. async _runBeforeAllHooksForSuite(suite, testInfo) {
  426. if (this._activeSuites.has(suite))
  427. return;
  428. const extraAnnotations = [];
  429. this._activeSuites.set(suite, extraAnnotations);
  430. await this._runAllHooksForSuite(suite, testInfo, "beforeAll", extraAnnotations);
  431. }
  432. async _runAllHooksForSuite(suite, testInfo, type, extraAnnotations) {
  433. let firstError;
  434. for (const hook of this._collectHooksAndModifiers(suite, type, testInfo)) {
  435. try {
  436. await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
  437. const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 };
  438. const runnable = { type: hook.type, slot: timeSlot, location: hook.location };
  439. const existingAnnotations = new Set(testInfo.annotations);
  440. try {
  441. await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "all-hooks-only", runnable);
  442. } finally {
  443. if (extraAnnotations) {
  444. const newAnnotations = testInfo.annotations.filter((a) => !existingAnnotations.has(a));
  445. extraAnnotations.push(...newAnnotations);
  446. }
  447. await this._fixtureRunner.teardownScope("test", testInfo, runnable);
  448. }
  449. });
  450. } catch (error) {
  451. firstError = firstError ?? error;
  452. if (type === "beforeAll" && error instanceof import_testInfo.TestSkipError)
  453. break;
  454. if (type === "beforeAll" && !this._skipRemainingTestsInSuite) {
  455. this._skipRemainingTestsInSuite = suite;
  456. }
  457. }
  458. }
  459. if (firstError)
  460. throw firstError;
  461. }
  462. async _runAfterAllHooksForSuite(suite, testInfo) {
  463. if (!this._activeSuites.has(suite))
  464. return;
  465. this._activeSuites.delete(suite);
  466. await this._runAllHooksForSuite(suite, testInfo, "afterAll");
  467. }
  468. async _runEachHooksForSuites(suites, type, testInfo, slot) {
  469. let firstError;
  470. const hooks = suites.map((suite) => this._collectHooksAndModifiers(suite, type, testInfo)).flat();
  471. for (const hook of hooks) {
  472. const runnable = { type: hook.type, location: hook.location, slot };
  473. if (testInfo._timeoutManager.isTimeExhaustedFor(runnable)) {
  474. continue;
  475. }
  476. try {
  477. await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
  478. await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "test", runnable);
  479. });
  480. } catch (error) {
  481. firstError = firstError ?? error;
  482. if (error instanceof import_testInfo.TestSkipError)
  483. break;
  484. }
  485. }
  486. if (firstError)
  487. throw firstError;
  488. }
  489. }
  490. function buildTestBeginPayload(testInfo) {
  491. return {
  492. testId: testInfo.testId,
  493. startWallTime: testInfo._startWallTime
  494. };
  495. }
  496. function buildTestEndPayload(testInfo) {
  497. return {
  498. testId: testInfo.testId,
  499. duration: testInfo.duration,
  500. status: testInfo.status,
  501. errors: testInfo.errors,
  502. hasNonRetriableError: testInfo._hasNonRetriableError,
  503. expectedStatus: testInfo.expectedStatus,
  504. annotations: testInfo.annotations,
  505. timeout: testInfo.timeout
  506. };
  507. }
  508. function getSuites(test) {
  509. const suites = [];
  510. for (let suite = test?.parent; suite; suite = suite.parent)
  511. suites.push(suite);
  512. suites.reverse();
  513. return suites;
  514. }
  515. function formatTestTitle(test, projectName) {
  516. const [, ...titles] = test.titlePath();
  517. const location = `${(0, import_util.relativeFilePath)(test.location.file)}:${test.location.line}:${test.location.column}`;
  518. const projectTitle = projectName ? `[${projectName}] \u203A ` : "";
  519. return `${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`;
  520. }
  521. function calculateMaxTimeout(t1, t2) {
  522. return !t1 || !t2 ? 0 : Math.max(t1, t2);
  523. }
  524. const create = (params) => new WorkerMain(params);
  525. // Annotate the CommonJS export names for ESM import in node:
  526. 0 && (module.exports = {
  527. WorkerMain,
  528. create
  529. });