| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- "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
- });
|