| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- "use strict";
- var __defProp = Object.defineProperty;
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
- var __getOwnPropNames = Object.getOwnPropertyNames;
- var __hasOwnProp = Object.prototype.hasOwnProperty;
- var __export = (target, all) => {
- for (var name in all)
- __defProp(target, name, { get: all[name], enumerable: true });
- };
- var __copyProps = (to, from, except, desc) => {
- if (from && typeof from === "object" || typeof from === "function") {
- for (let key of __getOwnPropNames(from))
- if (!__hasOwnProp.call(to, key) && key !== except)
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
- }
- return to;
- };
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
- var workerMain_exports = {};
- __export(workerMain_exports, {
- WorkerMain: () => WorkerMain,
- create: () => create
- });
- module.exports = __toCommonJS(workerMain_exports);
- var import_utils = require("playwright-core/lib/utils");
- var import_utils2 = require("playwright-core/lib/utils");
- var import_configLoader = require("../common/configLoader");
- var import_globals = require("../common/globals");
- var import_ipc = require("../common/ipc");
- var import_util = require("../util");
- var import_fixtureRunner = require("./fixtureRunner");
- var import_testInfo = require("./testInfo");
- var import_util2 = require("./util");
- var import_fixtures = require("../common/fixtures");
- var import_poolBuilder = require("../common/poolBuilder");
- var import_process = require("../common/process");
- var import_suiteUtils = require("../common/suiteUtils");
- var import_testLoader = require("../common/testLoader");
- class WorkerMain extends import_process.ProcessRunner {
- constructor(params) {
- super();
- // Accumulated fatal errors that cannot be attributed to a test.
- this._fatalErrors = [];
- // The stage of the full cleanup. Once "finished", we can safely stop running anything.
- this._didRunFullCleanup = false;
- // Whether the worker was requested to stop.
- this._isStopped = false;
- // This promise resolves once the single "run test group" call finishes.
- this._runFinished = new import_utils.ManualPromise();
- this._currentTest = null;
- this._lastRunningTests = [];
- this._totalRunningTests = 0;
- // Suites that had their beforeAll hooks, but not afterAll hooks executed.
- // These suites still need afterAll hooks to be executed for the proper cleanup.
- // Contains dynamic annotations originated by modifiers with a callback, e.g. `test.skip(() => true)`.
- this._activeSuites = /* @__PURE__ */ new Map();
- process.env.TEST_WORKER_INDEX = String(params.workerIndex);
- process.env.TEST_PARALLEL_INDEX = String(params.parallelIndex);
- (0, import_globals.setIsWorkerProcess)();
- this._params = params;
- this._fixtureRunner = new import_fixtureRunner.FixtureRunner();
- this._runFinished.resolve();
- process.on("unhandledRejection", (reason) => this.unhandledError(reason));
- process.on("uncaughtException", (error) => this.unhandledError(error));
- process.stdout.write = (chunk, cb) => {
- this.dispatchEvent("stdOut", (0, import_ipc.stdioChunkToParams)(chunk));
- this._currentTest?._tracing.appendStdioToTrace("stdout", chunk);
- if (typeof cb === "function")
- process.nextTick(cb);
- return true;
- };
- if (!process.env.PW_RUNNER_DEBUG) {
- process.stderr.write = (chunk, cb) => {
- this.dispatchEvent("stdErr", (0, import_ipc.stdioChunkToParams)(chunk));
- this._currentTest?._tracing.appendStdioToTrace("stderr", chunk);
- if (typeof cb === "function")
- process.nextTick(cb);
- return true;
- };
- }
- }
- _stop() {
- if (!this._isStopped) {
- this._isStopped = true;
- this._currentTest?._interrupt();
- }
- return this._runFinished;
- }
- async gracefullyClose() {
- try {
- await this._stop();
- if (!this._config) {
- return;
- }
- const fakeTestInfo = new import_testInfo.TestInfoImpl(this._config, this._project, this._params, void 0, 0, () => {
- }, () => {
- }, () => {
- }, () => {
- });
- const runnable = { type: "teardown" };
- await fakeTestInfo._runWithTimeout(runnable, () => this._loadIfNeeded()).catch(() => {
- });
- await this._fixtureRunner.teardownScope("test", fakeTestInfo, runnable).catch(() => {
- });
- await this._fixtureRunner.teardownScope("worker", fakeTestInfo, runnable).catch(() => {
- });
- await fakeTestInfo._runWithTimeout(runnable, () => (0, import_utils.gracefullyCloseAll)()).catch(() => {
- });
- this._fatalErrors.push(...fakeTestInfo.errors);
- } catch (e) {
- this._fatalErrors.push((0, import_util2.testInfoError)(e));
- }
- if (this._fatalErrors.length) {
- this._appendProcessTeardownDiagnostics(this._fatalErrors[this._fatalErrors.length - 1]);
- const payload = { fatalErrors: this._fatalErrors };
- this.dispatchEvent("teardownErrors", payload);
- }
- }
- _appendProcessTeardownDiagnostics(error) {
- if (!this._lastRunningTests.length)
- return;
- const count = this._totalRunningTests === 1 ? "1 test" : `${this._totalRunningTests} tests`;
- let lastMessage = "";
- if (this._lastRunningTests.length < this._totalRunningTests)
- lastMessage = `, last ${this._lastRunningTests.length} tests were`;
- const message = [
- "",
- "",
- import_utils2.colors.red(`Failed worker ran ${count}${lastMessage}:`),
- ...this._lastRunningTests.map((test) => formatTestTitle(test, this._project.project.name))
- ].join("\n");
- if (error.message) {
- if (error.stack) {
- let index = error.stack.indexOf(error.message);
- if (index !== -1) {
- index += error.message.length;
- error.stack = error.stack.substring(0, index) + message + error.stack.substring(index);
- }
- }
- error.message += message;
- } else if (error.value) {
- error.value += message;
- }
- }
- unhandledError(error) {
- if (!this._currentTest) {
- if (!this._fatalErrors.length)
- this._fatalErrors.push((0, import_util2.testInfoError)(error));
- void this._stop();
- return;
- }
- if (!this._currentTest._hasUnhandledError) {
- this._currentTest._hasUnhandledError = true;
- this._currentTest._failWithError(error);
- }
- const isExpectError = error instanceof Error && !!error.matcherResult;
- const shouldContinueInThisWorker = this._currentTest.expectedStatus === "failed" && isExpectError;
- if (!shouldContinueInThisWorker)
- void this._stop();
- }
- async _loadIfNeeded() {
- if (this._config)
- return;
- const config = await (0, import_configLoader.deserializeConfig)(this._params.config);
- const project = config.projects.find((p) => p.id === this._params.projectId);
- if (!project)
- throw new Error(`Project "${this._params.projectId}" not found in the worker process. Make sure project name does not change.`);
- this._config = config;
- this._project = project;
- this._poolBuilder = import_poolBuilder.PoolBuilder.createForWorker(this._project);
- }
- async runTestGroup(runPayload) {
- this._runFinished = new import_utils.ManualPromise();
- const entries = new Map(runPayload.entries.map((e) => [e.testId, e]));
- let fatalUnknownTestIds;
- try {
- await this._loadIfNeeded();
- const fileSuite = await (0, import_testLoader.loadTestFile)(runPayload.file, this._config.config.rootDir);
- const suite = (0, import_suiteUtils.bindFileSuiteToProject)(this._project, fileSuite);
- if (this._params.repeatEachIndex)
- (0, import_suiteUtils.applyRepeatEachIndex)(this._project, suite, this._params.repeatEachIndex);
- const hasEntries = (0, import_suiteUtils.filterTestsRemoveEmptySuites)(suite, (test) => entries.has(test.id));
- if (hasEntries) {
- this._poolBuilder.buildPools(suite);
- this._activeSuites = /* @__PURE__ */ new Map();
- this._didRunFullCleanup = false;
- const tests = suite.allTests();
- for (let i = 0; i < tests.length; i++) {
- if (this._isStopped && this._didRunFullCleanup)
- break;
- const entry = entries.get(tests[i].id);
- entries.delete(tests[i].id);
- (0, import_util.debugTest)(`test started "${tests[i].title}"`);
- await this._runTest(tests[i], entry.retry, tests[i + 1]);
- (0, import_util.debugTest)(`test finished "${tests[i].title}"`);
- }
- } else {
- fatalUnknownTestIds = runPayload.entries.map((e) => e.testId);
- void this._stop();
- }
- } catch (e) {
- this._fatalErrors.push((0, import_util2.testInfoError)(e));
- void this._stop();
- } finally {
- const donePayload = {
- fatalErrors: this._fatalErrors,
- skipTestsDueToSetupFailure: [],
- fatalUnknownTestIds
- };
- for (const test of this._skipRemainingTestsInSuite?.allTests() || []) {
- if (entries.has(test.id))
- donePayload.skipTestsDueToSetupFailure.push(test.id);
- }
- this.dispatchEvent("done", donePayload);
- this._fatalErrors = [];
- this._skipRemainingTestsInSuite = void 0;
- this._runFinished.resolve();
- }
- }
- resumeAfterStepError(params) {
- this._currentTest?.resumeAfterStepError(params);
- }
- async _runTest(test, retry, nextTest) {
- const testInfo = new import_testInfo.TestInfoImpl(
- this._config,
- this._project,
- this._params,
- test,
- retry,
- (stepBeginPayload) => this.dispatchEvent("stepBegin", stepBeginPayload),
- (stepRecoverFromErrorPayload) => this.dispatchEvent("stepRecoverFromError", stepRecoverFromErrorPayload),
- (stepEndPayload) => this.dispatchEvent("stepEnd", stepEndPayload),
- (attachment) => this.dispatchEvent("attach", attachment)
- );
- const processAnnotation = (annotation) => {
- testInfo.annotations.push(annotation);
- switch (annotation.type) {
- case "fixme":
- case "skip":
- testInfo.expectedStatus = "skipped";
- break;
- case "fail":
- if (testInfo.expectedStatus !== "skipped")
- testInfo.expectedStatus = "failed";
- break;
- case "slow":
- testInfo._timeoutManager.slow();
- break;
- }
- };
- if (!this._isStopped)
- this._fixtureRunner.setPool(test._pool);
- const suites = getSuites(test);
- const reversedSuites = suites.slice().reverse();
- const nextSuites = new Set(getSuites(nextTest));
- testInfo._timeoutManager.setTimeout(test.timeout);
- for (const annotation of test.annotations)
- processAnnotation(annotation);
- for (const suite of suites) {
- const extraAnnotations = this._activeSuites.get(suite) || [];
- for (const annotation of extraAnnotations)
- processAnnotation(annotation);
- }
- this._currentTest = testInfo;
- (0, import_globals.setCurrentTestInfo)(testInfo);
- this.dispatchEvent("testBegin", buildTestBeginPayload(testInfo));
- const isSkipped = testInfo.expectedStatus === "skipped";
- const hasAfterAllToRunBeforeNextTest = reversedSuites.some((suite) => {
- return this._activeSuites.has(suite) && !nextSuites.has(suite) && suite._hooks.some((hook) => hook.type === "afterAll");
- });
- if (isSkipped && nextTest && !hasAfterAllToRunBeforeNextTest) {
- testInfo.status = "skipped";
- this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
- return;
- }
- this._totalRunningTests++;
- this._lastRunningTests.push(test);
- if (this._lastRunningTests.length > 10)
- this._lastRunningTests.shift();
- let shouldRunAfterEachHooks = false;
- testInfo._allowSkips = true;
- await (async () => {
- await testInfo._runWithTimeout({ type: "test" }, async () => {
- const traceFixtureRegistration = test._pool.resolve("trace");
- if (!traceFixtureRegistration)
- return;
- if (typeof traceFixtureRegistration.fn === "function")
- throw new Error(`"trace" option cannot be a function`);
- await testInfo._tracing.startIfNeeded(traceFixtureRegistration.fn);
- });
- if (this._isStopped || isSkipped) {
- testInfo.status = "skipped";
- return;
- }
- await (0, import_utils.removeFolders)([testInfo.outputDir]);
- let testFunctionParams = null;
- await testInfo._runAsStep({ title: "Before Hooks", category: "hook" }, async () => {
- for (const suite of suites)
- await this._runBeforeAllHooksForSuite(suite, testInfo);
- shouldRunAfterEachHooks = true;
- await this._runEachHooksForSuites(suites, "beforeEach", testInfo);
- testFunctionParams = await this._fixtureRunner.resolveParametersForFunction(test.fn, testInfo, "test", { type: "test" });
- });
- if (testFunctionParams === null) {
- return;
- }
- await testInfo._runWithTimeout({ type: "test" }, async () => {
- const fn = test.fn;
- await fn(testFunctionParams, testInfo);
- });
- })().catch(() => {
- });
- testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed | 0;
- testInfo._allowSkips = true;
- const afterHooksTimeout = calculateMaxTimeout(this._project.project.timeout, testInfo.timeout);
- const afterHooksSlot = { timeout: afterHooksTimeout, elapsed: 0 };
- await testInfo._runAsStep({ title: "After Hooks", category: "hook" }, async () => {
- let firstAfterHooksError;
- try {
- await testInfo._runWithTimeout({ type: "test", slot: afterHooksSlot }, async () => testInfo._onDidFinishTestFunction?.());
- } catch (error) {
- firstAfterHooksError = firstAfterHooksError ?? error;
- }
- try {
- if (shouldRunAfterEachHooks)
- await this._runEachHooksForSuites(reversedSuites, "afterEach", testInfo, afterHooksSlot);
- } catch (error) {
- firstAfterHooksError = firstAfterHooksError ?? error;
- }
- testInfo._tracing.didFinishTestFunctionAndAfterEachHooks();
- try {
- await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: afterHooksSlot });
- } catch (error) {
- firstAfterHooksError = firstAfterHooksError ?? error;
- }
- for (const suite of reversedSuites) {
- if (!nextSuites.has(suite) || testInfo._isFailure()) {
- try {
- await this._runAfterAllHooksForSuite(suite, testInfo);
- } catch (error) {
- firstAfterHooksError = firstAfterHooksError ?? error;
- }
- }
- }
- if (firstAfterHooksError)
- throw firstAfterHooksError;
- }).catch(() => {
- });
- if (testInfo._isFailure())
- this._isStopped = true;
- if (this._isStopped) {
- this._didRunFullCleanup = true;
- await testInfo._runAsStep({ title: "Worker Cleanup", category: "hook" }, async () => {
- let firstWorkerCleanupError;
- const teardownSlot = { timeout: this._project.project.timeout, elapsed: 0 };
- try {
- await this._fixtureRunner.teardownScope("test", testInfo, { type: "test", slot: teardownSlot });
- } catch (error) {
- firstWorkerCleanupError = firstWorkerCleanupError ?? error;
- }
- for (const suite of reversedSuites) {
- try {
- await this._runAfterAllHooksForSuite(suite, testInfo);
- } catch (error) {
- firstWorkerCleanupError = firstWorkerCleanupError ?? error;
- }
- }
- try {
- await this._fixtureRunner.teardownScope("worker", testInfo, { type: "teardown", slot: teardownSlot });
- } catch (error) {
- firstWorkerCleanupError = firstWorkerCleanupError ?? error;
- }
- if (firstWorkerCleanupError)
- throw firstWorkerCleanupError;
- }).catch(() => {
- });
- }
- const tracingSlot = { timeout: this._project.project.timeout, elapsed: 0 };
- await testInfo._runWithTimeout({ type: "test", slot: tracingSlot }, async () => {
- await testInfo._tracing.stopIfNeeded();
- }).catch(() => {
- });
- testInfo.duration = testInfo._timeoutManager.defaultSlot().elapsed + afterHooksSlot.elapsed | 0;
- this._currentTest = null;
- (0, import_globals.setCurrentTestInfo)(null);
- this.dispatchEvent("testEnd", buildTestEndPayload(testInfo));
- const preserveOutput = this._config.config.preserveOutput === "always" || this._config.config.preserveOutput === "failures-only" && testInfo._isFailure();
- if (!preserveOutput)
- await (0, import_utils.removeFolders)([testInfo.outputDir]);
- }
- _collectHooksAndModifiers(suite, type, testInfo) {
- const runnables = [];
- for (const modifier of suite._modifiers) {
- const modifierType = this._fixtureRunner.dependsOnWorkerFixturesOnly(modifier.fn, modifier.location) ? "beforeAll" : "beforeEach";
- if (modifierType !== type)
- continue;
- const fn = async (fixtures) => {
- const result = await modifier.fn(fixtures);
- testInfo._modifier(modifier.type, modifier.location, [!!result, modifier.description]);
- };
- (0, import_fixtures.inheritFixtureNames)(modifier.fn, fn);
- runnables.push({
- title: `${modifier.type} modifier`,
- location: modifier.location,
- type: modifier.type,
- fn
- });
- }
- runnables.push(...suite._hooks.filter((hook) => hook.type === type));
- return runnables;
- }
- async _runBeforeAllHooksForSuite(suite, testInfo) {
- if (this._activeSuites.has(suite))
- return;
- const extraAnnotations = [];
- this._activeSuites.set(suite, extraAnnotations);
- await this._runAllHooksForSuite(suite, testInfo, "beforeAll", extraAnnotations);
- }
- async _runAllHooksForSuite(suite, testInfo, type, extraAnnotations) {
- let firstError;
- for (const hook of this._collectHooksAndModifiers(suite, type, testInfo)) {
- try {
- await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
- const timeSlot = { timeout: this._project.project.timeout, elapsed: 0 };
- const runnable = { type: hook.type, slot: timeSlot, location: hook.location };
- const existingAnnotations = new Set(testInfo.annotations);
- try {
- await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "all-hooks-only", runnable);
- } finally {
- if (extraAnnotations) {
- const newAnnotations = testInfo.annotations.filter((a) => !existingAnnotations.has(a));
- extraAnnotations.push(...newAnnotations);
- }
- await this._fixtureRunner.teardownScope("test", testInfo, runnable);
- }
- });
- } catch (error) {
- firstError = firstError ?? error;
- if (type === "beforeAll" && error instanceof import_testInfo.TestSkipError)
- break;
- if (type === "beforeAll" && !this._skipRemainingTestsInSuite) {
- this._skipRemainingTestsInSuite = suite;
- }
- }
- }
- if (firstError)
- throw firstError;
- }
- async _runAfterAllHooksForSuite(suite, testInfo) {
- if (!this._activeSuites.has(suite))
- return;
- this._activeSuites.delete(suite);
- await this._runAllHooksForSuite(suite, testInfo, "afterAll");
- }
- async _runEachHooksForSuites(suites, type, testInfo, slot) {
- let firstError;
- const hooks = suites.map((suite) => this._collectHooksAndModifiers(suite, type, testInfo)).flat();
- for (const hook of hooks) {
- const runnable = { type: hook.type, location: hook.location, slot };
- if (testInfo._timeoutManager.isTimeExhaustedFor(runnable)) {
- continue;
- }
- try {
- await testInfo._runAsStep({ title: hook.title, category: "hook", location: hook.location }, async () => {
- await this._fixtureRunner.resolveParametersAndRunFunction(hook.fn, testInfo, "test", runnable);
- });
- } catch (error) {
- firstError = firstError ?? error;
- if (error instanceof import_testInfo.TestSkipError)
- break;
- }
- }
- if (firstError)
- throw firstError;
- }
- }
- function buildTestBeginPayload(testInfo) {
- return {
- testId: testInfo.testId,
- startWallTime: testInfo._startWallTime
- };
- }
- function buildTestEndPayload(testInfo) {
- return {
- testId: testInfo.testId,
- duration: testInfo.duration,
- status: testInfo.status,
- errors: testInfo.errors,
- hasNonRetriableError: testInfo._hasNonRetriableError,
- expectedStatus: testInfo.expectedStatus,
- annotations: testInfo.annotations,
- timeout: testInfo.timeout
- };
- }
- function getSuites(test) {
- const suites = [];
- for (let suite = test?.parent; suite; suite = suite.parent)
- suites.push(suite);
- suites.reverse();
- return suites;
- }
- function formatTestTitle(test, projectName) {
- const [, ...titles] = test.titlePath();
- const location = `${(0, import_util.relativeFilePath)(test.location.file)}:${test.location.line}:${test.location.column}`;
- const projectTitle = projectName ? `[${projectName}] \u203A ` : "";
- return `${projectTitle}${location} \u203A ${titles.join(" \u203A ")}`;
- }
- function calculateMaxTimeout(t1, t2) {
- return !t1 || !t2 ? 0 : Math.max(t1, t2);
- }
- const create = (params) => new WorkerMain(params);
- // Annotate the CommonJS export names for ESM import in node:
- 0 && (module.exports = {
- WorkerMain,
- create
- });
|