First Commit
This commit is contained in:
250
node_modules/playwright/lib/worker/fixtureRunner.js
generated
vendored
Normal file
250
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
node_modules/playwright/lib/worker/testInfo.js
generated
vendored
Normal file
517
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
node_modules/playwright/lib/worker/testTracing.js
generated
vendored
Normal file
344
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
node_modules/playwright/lib/worker/timeoutManager.js
generated
vendored
Normal file
174
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
node_modules/playwright/lib/worker/util.js
generated
vendored
Normal file
31
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
node_modules/playwright/lib/worker/workerMain.js
generated
vendored
Normal file
516
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