Implement comprehensive frontend integration testing with Playwright
- Add Playwright E2E testing framework with cross-browser support (Chrome, Firefox) - Create authentication helpers for CalDAV server integration - Implement calendar interaction helpers with event creation, drag-and-drop, and view switching - Add comprehensive drag-and-drop test suite with event cleanup - Configure CI/CD integration with Gitea Actions for headless testing - Support both local development and CI environments with proper dependency management - Include video recording, screenshots, and HTML reporting for test debugging - Handle Firefox-specific timing and interaction challenges with force clicks and timeouts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										250
									
								
								frontend/e2e/node_modules/playwright/lib/worker/fixtureRunner.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								frontend/e2e/node_modules/playwright/lib/worker/fixtureRunner.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,250 @@ | ||||
| "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 fixtureRunner_exports = {}; | ||||
| __export(fixtureRunner_exports, { | ||||
|   FixtureRunner: () => FixtureRunner | ||||
| }); | ||||
| module.exports = __toCommonJS(fixtureRunner_exports); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_fixtures = require("../common/fixtures"); | ||||
| var import_util = require("../util"); | ||||
| class Fixture { | ||||
|   constructor(runner, registration) { | ||||
|     this.failed = false; | ||||
|     this._deps = /* @__PURE__ */ new Set(); | ||||
|     this._usages = /* @__PURE__ */ new Set(); | ||||
|     this.runner = runner; | ||||
|     this.registration = registration; | ||||
|     this.value = null; | ||||
|     const isUserFixture = this.registration.location && (0, import_util.filterStackFile)(this.registration.location.file); | ||||
|     const title = this.registration.customTitle || this.registration.name; | ||||
|     const location = isUserFixture ? this.registration.location : void 0; | ||||
|     this._stepInfo = { title: `Fixture ${(0, import_utils.escapeWithQuotes)(title, '"')}`, category: "fixture", location }; | ||||
|     if (this.registration.box) | ||||
|       this._stepInfo.group = isUserFixture ? "configuration" : "internal"; | ||||
|     this._setupDescription = { | ||||
|       title, | ||||
|       phase: "setup", | ||||
|       location, | ||||
|       slot: this.registration.timeout === void 0 ? void 0 : { | ||||
|         timeout: this.registration.timeout, | ||||
|         elapsed: 0 | ||||
|       } | ||||
|     }; | ||||
|     this._teardownDescription = { ...this._setupDescription, phase: "teardown" }; | ||||
|   } | ||||
|   async setup(testInfo, runnable) { | ||||
|     this.runner.instanceForId.set(this.registration.id, this); | ||||
|     if (typeof this.registration.fn !== "function") { | ||||
|       this.value = this.registration.fn; | ||||
|       return; | ||||
|     } | ||||
|     await testInfo._runAsStep(this._stepInfo, async () => { | ||||
|       await testInfo._runWithTimeout({ ...runnable, fixture: this._setupDescription }, () => this._setupInternal(testInfo)); | ||||
|     }); | ||||
|   } | ||||
|   async _setupInternal(testInfo) { | ||||
|     const params = {}; | ||||
|     for (const name of this.registration.deps) { | ||||
|       const registration = this.runner.pool.resolve(name, this.registration); | ||||
|       const dep = this.runner.instanceForId.get(registration.id); | ||||
|       if (!dep) { | ||||
|         this.failed = true; | ||||
|         return; | ||||
|       } | ||||
|       dep._usages.add(this); | ||||
|       this._deps.add(dep); | ||||
|       params[name] = dep.value; | ||||
|       if (dep.failed) { | ||||
|         this.failed = true; | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|     let called = false; | ||||
|     const useFuncStarted = new import_utils.ManualPromise(); | ||||
|     const useFunc = async (value) => { | ||||
|       if (called) | ||||
|         throw new Error(`Cannot provide fixture value for the second time`); | ||||
|       called = true; | ||||
|       this.value = value; | ||||
|       this._useFuncFinished = new import_utils.ManualPromise(); | ||||
|       useFuncStarted.resolve(); | ||||
|       await this._useFuncFinished; | ||||
|     }; | ||||
|     const workerInfo = { config: testInfo.config, parallelIndex: testInfo.parallelIndex, workerIndex: testInfo.workerIndex, project: testInfo.project }; | ||||
|     const info = this.registration.scope === "worker" ? workerInfo : testInfo; | ||||
|     this._selfTeardownComplete = (async () => { | ||||
|       try { | ||||
|         await this.registration.fn(params, useFunc, info); | ||||
|       } catch (error) { | ||||
|         this.failed = true; | ||||
|         if (!useFuncStarted.isDone()) | ||||
|           useFuncStarted.reject(error); | ||||
|         else | ||||
|           throw error; | ||||
|       } | ||||
|     })(); | ||||
|     await useFuncStarted; | ||||
|   } | ||||
|   async teardown(testInfo, runnable) { | ||||
|     try { | ||||
|       const fixtureRunnable = { ...runnable, fixture: this._teardownDescription }; | ||||
|       if (!testInfo._timeoutManager.isTimeExhaustedFor(fixtureRunnable)) { | ||||
|         await testInfo._runAsStep(this._stepInfo, async () => { | ||||
|           await testInfo._runWithTimeout(fixtureRunnable, () => this._teardownInternal()); | ||||
|         }); | ||||
|       } | ||||
|     } finally { | ||||
|       for (const dep of this._deps) | ||||
|         dep._usages.delete(this); | ||||
|       this.runner.instanceForId.delete(this.registration.id); | ||||
|     } | ||||
|   } | ||||
|   async _teardownInternal() { | ||||
|     if (typeof this.registration.fn !== "function") | ||||
|       return; | ||||
|     if (this._usages.size !== 0) { | ||||
|       console.error("Internal error: fixture integrity at", this._teardownDescription.title); | ||||
|       this._usages.clear(); | ||||
|     } | ||||
|     if (this._useFuncFinished) { | ||||
|       this._useFuncFinished.resolve(); | ||||
|       this._useFuncFinished = void 0; | ||||
|       await this._selfTeardownComplete; | ||||
|     } | ||||
|   } | ||||
|   _collectFixturesInTeardownOrder(scope, collector) { | ||||
|     if (this.registration.scope !== scope) | ||||
|       return; | ||||
|     for (const fixture of this._usages) | ||||
|       fixture._collectFixturesInTeardownOrder(scope, collector); | ||||
|     collector.add(this); | ||||
|   } | ||||
| } | ||||
| class FixtureRunner { | ||||
|   constructor() { | ||||
|     this.testScopeClean = true; | ||||
|     this.instanceForId = /* @__PURE__ */ new Map(); | ||||
|   } | ||||
|   setPool(pool) { | ||||
|     if (!this.testScopeClean) | ||||
|       throw new Error("Did not teardown test scope"); | ||||
|     if (this.pool && pool.digest !== this.pool.digest) { | ||||
|       throw new Error([ | ||||
|         `Playwright detected inconsistent test.use() options.`, | ||||
|         `Most common mistakes that lead to this issue:`, | ||||
|         `  - Calling test.use() outside of the test file, for example in a common helper.`, | ||||
|         `  - One test file imports from another test file.` | ||||
|       ].join("\n")); | ||||
|     } | ||||
|     this.pool = pool; | ||||
|   } | ||||
|   _collectFixturesInSetupOrder(registration, collector) { | ||||
|     if (collector.has(registration)) | ||||
|       return; | ||||
|     for (const name of registration.deps) { | ||||
|       const dep = this.pool.resolve(name, registration); | ||||
|       this._collectFixturesInSetupOrder(dep, collector); | ||||
|     } | ||||
|     collector.add(registration); | ||||
|   } | ||||
|   async teardownScope(scope, testInfo, runnable) { | ||||
|     const fixtures = Array.from(this.instanceForId.values()).reverse(); | ||||
|     const collector = /* @__PURE__ */ new Set(); | ||||
|     for (const fixture of fixtures) | ||||
|       fixture._collectFixturesInTeardownOrder(scope, collector); | ||||
|     let firstError; | ||||
|     for (const fixture of collector) { | ||||
|       try { | ||||
|         await fixture.teardown(testInfo, runnable); | ||||
|       } catch (error) { | ||||
|         firstError = firstError ?? error; | ||||
|       } | ||||
|     } | ||||
|     if (scope === "test") | ||||
|       this.testScopeClean = true; | ||||
|     if (firstError) | ||||
|       throw firstError; | ||||
|   } | ||||
|   async resolveParametersForFunction(fn, testInfo, autoFixtures, runnable) { | ||||
|     const collector = /* @__PURE__ */ new Set(); | ||||
|     const auto = []; | ||||
|     for (const registration of this.pool.autoFixtures()) { | ||||
|       let shouldRun = true; | ||||
|       if (autoFixtures === "all-hooks-only") | ||||
|         shouldRun = registration.scope === "worker" || registration.auto === "all-hooks-included"; | ||||
|       else if (autoFixtures === "worker") | ||||
|         shouldRun = registration.scope === "worker"; | ||||
|       if (shouldRun) | ||||
|         auto.push(registration); | ||||
|     } | ||||
|     auto.sort((r1, r2) => (r1.scope === "worker" ? 0 : 1) - (r2.scope === "worker" ? 0 : 1)); | ||||
|     for (const registration of auto) | ||||
|       this._collectFixturesInSetupOrder(registration, collector); | ||||
|     const names = getRequiredFixtureNames(fn); | ||||
|     for (const name of names) | ||||
|       this._collectFixturesInSetupOrder(this.pool.resolve(name), collector); | ||||
|     for (const registration of collector) | ||||
|       await this._setupFixtureForRegistration(registration, testInfo, runnable); | ||||
|     const params = {}; | ||||
|     for (const name of names) { | ||||
|       const registration = this.pool.resolve(name); | ||||
|       const fixture = this.instanceForId.get(registration.id); | ||||
|       if (!fixture || fixture.failed) | ||||
|         return null; | ||||
|       params[name] = fixture.value; | ||||
|     } | ||||
|     return params; | ||||
|   } | ||||
|   async resolveParametersAndRunFunction(fn, testInfo, autoFixtures, runnable) { | ||||
|     const params = await this.resolveParametersForFunction(fn, testInfo, autoFixtures, runnable); | ||||
|     if (params === null) { | ||||
|       return null; | ||||
|     } | ||||
|     await testInfo._runWithTimeout(runnable, () => fn(params, testInfo)); | ||||
|   } | ||||
|   async _setupFixtureForRegistration(registration, testInfo, runnable) { | ||||
|     if (registration.scope === "test") | ||||
|       this.testScopeClean = false; | ||||
|     let fixture = this.instanceForId.get(registration.id); | ||||
|     if (fixture) | ||||
|       return fixture; | ||||
|     fixture = new Fixture(this, registration); | ||||
|     await fixture.setup(testInfo, runnable); | ||||
|     return fixture; | ||||
|   } | ||||
|   dependsOnWorkerFixturesOnly(fn, location) { | ||||
|     const names = getRequiredFixtureNames(fn, location); | ||||
|     for (const name of names) { | ||||
|       const registration = this.pool.resolve(name); | ||||
|       if (registration.scope !== "worker") | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| } | ||||
| function getRequiredFixtureNames(fn, location) { | ||||
|   return (0, import_fixtures.fixtureParameterNames)(fn, location ?? { file: "<unknown>", line: 1, column: 1 }, (e) => { | ||||
|     throw new Error(`${(0, import_util.formatLocation)(e.location)}: ${e.message}`); | ||||
|   }); | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   FixtureRunner | ||||
| }); | ||||
							
								
								
									
										517
									
								
								frontend/e2e/node_modules/playwright/lib/worker/testInfo.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										517
									
								
								frontend/e2e/node_modules/playwright/lib/worker/testInfo.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,517 @@ | ||||
| "use strict"; | ||||
| var __create = Object.create; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __getProtoOf = Object.getPrototypeOf; | ||||
| 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||||
|   // If the importer is in node compatibility mode or this is not an ESM | ||||
|   // file that has been converted to a CommonJS file using a Babel- | ||||
|   // compatible transform (i.e. "__esModule" has not been set), then set | ||||
|   // "default" to the CommonJS "module.exports" for node compatibility. | ||||
|   isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||||
|   mod | ||||
| )); | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var testInfo_exports = {}; | ||||
| __export(testInfo_exports, { | ||||
|   StepSkipError: () => StepSkipError, | ||||
|   TestInfoImpl: () => TestInfoImpl, | ||||
|   TestSkipError: () => TestSkipError, | ||||
|   TestStepInfoImpl: () => TestStepInfoImpl | ||||
| }); | ||||
| module.exports = __toCommonJS(testInfo_exports); | ||||
| var import_fs = __toESM(require("fs")); | ||||
| var import_path = __toESM(require("path")); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_timeoutManager = require("./timeoutManager"); | ||||
| var import_util = require("../util"); | ||||
| var import_testTracing = require("./testTracing"); | ||||
| var import_util2 = require("./util"); | ||||
| var import_transform = require("../transform/transform"); | ||||
| class TestInfoImpl { | ||||
|   constructor(configInternal, projectInternal, workerParams, test, retry, onStepBegin, onStepRecoverFromError, onStepEnd, onAttach) { | ||||
|     this._snapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} }; | ||||
|     this._ariaSnapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} }; | ||||
|     this._wasInterrupted = false; | ||||
|     this._lastStepId = 0; | ||||
|     this._steps = []; | ||||
|     this._stepMap = /* @__PURE__ */ new Map(); | ||||
|     this._hasNonRetriableError = false; | ||||
|     this._hasUnhandledError = false; | ||||
|     this._allowSkips = false; | ||||
|     this.duration = 0; | ||||
|     this.annotations = []; | ||||
|     this.attachments = []; | ||||
|     this.status = "passed"; | ||||
|     this.snapshotSuffix = ""; | ||||
|     this.errors = []; | ||||
|     this.testId = test?.id ?? ""; | ||||
|     this._onStepBegin = onStepBegin; | ||||
|     this._onStepRecoverFromError = onStepRecoverFromError; | ||||
|     this._onStepEnd = onStepEnd; | ||||
|     this._onAttach = onAttach; | ||||
|     this._startTime = (0, import_utils.monotonicTime)(); | ||||
|     this._startWallTime = Date.now(); | ||||
|     this._requireFile = test?._requireFile ?? ""; | ||||
|     this._uniqueSymbol = Symbol("testInfoUniqueSymbol"); | ||||
|     this.repeatEachIndex = workerParams.repeatEachIndex; | ||||
|     this.retry = retry; | ||||
|     this.workerIndex = workerParams.workerIndex; | ||||
|     this.parallelIndex = workerParams.parallelIndex; | ||||
|     this._projectInternal = projectInternal; | ||||
|     this.project = projectInternal.project; | ||||
|     this._configInternal = configInternal; | ||||
|     this.config = configInternal.config; | ||||
|     this.title = test?.title ?? ""; | ||||
|     this.titlePath = test?.titlePath() ?? []; | ||||
|     this.file = test?.location.file ?? ""; | ||||
|     this.line = test?.location.line ?? 0; | ||||
|     this.column = test?.location.column ?? 0; | ||||
|     this.tags = test?.tags ?? []; | ||||
|     this.fn = test?.fn ?? (() => { | ||||
|     }); | ||||
|     this.expectedStatus = test?.expectedStatus ?? "skipped"; | ||||
|     this._recoverFromStepErrorResults = workerParams.recoverFromStepErrors ? /* @__PURE__ */ new Map() : void 0; | ||||
|     this._timeoutManager = new import_timeoutManager.TimeoutManager(this.project.timeout); | ||||
|     if (configInternal.configCLIOverrides.debug) | ||||
|       this._setDebugMode(); | ||||
|     this.outputDir = (() => { | ||||
|       const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, "")); | ||||
|       const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === "win32" ? new RegExp("\\\\", "g") : new RegExp("/", "g"), "-"); | ||||
|       const fullTitleWithoutSpec = this.titlePath.slice(1).join(" "); | ||||
|       let testOutputDir = (0, import_util.trimLongString)(sanitizedRelativePath + "-" + (0, import_utils.sanitizeForFilePath)(fullTitleWithoutSpec), import_util.windowsFilesystemFriendlyLength); | ||||
|       if (projectInternal.id) | ||||
|         testOutputDir += "-" + (0, import_utils.sanitizeForFilePath)(projectInternal.id); | ||||
|       if (this.retry) | ||||
|         testOutputDir += "-retry" + this.retry; | ||||
|       if (this.repeatEachIndex) | ||||
|         testOutputDir += "-repeat" + this.repeatEachIndex; | ||||
|       return import_path.default.join(this.project.outputDir, testOutputDir); | ||||
|     })(); | ||||
|     this.snapshotDir = (() => { | ||||
|       const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile); | ||||
|       return import_path.default.join(this.project.snapshotDir, relativeTestFilePath + "-snapshots"); | ||||
|     })(); | ||||
|     this._attachmentsPush = this.attachments.push.bind(this.attachments); | ||||
|     this.attachments.push = (...attachments) => { | ||||
|       for (const a of attachments) | ||||
|         this._attach(a, this._parentStep()?.stepId); | ||||
|       return this.attachments.length; | ||||
|     }; | ||||
|     this._tracing = new import_testTracing.TestTracing(this, workerParams.artifactsDir); | ||||
|     this.skip = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("skip", location, args)); | ||||
|     this.fixme = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("fixme", location, args)); | ||||
|     this.fail = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("fail", location, args)); | ||||
|     this.slow = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("slow", location, args)); | ||||
|   } | ||||
|   get error() { | ||||
|     return this.errors[0]; | ||||
|   } | ||||
|   set error(e) { | ||||
|     if (e === void 0) | ||||
|       throw new Error("Cannot assign testInfo.error undefined value!"); | ||||
|     this.errors[0] = e; | ||||
|   } | ||||
|   get timeout() { | ||||
|     return this._timeoutManager.defaultSlot().timeout; | ||||
|   } | ||||
|   set timeout(timeout) { | ||||
|   } | ||||
|   _deadlineForMatcher(timeout) { | ||||
|     const startTime = (0, import_utils.monotonicTime)(); | ||||
|     const matcherDeadline = timeout ? startTime + timeout : import_timeoutManager.kMaxDeadline; | ||||
|     const testDeadline = this._timeoutManager.currentSlotDeadline() - 250; | ||||
|     const matcherMessage = `Timeout ${timeout}ms exceeded while waiting on the predicate`; | ||||
|     const testMessage = `Test timeout of ${this.timeout}ms exceeded`; | ||||
|     return { deadline: Math.min(testDeadline, matcherDeadline), timeoutMessage: testDeadline < matcherDeadline ? testMessage : matcherMessage }; | ||||
|   } | ||||
|   static _defaultDeadlineForMatcher(timeout) { | ||||
|     return { deadline: timeout ? (0, import_utils.monotonicTime)() + timeout : 0, timeoutMessage: `Timeout ${timeout}ms exceeded while waiting on the predicate` }; | ||||
|   } | ||||
|   _modifier(type, location, modifierArgs) { | ||||
|     if (typeof modifierArgs[1] === "function") { | ||||
|       throw new Error([ | ||||
|         "It looks like you are calling test.skip() inside the test and pass a callback.", | ||||
|         "Pass a condition instead and optional description instead:", | ||||
|         `test('my test', async ({ page, isMobile }) => {`, | ||||
|         `  test.skip(isMobile, 'This test is not applicable on mobile');`, | ||||
|         `});` | ||||
|       ].join("\n")); | ||||
|     } | ||||
|     if (modifierArgs.length >= 1 && !modifierArgs[0]) | ||||
|       return; | ||||
|     const description = modifierArgs[1]; | ||||
|     this.annotations.push({ type, description, location }); | ||||
|     if (type === "slow") { | ||||
|       this._timeoutManager.slow(); | ||||
|     } else if (type === "skip" || type === "fixme") { | ||||
|       this.expectedStatus = "skipped"; | ||||
|       throw new TestSkipError("Test is skipped: " + (description || "")); | ||||
|     } else if (type === "fail") { | ||||
|       if (this.expectedStatus !== "skipped") | ||||
|         this.expectedStatus = "failed"; | ||||
|     } | ||||
|   } | ||||
|   _findLastPredefinedStep(steps) { | ||||
|     for (let i = steps.length - 1; i >= 0; i--) { | ||||
|       const child = this._findLastPredefinedStep(steps[i].steps); | ||||
|       if (child) | ||||
|         return child; | ||||
|       if ((steps[i].category === "hook" || steps[i].category === "fixture") && !steps[i].endWallTime) | ||||
|         return steps[i]; | ||||
|     } | ||||
|   } | ||||
|   _parentStep() { | ||||
|     return (0, import_utils.currentZone)().data("stepZone") ?? this._findLastPredefinedStep(this._steps); | ||||
|   } | ||||
|   _addStep(data, parentStep) { | ||||
|     const stepId = `${data.category}@${++this._lastStepId}`; | ||||
|     if (data.category === "hook" || data.category === "fixture") { | ||||
|       parentStep = this._findLastPredefinedStep(this._steps); | ||||
|     } else { | ||||
|       if (!parentStep) | ||||
|         parentStep = this._parentStep(); | ||||
|     } | ||||
|     const filteredStack = (0, import_util.filteredStackTrace)((0, import_utils.captureRawStack)()); | ||||
|     let boxedStack = parentStep?.boxedStack; | ||||
|     let location = data.location; | ||||
|     if (!boxedStack && data.box) { | ||||
|       boxedStack = filteredStack.slice(1); | ||||
|       location = location || boxedStack[0]; | ||||
|     } | ||||
|     location = location || filteredStack[0]; | ||||
|     const step = { | ||||
|       ...data, | ||||
|       stepId, | ||||
|       group: parentStep?.group ?? data.group, | ||||
|       boxedStack, | ||||
|       location, | ||||
|       steps: [], | ||||
|       attachmentIndices: [], | ||||
|       info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info), | ||||
|       recoverFromStepError: async (error) => { | ||||
|         if (!this._recoverFromStepErrorResults) | ||||
|           return { stepId, status: "failed" }; | ||||
|         const payload = { | ||||
|           testId: this.testId, | ||||
|           stepId, | ||||
|           error: (0, import_util.serializeError)(error) | ||||
|         }; | ||||
|         this._onStepRecoverFromError(payload); | ||||
|         const recoveryPromise = new import_utils.ManualPromise(); | ||||
|         this._recoverFromStepErrorResults.set(stepId, recoveryPromise); | ||||
|         const recoveryResult = await recoveryPromise; | ||||
|         if (recoveryResult.stepId !== stepId) | ||||
|           return { stepId, status: "failed" }; | ||||
|         return recoveryResult; | ||||
|       }, | ||||
|       complete: (result) => { | ||||
|         if (step.endWallTime) | ||||
|           return; | ||||
|         step.endWallTime = Date.now(); | ||||
|         if (result.error) { | ||||
|           if (typeof result.error === "object" && !result.error?.[stepSymbol]) | ||||
|             result.error[stepSymbol] = step; | ||||
|           const error = (0, import_util2.testInfoError)(result.error); | ||||
|           if (step.boxedStack) | ||||
|             error.stack = `${error.message} | ||||
| ${(0, import_utils.stringifyStackFrames)(step.boxedStack).join("\n")}`; | ||||
|           step.error = error; | ||||
|         } | ||||
|         if (!step.error) { | ||||
|           for (const childStep of step.steps) { | ||||
|             if (childStep.error && childStep.infectParentStepsWithError) { | ||||
|               step.error = childStep.error; | ||||
|               step.infectParentStepsWithError = true; | ||||
|               break; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         if (!step.group) { | ||||
|           const payload = { | ||||
|             testId: this.testId, | ||||
|             stepId, | ||||
|             wallTime: step.endWallTime, | ||||
|             error: step.error, | ||||
|             suggestedRebaseline: result.suggestedRebaseline, | ||||
|             annotations: step.info.annotations | ||||
|           }; | ||||
|           this._onStepEnd(payload); | ||||
|         } | ||||
|         if (step.group !== "internal") { | ||||
|           const errorForTrace = step.error ? { name: "", message: step.error.message || "", stack: step.error.stack } : void 0; | ||||
|           const attachments = step.attachmentIndices.map((i) => this.attachments[i]); | ||||
|           this._tracing.appendAfterActionForStep(stepId, errorForTrace, attachments, step.info.annotations); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|     const parentStepList = parentStep ? parentStep.steps : this._steps; | ||||
|     parentStepList.push(step); | ||||
|     this._stepMap.set(stepId, step); | ||||
|     if (!step.group) { | ||||
|       const payload = { | ||||
|         testId: this.testId, | ||||
|         stepId, | ||||
|         parentStepId: parentStep ? parentStep.stepId : void 0, | ||||
|         title: step.title, | ||||
|         category: step.category, | ||||
|         wallTime: Date.now(), | ||||
|         location: step.location | ||||
|       }; | ||||
|       this._onStepBegin(payload); | ||||
|     } | ||||
|     if (step.group !== "internal") { | ||||
|       this._tracing.appendBeforeActionForStep({ | ||||
|         stepId, | ||||
|         parentId: parentStep?.stepId, | ||||
|         title: step.title, | ||||
|         category: step.category, | ||||
|         params: step.params, | ||||
|         stack: step.location ? [step.location] : [], | ||||
|         group: step.group | ||||
|       }); | ||||
|     } | ||||
|     return step; | ||||
|   } | ||||
|   resumeAfterStepError(result) { | ||||
|     const recoveryPromise = this._recoverFromStepErrorResults?.get(result.stepId); | ||||
|     if (recoveryPromise) | ||||
|       recoveryPromise.resolve(result); | ||||
|   } | ||||
|   _interrupt() { | ||||
|     this._wasInterrupted = true; | ||||
|     this._timeoutManager.interrupt(); | ||||
|     if (this.status === "passed") | ||||
|       this.status = "interrupted"; | ||||
|   } | ||||
|   _failWithError(error) { | ||||
|     if (this.status === "passed" || this.status === "skipped") | ||||
|       this.status = error instanceof import_timeoutManager.TimeoutManagerError ? "timedOut" : "failed"; | ||||
|     const serialized = (0, import_util2.testInfoError)(error); | ||||
|     const step = typeof error === "object" ? error?.[stepSymbol] : void 0; | ||||
|     if (step && step.boxedStack) | ||||
|       serialized.stack = `${error.name}: ${error.message} | ||||
| ${(0, import_utils.stringifyStackFrames)(step.boxedStack).join("\n")}`; | ||||
|     this.errors.push(serialized); | ||||
|     this._tracing.appendForError(serialized); | ||||
|   } | ||||
|   async _runAsStep(stepInfo, cb) { | ||||
|     const step = this._addStep(stepInfo); | ||||
|     try { | ||||
|       await cb(); | ||||
|       step.complete({}); | ||||
|     } catch (error) { | ||||
|       step.complete({ error }); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|   async _runWithTimeout(runnable, cb) { | ||||
|     try { | ||||
|       await this._timeoutManager.withRunnable(runnable, async () => { | ||||
|         try { | ||||
|           await cb(); | ||||
|         } catch (e) { | ||||
|           if (this._allowSkips && e instanceof TestSkipError) { | ||||
|             if (this.status === "passed") | ||||
|               this.status = "skipped"; | ||||
|           } else { | ||||
|             this._failWithError(e); | ||||
|           } | ||||
|           throw e; | ||||
|         } | ||||
|       }); | ||||
|     } catch (error) { | ||||
|       if (!this._wasInterrupted && error instanceof import_timeoutManager.TimeoutManagerError) | ||||
|         this._failWithError(error); | ||||
|       throw error; | ||||
|     } | ||||
|   } | ||||
|   _isFailure() { | ||||
|     return this.status !== "skipped" && this.status !== this.expectedStatus; | ||||
|   } | ||||
|   _currentHookType() { | ||||
|     const type = this._timeoutManager.currentSlotType(); | ||||
|     return ["beforeAll", "afterAll", "beforeEach", "afterEach"].includes(type) ? type : void 0; | ||||
|   } | ||||
|   _setDebugMode() { | ||||
|     this._timeoutManager.setIgnoreTimeouts(); | ||||
|   } | ||||
|   // ------------ TestInfo methods ------------ | ||||
|   async attach(name, options = {}) { | ||||
|     const step = this._addStep({ | ||||
|       title: `Attach ${(0, import_utils.escapeWithQuotes)(name, '"')}`, | ||||
|       category: "test.attach" | ||||
|     }); | ||||
|     this._attach(await (0, import_util.normalizeAndSaveAttachment)(this.outputPath(), name, options), step.stepId); | ||||
|     step.complete({}); | ||||
|   } | ||||
|   _attach(attachment, stepId) { | ||||
|     const index = this._attachmentsPush(attachment) - 1; | ||||
|     if (stepId) { | ||||
|       this._stepMap.get(stepId).attachmentIndices.push(index); | ||||
|     } else { | ||||
|       const stepId2 = `attach@${(0, import_utils.createGuid)()}`; | ||||
|       this._tracing.appendBeforeActionForStep({ stepId: stepId2, title: `Attach ${(0, import_utils.escapeWithQuotes)(attachment.name, '"')}`, category: "test.attach", stack: [] }); | ||||
|       this._tracing.appendAfterActionForStep(stepId2, void 0, [attachment]); | ||||
|     } | ||||
|     this._onAttach({ | ||||
|       testId: this.testId, | ||||
|       name: attachment.name, | ||||
|       contentType: attachment.contentType, | ||||
|       path: attachment.path, | ||||
|       body: attachment.body?.toString("base64"), | ||||
|       stepId | ||||
|     }); | ||||
|   } | ||||
|   outputPath(...pathSegments) { | ||||
|     const outputPath = this._getOutputPath(...pathSegments); | ||||
|     import_fs.default.mkdirSync(this.outputDir, { recursive: true }); | ||||
|     return outputPath; | ||||
|   } | ||||
|   _getOutputPath(...pathSegments) { | ||||
|     const joinedPath = import_path.default.join(...pathSegments); | ||||
|     const outputPath = (0, import_util.getContainedPath)(this.outputDir, joinedPath); | ||||
|     if (outputPath) | ||||
|       return outputPath; | ||||
|     throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path. | ||||
|  | ||||
| 	outputPath: ${joinedPath}`); | ||||
|   } | ||||
|   _fsSanitizedTestName() { | ||||
|     const fullTitleWithoutSpec = this.titlePath.slice(1).join(" "); | ||||
|     return (0, import_utils.sanitizeForFilePath)((0, import_util.trimLongString)(fullTitleWithoutSpec)); | ||||
|   } | ||||
|   _resolveSnapshotPaths(kind, name, updateSnapshotIndex, anonymousExtension) { | ||||
|     const snapshotNames = kind === "aria" ? this._ariaSnapshotNames : this._snapshotNames; | ||||
|     const defaultExtensions = { "aria": ".aria.yml", "screenshot": ".png", "snapshot": ".txt" }; | ||||
|     const ariaAwareExtname = (filePath) => kind === "aria" && filePath.endsWith(".aria.yml") ? ".aria.yml" : import_path.default.extname(filePath); | ||||
|     let subPath; | ||||
|     let ext; | ||||
|     let relativeOutputPath; | ||||
|     if (!name) { | ||||
|       const index = snapshotNames.lastAnonymousSnapshotIndex + 1; | ||||
|       if (updateSnapshotIndex === "updateSnapshotIndex") | ||||
|         snapshotNames.lastAnonymousSnapshotIndex = index; | ||||
|       const fullTitleWithoutSpec = [...this.titlePath.slice(1), index].join(" "); | ||||
|       ext = anonymousExtension ?? defaultExtensions[kind]; | ||||
|       subPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(fullTitleWithoutSpec) + ext, ext); | ||||
|       relativeOutputPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(fullTitleWithoutSpec, import_util.windowsFilesystemFriendlyLength) + ext, ext); | ||||
|     } else { | ||||
|       if (Array.isArray(name)) { | ||||
|         subPath = import_path.default.join(...name); | ||||
|         relativeOutputPath = import_path.default.join(...name); | ||||
|         ext = ariaAwareExtname(subPath); | ||||
|       } else { | ||||
|         ext = ariaAwareExtname(name); | ||||
|         subPath = (0, import_util.sanitizeFilePathBeforeExtension)(name, ext); | ||||
|         relativeOutputPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(name, import_util.windowsFilesystemFriendlyLength), ext); | ||||
|       } | ||||
|       const index = (snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] || 0) + 1; | ||||
|       if (updateSnapshotIndex === "updateSnapshotIndex") | ||||
|         snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] = index; | ||||
|       if (index > 1) | ||||
|         relativeOutputPath = (0, import_util.addSuffixToFilePath)(relativeOutputPath, `-${index - 1}`); | ||||
|     } | ||||
|     const absoluteSnapshotPath = this._applyPathTemplate(kind, subPath, ext); | ||||
|     return { absoluteSnapshotPath, relativeOutputPath }; | ||||
|   } | ||||
|   _applyPathTemplate(kind, relativePath, ext) { | ||||
|     const legacyTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}"; | ||||
|     let template; | ||||
|     if (kind === "screenshot") { | ||||
|       template = this._projectInternal.expect?.toHaveScreenshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || legacyTemplate; | ||||
|     } else if (kind === "aria") { | ||||
|       const ariaDefaultTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}"; | ||||
|       template = this._projectInternal.expect?.toMatchAriaSnapshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || ariaDefaultTemplate; | ||||
|     } else { | ||||
|       template = this._projectInternal.snapshotPathTemplate || legacyTemplate; | ||||
|     } | ||||
|     const dir = import_path.default.dirname(relativePath); | ||||
|     const name = import_path.default.basename(relativePath, ext); | ||||
|     const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile); | ||||
|     const parsedRelativeTestFilePath = import_path.default.parse(relativeTestFilePath); | ||||
|     const projectNamePathSegment = (0, import_utils.sanitizeForFilePath)(this.project.name); | ||||
|     const snapshotPath = template.replace(/\{(.)?testDir\}/g, "$1" + this.project.testDir).replace(/\{(.)?snapshotDir\}/g, "$1" + this.project.snapshotDir).replace(/\{(.)?snapshotSuffix\}/g, this.snapshotSuffix ? "$1" + this.snapshotSuffix : "").replace(/\{(.)?testFileDir\}/g, "$1" + parsedRelativeTestFilePath.dir).replace(/\{(.)?platform\}/g, "$1" + process.platform).replace(/\{(.)?projectName\}/g, projectNamePathSegment ? "$1" + projectNamePathSegment : "").replace(/\{(.)?testName\}/g, "$1" + this._fsSanitizedTestName()).replace(/\{(.)?testFileName\}/g, "$1" + parsedRelativeTestFilePath.base).replace(/\{(.)?testFilePath\}/g, "$1" + relativeTestFilePath).replace(/\{(.)?arg\}/g, "$1" + import_path.default.join(dir, name)).replace(/\{(.)?ext\}/g, ext ? "$1" + ext : ""); | ||||
|     return import_path.default.normalize(import_path.default.resolve(this._configInternal.configDir, snapshotPath)); | ||||
|   } | ||||
|   snapshotPath(...args) { | ||||
|     let name = args; | ||||
|     let kind = "snapshot"; | ||||
|     const options = args[args.length - 1]; | ||||
|     if (options && typeof options === "object") { | ||||
|       kind = options.kind ?? kind; | ||||
|       name = args.slice(0, -1); | ||||
|     } | ||||
|     if (!["snapshot", "screenshot", "aria"].includes(kind)) | ||||
|       throw new Error(`testInfo.snapshotPath: unknown kind "${kind}", must be one of "snapshot", "screenshot" or "aria"`); | ||||
|     return this._resolveSnapshotPaths(kind, name.length <= 1 ? name[0] : name, "dontUpdateSnapshotIndex").absoluteSnapshotPath; | ||||
|   } | ||||
|   setTimeout(timeout) { | ||||
|     this._timeoutManager.setTimeout(timeout); | ||||
|   } | ||||
| } | ||||
| class TestStepInfoImpl { | ||||
|   constructor(testInfo, stepId, title, parentStep) { | ||||
|     this.annotations = []; | ||||
|     this._testInfo = testInfo; | ||||
|     this._stepId = stepId; | ||||
|     this._title = title; | ||||
|     this._parentStep = parentStep; | ||||
|     this.skip = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => { | ||||
|       if (args.length > 0 && !args[0]) | ||||
|         return; | ||||
|       const description = args[1]; | ||||
|       this.annotations.push({ type: "skip", description, location }); | ||||
|       throw new StepSkipError(description); | ||||
|     }); | ||||
|   } | ||||
|   async _runStepBody(skip, body, location) { | ||||
|     if (skip) { | ||||
|       this.annotations.push({ type: "skip", location }); | ||||
|       return void 0; | ||||
|     } | ||||
|     try { | ||||
|       return await body(this); | ||||
|     } catch (e) { | ||||
|       if (e instanceof StepSkipError) | ||||
|         return void 0; | ||||
|       throw e; | ||||
|     } | ||||
|   } | ||||
|   _attachToStep(attachment) { | ||||
|     this._testInfo._attach(attachment, this._stepId); | ||||
|   } | ||||
|   async attach(name, options) { | ||||
|     this._attachToStep(await (0, import_util.normalizeAndSaveAttachment)(this._testInfo.outputPath(), name, options)); | ||||
|   } | ||||
|   get titlePath() { | ||||
|     const parent = this._parentStep ?? this._testInfo; | ||||
|     return [...parent.titlePath, this._title]; | ||||
|   } | ||||
| } | ||||
| class TestSkipError extends Error { | ||||
| } | ||||
| class StepSkipError extends Error { | ||||
| } | ||||
| const stepSymbol = Symbol("step"); | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   StepSkipError, | ||||
|   TestInfoImpl, | ||||
|   TestSkipError, | ||||
|   TestStepInfoImpl | ||||
| }); | ||||
							
								
								
									
										344
									
								
								frontend/e2e/node_modules/playwright/lib/worker/testTracing.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								frontend/e2e/node_modules/playwright/lib/worker/testTracing.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,344 @@ | ||||
| "use strict"; | ||||
| var __create = Object.create; | ||||
| var __defProp = Object.defineProperty; | ||||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||||
| var __getProtoOf = Object.getPrototypeOf; | ||||
| 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||||
|   // If the importer is in node compatibility mode or this is not an ESM | ||||
|   // file that has been converted to a CommonJS file using a Babel- | ||||
|   // compatible transform (i.e. "__esModule" has not been set), then set | ||||
|   // "default" to the CommonJS "module.exports" for node compatibility. | ||||
|   isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||||
|   mod | ||||
| )); | ||||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||||
| var testTracing_exports = {}; | ||||
| __export(testTracing_exports, { | ||||
|   TestTracing: () => TestTracing, | ||||
|   testTraceEntryName: () => testTraceEntryName | ||||
| }); | ||||
| module.exports = __toCommonJS(testTracing_exports); | ||||
| var import_fs = __toESM(require("fs")); | ||||
| var import_path = __toESM(require("path")); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_zipBundle = require("playwright-core/lib/zipBundle"); | ||||
| var import_util = require("../util"); | ||||
| const testTraceEntryName = "test.trace"; | ||||
| const version = 8; | ||||
| let traceOrdinal = 0; | ||||
| class TestTracing { | ||||
|   constructor(testInfo, artifactsDir) { | ||||
|     this._traceEvents = []; | ||||
|     this._temporaryTraceFiles = []; | ||||
|     this._didFinishTestFunctionAndAfterEachHooks = false; | ||||
|     this._testInfo = testInfo; | ||||
|     this._artifactsDir = artifactsDir; | ||||
|     this._tracesDir = import_path.default.join(this._artifactsDir, "traces"); | ||||
|     this._contextCreatedEvent = { | ||||
|       version, | ||||
|       type: "context-options", | ||||
|       origin: "testRunner", | ||||
|       browserName: "", | ||||
|       options: {}, | ||||
|       platform: process.platform, | ||||
|       wallTime: Date.now(), | ||||
|       monotonicTime: (0, import_utils.monotonicTime)(), | ||||
|       sdkLanguage: "javascript" | ||||
|     }; | ||||
|     this._appendTraceEvent(this._contextCreatedEvent); | ||||
|   } | ||||
|   _shouldCaptureTrace() { | ||||
|     if (this._options?.mode === "on") | ||||
|       return true; | ||||
|     if (this._options?.mode === "retain-on-failure") | ||||
|       return true; | ||||
|     if (this._options?.mode === "on-first-retry" && this._testInfo.retry === 1) | ||||
|       return true; | ||||
|     if (this._options?.mode === "on-all-retries" && this._testInfo.retry > 0) | ||||
|       return true; | ||||
|     if (this._options?.mode === "retain-on-first-failure" && this._testInfo.retry === 0) | ||||
|       return true; | ||||
|     return false; | ||||
|   } | ||||
|   async startIfNeeded(value) { | ||||
|     const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, _live: false, mode: "off" }; | ||||
|     if (!value) { | ||||
|       this._options = defaultTraceOptions; | ||||
|     } else if (typeof value === "string") { | ||||
|       this._options = { ...defaultTraceOptions, mode: value === "retry-with-trace" ? "on-first-retry" : value }; | ||||
|     } else { | ||||
|       const mode = value.mode || "off"; | ||||
|       this._options = { ...defaultTraceOptions, ...value, mode: mode === "retry-with-trace" ? "on-first-retry" : mode }; | ||||
|     } | ||||
|     if (!this._shouldCaptureTrace()) { | ||||
|       this._options = void 0; | ||||
|       return; | ||||
|     } | ||||
|     if (!this._liveTraceFile && this._options._live) { | ||||
|       this._liveTraceFile = { file: import_path.default.join(this._tracesDir, `${this._testInfo.testId}-test.trace`), fs: new import_utils.SerializedFS() }; | ||||
|       this._liveTraceFile.fs.mkdir(import_path.default.dirname(this._liveTraceFile.file)); | ||||
|       const data = this._traceEvents.map((e) => JSON.stringify(e)).join("\n") + "\n"; | ||||
|       this._liveTraceFile.fs.writeFile(this._liveTraceFile.file, data); | ||||
|     } | ||||
|   } | ||||
|   didFinishTestFunctionAndAfterEachHooks() { | ||||
|     this._didFinishTestFunctionAndAfterEachHooks = true; | ||||
|   } | ||||
|   artifactsDir() { | ||||
|     return this._artifactsDir; | ||||
|   } | ||||
|   tracesDir() { | ||||
|     return this._tracesDir; | ||||
|   } | ||||
|   traceTitle() { | ||||
|     return [import_path.default.relative(this._testInfo.project.testDir, this._testInfo.file) + ":" + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(" \u203A "); | ||||
|   } | ||||
|   generateNextTraceRecordingName() { | ||||
|     const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : ""; | ||||
|     ++traceOrdinal; | ||||
|     const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : ""; | ||||
|     return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`; | ||||
|   } | ||||
|   _generateNextTraceRecordingPath() { | ||||
|     const file = import_path.default.join(this._artifactsDir, (0, import_utils.createGuid)() + ".zip"); | ||||
|     this._temporaryTraceFiles.push(file); | ||||
|     return file; | ||||
|   } | ||||
|   traceOptions() { | ||||
|     return this._options; | ||||
|   } | ||||
|   maybeGenerateNextTraceRecordingPath() { | ||||
|     if (this._didFinishTestFunctionAndAfterEachHooks && this._shouldAbandonTrace()) | ||||
|       return; | ||||
|     return this._generateNextTraceRecordingPath(); | ||||
|   } | ||||
|   _shouldAbandonTrace() { | ||||
|     if (!this._options) | ||||
|       return true; | ||||
|     const testFailed = this._testInfo.status !== this._testInfo.expectedStatus; | ||||
|     return !testFailed && (this._options.mode === "retain-on-failure" || this._options.mode === "retain-on-first-failure"); | ||||
|   } | ||||
|   async stopIfNeeded() { | ||||
|     if (!this._options) | ||||
|       return; | ||||
|     const error = await this._liveTraceFile?.fs.syncAndGetError(); | ||||
|     if (error) | ||||
|       throw error; | ||||
|     if (this._shouldAbandonTrace()) { | ||||
|       for (const file of this._temporaryTraceFiles) | ||||
|         await import_fs.default.promises.unlink(file).catch(() => { | ||||
|         }); | ||||
|       return; | ||||
|     } | ||||
|     const zipFile = new import_zipBundle.yazl.ZipFile(); | ||||
|     if (!this._options?.attachments) { | ||||
|       for (const event of this._traceEvents) { | ||||
|         if (event.type === "after") | ||||
|           delete event.attachments; | ||||
|       } | ||||
|     } | ||||
|     if (this._options?.sources) { | ||||
|       const sourceFiles = /* @__PURE__ */ new Set(); | ||||
|       for (const event of this._traceEvents) { | ||||
|         if (event.type === "before") { | ||||
|           for (const frame of event.stack || []) | ||||
|             sourceFiles.add(frame.file); | ||||
|         } | ||||
|       } | ||||
|       for (const sourceFile of sourceFiles) { | ||||
|         await import_fs.default.promises.readFile(sourceFile, "utf8").then((source) => { | ||||
|           zipFile.addBuffer(Buffer.from(source), "resources/src@" + (0, import_utils.calculateSha1)(sourceFile) + ".txt"); | ||||
|         }).catch(() => { | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|     const sha1s = /* @__PURE__ */ new Set(); | ||||
|     for (const event of this._traceEvents.filter((e) => e.type === "after")) { | ||||
|       for (const attachment of event.attachments || []) { | ||||
|         let contentPromise; | ||||
|         if (attachment.path) | ||||
|           contentPromise = import_fs.default.promises.readFile(attachment.path).catch(() => void 0); | ||||
|         else if (attachment.base64) | ||||
|           contentPromise = Promise.resolve(Buffer.from(attachment.base64, "base64")); | ||||
|         const content = await contentPromise; | ||||
|         if (content === void 0) | ||||
|           continue; | ||||
|         const sha1 = (0, import_utils.calculateSha1)(content); | ||||
|         attachment.sha1 = sha1; | ||||
|         delete attachment.path; | ||||
|         delete attachment.base64; | ||||
|         if (sha1s.has(sha1)) | ||||
|           continue; | ||||
|         sha1s.add(sha1); | ||||
|         zipFile.addBuffer(content, "resources/" + sha1); | ||||
|       } | ||||
|     } | ||||
|     const traceContent = Buffer.from(this._traceEvents.map((e) => JSON.stringify(e)).join("\n")); | ||||
|     zipFile.addBuffer(traceContent, testTraceEntryName); | ||||
|     await new Promise((f) => { | ||||
|       zipFile.end(void 0, () => { | ||||
|         zipFile.outputStream.pipe(import_fs.default.createWriteStream(this._generateNextTraceRecordingPath())).on("close", f); | ||||
|       }); | ||||
|     }); | ||||
|     const tracePath = this._testInfo.outputPath("trace.zip"); | ||||
|     await mergeTraceFiles(tracePath, this._temporaryTraceFiles); | ||||
|     this._testInfo.attachments.push({ name: "trace", path: tracePath, contentType: "application/zip" }); | ||||
|   } | ||||
|   appendForError(error) { | ||||
|     const rawStack = error.stack?.split("\n") || []; | ||||
|     const stack = rawStack ? (0, import_util.filteredStackTrace)(rawStack) : []; | ||||
|     this._appendTraceEvent({ | ||||
|       type: "error", | ||||
|       message: this._formatError(error), | ||||
|       stack | ||||
|     }); | ||||
|   } | ||||
|   _formatError(error) { | ||||
|     const parts = [error.message || String(error.value)]; | ||||
|     if (error.cause) | ||||
|       parts.push("[cause]: " + this._formatError(error.cause)); | ||||
|     return parts.join("\n"); | ||||
|   } | ||||
|   appendStdioToTrace(type, chunk) { | ||||
|     this._appendTraceEvent({ | ||||
|       type, | ||||
|       timestamp: (0, import_utils.monotonicTime)(), | ||||
|       text: typeof chunk === "string" ? chunk : void 0, | ||||
|       base64: typeof chunk === "string" ? void 0 : chunk.toString("base64") | ||||
|     }); | ||||
|   } | ||||
|   appendBeforeActionForStep(options) { | ||||
|     this._appendTraceEvent({ | ||||
|       type: "before", | ||||
|       callId: options.stepId, | ||||
|       stepId: options.stepId, | ||||
|       parentId: options.parentId, | ||||
|       startTime: (0, import_utils.monotonicTime)(), | ||||
|       class: "Test", | ||||
|       method: options.category, | ||||
|       title: options.title, | ||||
|       params: Object.fromEntries(Object.entries(options.params || {}).map(([name, value]) => [name, generatePreview(value)])), | ||||
|       stack: options.stack, | ||||
|       group: options.group | ||||
|     }); | ||||
|   } | ||||
|   appendAfterActionForStep(callId, error, attachments = [], annotations) { | ||||
|     this._appendTraceEvent({ | ||||
|       type: "after", | ||||
|       callId, | ||||
|       endTime: (0, import_utils.monotonicTime)(), | ||||
|       attachments: serializeAttachments(attachments), | ||||
|       annotations, | ||||
|       error | ||||
|     }); | ||||
|   } | ||||
|   _appendTraceEvent(event) { | ||||
|     this._traceEvents.push(event); | ||||
|     if (this._liveTraceFile) | ||||
|       this._liveTraceFile.fs.appendFile(this._liveTraceFile.file, JSON.stringify(event) + "\n", true); | ||||
|   } | ||||
| } | ||||
| function serializeAttachments(attachments) { | ||||
|   if (attachments.length === 0) | ||||
|     return void 0; | ||||
|   return attachments.filter((a) => a.name !== "trace").map((a) => { | ||||
|     return { | ||||
|       name: a.name, | ||||
|       contentType: a.contentType, | ||||
|       path: a.path, | ||||
|       base64: a.body?.toString("base64") | ||||
|     }; | ||||
|   }); | ||||
| } | ||||
| function generatePreview(value, visited = /* @__PURE__ */ new Set()) { | ||||
|   if (visited.has(value)) | ||||
|     return ""; | ||||
|   visited.add(value); | ||||
|   if (typeof value === "string") | ||||
|     return value; | ||||
|   if (typeof value === "number") | ||||
|     return value.toString(); | ||||
|   if (typeof value === "boolean") | ||||
|     return value.toString(); | ||||
|   if (value === null) | ||||
|     return "null"; | ||||
|   if (value === void 0) | ||||
|     return "undefined"; | ||||
|   if (Array.isArray(value)) | ||||
|     return "[" + value.map((v) => generatePreview(v, visited)).join(", ") + "]"; | ||||
|   if (typeof value === "object") | ||||
|     return "Object"; | ||||
|   return String(value); | ||||
| } | ||||
| async function mergeTraceFiles(fileName, temporaryTraceFiles) { | ||||
|   temporaryTraceFiles = temporaryTraceFiles.filter((file) => import_fs.default.existsSync(file)); | ||||
|   if (temporaryTraceFiles.length === 1) { | ||||
|     await import_fs.default.promises.rename(temporaryTraceFiles[0], fileName); | ||||
|     return; | ||||
|   } | ||||
|   const mergePromise = new import_utils.ManualPromise(); | ||||
|   const zipFile = new import_zipBundle.yazl.ZipFile(); | ||||
|   const entryNames = /* @__PURE__ */ new Set(); | ||||
|   zipFile.on("error", (error) => mergePromise.reject(error)); | ||||
|   for (let i = temporaryTraceFiles.length - 1; i >= 0; --i) { | ||||
|     const tempFile = temporaryTraceFiles[i]; | ||||
|     const promise = new import_utils.ManualPromise(); | ||||
|     import_zipBundle.yauzl.open(tempFile, (err, inZipFile) => { | ||||
|       if (err) { | ||||
|         promise.reject(err); | ||||
|         return; | ||||
|       } | ||||
|       let pendingEntries = inZipFile.entryCount; | ||||
|       inZipFile.on("entry", (entry) => { | ||||
|         let entryName = entry.fileName; | ||||
|         if (entry.fileName === testTraceEntryName) { | ||||
|         } else if (entry.fileName.match(/trace\.[a-z]*$/)) { | ||||
|           entryName = i + "-" + entry.fileName; | ||||
|         } | ||||
|         if (entryNames.has(entryName)) { | ||||
|           if (--pendingEntries === 0) | ||||
|             promise.resolve(); | ||||
|           return; | ||||
|         } | ||||
|         entryNames.add(entryName); | ||||
|         inZipFile.openReadStream(entry, (err2, readStream) => { | ||||
|           if (err2) { | ||||
|             promise.reject(err2); | ||||
|             return; | ||||
|           } | ||||
|           zipFile.addReadStream(readStream, entryName); | ||||
|           if (--pendingEntries === 0) | ||||
|             promise.resolve(); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|     await promise; | ||||
|   } | ||||
|   zipFile.end(void 0, () => { | ||||
|     zipFile.outputStream.pipe(import_fs.default.createWriteStream(fileName)).on("close", () => { | ||||
|       void Promise.all(temporaryTraceFiles.map((tempFile) => import_fs.default.promises.unlink(tempFile))).then(() => { | ||||
|         mergePromise.resolve(); | ||||
|       }).catch((error) => mergePromise.reject(error)); | ||||
|     }).on("error", (error) => mergePromise.reject(error)); | ||||
|   }); | ||||
|   await mergePromise; | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   TestTracing, | ||||
|   testTraceEntryName | ||||
| }); | ||||
							
								
								
									
										174
									
								
								frontend/e2e/node_modules/playwright/lib/worker/timeoutManager.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								frontend/e2e/node_modules/playwright/lib/worker/timeoutManager.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | ||||
| "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 timeoutManager_exports = {}; | ||||
| __export(timeoutManager_exports, { | ||||
|   TimeoutManager: () => TimeoutManager, | ||||
|   TimeoutManagerError: () => TimeoutManagerError, | ||||
|   kMaxDeadline: () => kMaxDeadline | ||||
| }); | ||||
| module.exports = __toCommonJS(timeoutManager_exports); | ||||
| var import_utils = require("playwright-core/lib/utils"); | ||||
| var import_utils2 = require("playwright-core/lib/utils"); | ||||
| var import_util = require("../util"); | ||||
| const kMaxDeadline = 2147483647; | ||||
| class TimeoutManager { | ||||
|   constructor(timeout) { | ||||
|     this._ignoreTimeouts = false; | ||||
|     this._defaultSlot = { timeout, elapsed: 0 }; | ||||
|   } | ||||
|   setIgnoreTimeouts() { | ||||
|     this._ignoreTimeouts = true; | ||||
|     if (this._running) | ||||
|       this._updateTimeout(this._running); | ||||
|   } | ||||
|   interrupt() { | ||||
|     if (this._running) | ||||
|       this._running.timeoutPromise.reject(this._createTimeoutError(this._running)); | ||||
|   } | ||||
|   isTimeExhaustedFor(runnable) { | ||||
|     const slot = runnable.fixture?.slot || runnable.slot || this._defaultSlot; | ||||
|     return slot.timeout > 0 && slot.elapsed >= slot.timeout - 1; | ||||
|   } | ||||
|   async withRunnable(runnable, cb) { | ||||
|     if (this._running) | ||||
|       throw new Error(`Internal error: duplicate runnable`); | ||||
|     const running = this._running = { | ||||
|       runnable, | ||||
|       slot: runnable.fixture?.slot || runnable.slot || this._defaultSlot, | ||||
|       start: (0, import_utils.monotonicTime)(), | ||||
|       deadline: kMaxDeadline, | ||||
|       timer: void 0, | ||||
|       timeoutPromise: new import_utils.ManualPromise() | ||||
|     }; | ||||
|     let debugTitle = ""; | ||||
|     try { | ||||
|       if (import_util.debugTest.enabled) { | ||||
|         debugTitle = runnable.fixture ? `${runnable.fixture.phase} "${runnable.fixture.title}"` : runnable.type; | ||||
|         const location = runnable.location ? ` at "${(0, import_util.formatLocation)(runnable.location)}"` : ``; | ||||
|         (0, import_util.debugTest)(`started ${debugTitle}${location}`); | ||||
|       } | ||||
|       this._updateTimeout(running); | ||||
|       return await Promise.race([ | ||||
|         cb(), | ||||
|         running.timeoutPromise | ||||
|       ]); | ||||
|     } finally { | ||||
|       if (running.timer) | ||||
|         clearTimeout(running.timer); | ||||
|       running.timer = void 0; | ||||
|       running.slot.elapsed += (0, import_utils.monotonicTime)() - running.start; | ||||
|       this._running = void 0; | ||||
|       if (import_util.debugTest.enabled) | ||||
|         (0, import_util.debugTest)(`finished ${debugTitle}`); | ||||
|     } | ||||
|   } | ||||
|   _updateTimeout(running) { | ||||
|     if (running.timer) | ||||
|       clearTimeout(running.timer); | ||||
|     running.timer = void 0; | ||||
|     if (this._ignoreTimeouts || !running.slot.timeout) { | ||||
|       running.deadline = kMaxDeadline; | ||||
|       return; | ||||
|     } | ||||
|     running.deadline = running.start + (running.slot.timeout - running.slot.elapsed); | ||||
|     const timeout = running.deadline - (0, import_utils.monotonicTime)() + 1; | ||||
|     if (timeout <= 0) | ||||
|       running.timeoutPromise.reject(this._createTimeoutError(running)); | ||||
|     else | ||||
|       running.timer = setTimeout(() => running.timeoutPromise.reject(this._createTimeoutError(running)), timeout); | ||||
|   } | ||||
|   defaultSlot() { | ||||
|     return this._defaultSlot; | ||||
|   } | ||||
|   slow() { | ||||
|     const slot = this._running ? this._running.slot : this._defaultSlot; | ||||
|     slot.timeout = slot.timeout * 3; | ||||
|     if (this._running) | ||||
|       this._updateTimeout(this._running); | ||||
|   } | ||||
|   setTimeout(timeout) { | ||||
|     const slot = this._running ? this._running.slot : this._defaultSlot; | ||||
|     slot.timeout = timeout; | ||||
|     if (this._running) | ||||
|       this._updateTimeout(this._running); | ||||
|   } | ||||
|   currentSlotDeadline() { | ||||
|     return this._running ? this._running.deadline : kMaxDeadline; | ||||
|   } | ||||
|   currentSlotType() { | ||||
|     return this._running ? this._running.runnable.type : "test"; | ||||
|   } | ||||
|   _createTimeoutError(running) { | ||||
|     let message = ""; | ||||
|     const timeout = running.slot.timeout; | ||||
|     const runnable = running.runnable; | ||||
|     switch (runnable.type) { | ||||
|       case "test": { | ||||
|         if (runnable.fixture) { | ||||
|           if (runnable.fixture.phase === "setup") | ||||
|             message = `Test timeout of ${timeout}ms exceeded while setting up "${runnable.fixture.title}".`; | ||||
|           else | ||||
|             message = `Tearing down "${runnable.fixture.title}" exceeded the test timeout of ${timeout}ms.`; | ||||
|         } else { | ||||
|           message = `Test timeout of ${timeout}ms exceeded.`; | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       case "afterEach": | ||||
|       case "beforeEach": | ||||
|         message = `Test timeout of ${timeout}ms exceeded while running "${runnable.type}" hook.`; | ||||
|         break; | ||||
|       case "beforeAll": | ||||
|       case "afterAll": | ||||
|         message = `"${runnable.type}" hook timeout of ${timeout}ms exceeded.`; | ||||
|         break; | ||||
|       case "teardown": { | ||||
|         if (runnable.fixture) | ||||
|           message = `Worker teardown timeout of ${timeout}ms exceeded while ${runnable.fixture.phase === "setup" ? "setting up" : "tearing down"} "${runnable.fixture.title}".`; | ||||
|         else | ||||
|           message = `Worker teardown timeout of ${timeout}ms exceeded.`; | ||||
|         break; | ||||
|       } | ||||
|       case "skip": | ||||
|       case "slow": | ||||
|       case "fixme": | ||||
|       case "fail": | ||||
|         message = `"${runnable.type}" modifier timeout of ${timeout}ms exceeded.`; | ||||
|         break; | ||||
|     } | ||||
|     const fixtureWithSlot = runnable.fixture?.slot ? runnable.fixture : void 0; | ||||
|     if (fixtureWithSlot) | ||||
|       message = `Fixture "${fixtureWithSlot.title}" timeout of ${timeout}ms exceeded during ${fixtureWithSlot.phase}.`; | ||||
|     message = import_utils2.colors.red(message); | ||||
|     const location = (fixtureWithSlot || runnable).location; | ||||
|     const error = new TimeoutManagerError(message); | ||||
|     error.name = ""; | ||||
|     error.stack = message + (location ? ` | ||||
|     at ${location.file}:${location.line}:${location.column}` : ""); | ||||
|     return error; | ||||
|   } | ||||
| } | ||||
| class TimeoutManagerError extends Error { | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   TimeoutManager, | ||||
|   TimeoutManagerError, | ||||
|   kMaxDeadline | ||||
| }); | ||||
							
								
								
									
										31
									
								
								frontend/e2e/node_modules/playwright/lib/worker/util.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								frontend/e2e/node_modules/playwright/lib/worker/util.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| "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 util_exports = {}; | ||||
| __export(util_exports, { | ||||
|   testInfoError: () => testInfoError | ||||
| }); | ||||
| module.exports = __toCommonJS(util_exports); | ||||
| var import_util = require("../util"); | ||||
| function testInfoError(error) { | ||||
|   return (0, import_util.serializeError)(error); | ||||
| } | ||||
| // Annotate the CommonJS export names for ESM import in node: | ||||
| 0 && (module.exports = { | ||||
|   testInfoError | ||||
| }); | ||||
							
								
								
									
										516
									
								
								frontend/e2e/node_modules/playwright/lib/worker/workerMain.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								frontend/e2e/node_modules/playwright/lib/worker/workerMain.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,516 @@ | ||||
| "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 | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user
	 Connor Johnstone
					Connor Johnstone