testInfo.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __export = (target, all) => {
  9. for (var name in all)
  10. __defProp(target, name, { get: all[name], enumerable: true });
  11. };
  12. var __copyProps = (to, from, except, desc) => {
  13. if (from && typeof from === "object" || typeof from === "function") {
  14. for (let key of __getOwnPropNames(from))
  15. if (!__hasOwnProp.call(to, key) && key !== except)
  16. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  17. }
  18. return to;
  19. };
  20. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  21. // If the importer is in node compatibility mode or this is not an ESM
  22. // file that has been converted to a CommonJS file using a Babel-
  23. // compatible transform (i.e. "__esModule" has not been set), then set
  24. // "default" to the CommonJS "module.exports" for node compatibility.
  25. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  26. mod
  27. ));
  28. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  29. var testInfo_exports = {};
  30. __export(testInfo_exports, {
  31. StepSkipError: () => StepSkipError,
  32. TestInfoImpl: () => TestInfoImpl,
  33. TestSkipError: () => TestSkipError,
  34. TestStepInfoImpl: () => TestStepInfoImpl
  35. });
  36. module.exports = __toCommonJS(testInfo_exports);
  37. var import_fs = __toESM(require("fs"));
  38. var import_path = __toESM(require("path"));
  39. var import_utils = require("playwright-core/lib/utils");
  40. var import_timeoutManager = require("./timeoutManager");
  41. var import_util = require("../util");
  42. var import_testTracing = require("./testTracing");
  43. var import_util2 = require("./util");
  44. var import_transform = require("../transform/transform");
  45. class TestInfoImpl {
  46. constructor(configInternal, projectInternal, workerParams, test, retry, onStepBegin, onStepRecoverFromError, onStepEnd, onAttach) {
  47. this._snapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
  48. this._ariaSnapshotNames = { lastAnonymousSnapshotIndex: 0, lastNamedSnapshotIndex: {} };
  49. this._wasInterrupted = false;
  50. this._lastStepId = 0;
  51. this._steps = [];
  52. this._stepMap = /* @__PURE__ */ new Map();
  53. this._hasNonRetriableError = false;
  54. this._hasUnhandledError = false;
  55. this._allowSkips = false;
  56. this.duration = 0;
  57. this.annotations = [];
  58. this.attachments = [];
  59. this.status = "passed";
  60. this.snapshotSuffix = "";
  61. this.errors = [];
  62. this.testId = test?.id ?? "";
  63. this._onStepBegin = onStepBegin;
  64. this._onStepRecoverFromError = onStepRecoverFromError;
  65. this._onStepEnd = onStepEnd;
  66. this._onAttach = onAttach;
  67. this._startTime = (0, import_utils.monotonicTime)();
  68. this._startWallTime = Date.now();
  69. this._requireFile = test?._requireFile ?? "";
  70. this._uniqueSymbol = Symbol("testInfoUniqueSymbol");
  71. this.repeatEachIndex = workerParams.repeatEachIndex;
  72. this.retry = retry;
  73. this.workerIndex = workerParams.workerIndex;
  74. this.parallelIndex = workerParams.parallelIndex;
  75. this._projectInternal = projectInternal;
  76. this.project = projectInternal.project;
  77. this._configInternal = configInternal;
  78. this.config = configInternal.config;
  79. this.title = test?.title ?? "";
  80. this.titlePath = test?.titlePath() ?? [];
  81. this.file = test?.location.file ?? "";
  82. this.line = test?.location.line ?? 0;
  83. this.column = test?.location.column ?? 0;
  84. this.tags = test?.tags ?? [];
  85. this.fn = test?.fn ?? (() => {
  86. });
  87. this.expectedStatus = test?.expectedStatus ?? "skipped";
  88. this._recoverFromStepErrorResults = workerParams.recoverFromStepErrors ? /* @__PURE__ */ new Map() : void 0;
  89. this._timeoutManager = new import_timeoutManager.TimeoutManager(this.project.timeout);
  90. if (configInternal.configCLIOverrides.debug)
  91. this._setDebugMode();
  92. this.outputDir = (() => {
  93. const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile.replace(/\.(spec|test)\.(js|ts|jsx|tsx|mjs|mts|cjs|cts)$/, ""));
  94. const sanitizedRelativePath = relativeTestFilePath.replace(process.platform === "win32" ? new RegExp("\\\\", "g") : new RegExp("/", "g"), "-");
  95. const fullTitleWithoutSpec = this.titlePath.slice(1).join(" ");
  96. let testOutputDir = (0, import_util.trimLongString)(sanitizedRelativePath + "-" + (0, import_utils.sanitizeForFilePath)(fullTitleWithoutSpec), import_util.windowsFilesystemFriendlyLength);
  97. if (projectInternal.id)
  98. testOutputDir += "-" + (0, import_utils.sanitizeForFilePath)(projectInternal.id);
  99. if (this.retry)
  100. testOutputDir += "-retry" + this.retry;
  101. if (this.repeatEachIndex)
  102. testOutputDir += "-repeat" + this.repeatEachIndex;
  103. return import_path.default.join(this.project.outputDir, testOutputDir);
  104. })();
  105. this.snapshotDir = (() => {
  106. const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile);
  107. return import_path.default.join(this.project.snapshotDir, relativeTestFilePath + "-snapshots");
  108. })();
  109. this._attachmentsPush = this.attachments.push.bind(this.attachments);
  110. this.attachments.push = (...attachments) => {
  111. for (const a of attachments)
  112. this._attach(a, this._parentStep()?.stepId);
  113. return this.attachments.length;
  114. };
  115. this._tracing = new import_testTracing.TestTracing(this, workerParams.artifactsDir);
  116. this.skip = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("skip", location, args));
  117. this.fixme = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("fixme", location, args));
  118. this.fail = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("fail", location, args));
  119. this.slow = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => this._modifier("slow", location, args));
  120. }
  121. get error() {
  122. return this.errors[0];
  123. }
  124. set error(e) {
  125. if (e === void 0)
  126. throw new Error("Cannot assign testInfo.error undefined value!");
  127. this.errors[0] = e;
  128. }
  129. get timeout() {
  130. return this._timeoutManager.defaultSlot().timeout;
  131. }
  132. set timeout(timeout) {
  133. }
  134. _deadlineForMatcher(timeout) {
  135. const startTime = (0, import_utils.monotonicTime)();
  136. const matcherDeadline = timeout ? startTime + timeout : import_timeoutManager.kMaxDeadline;
  137. const testDeadline = this._timeoutManager.currentSlotDeadline() - 250;
  138. const matcherMessage = `Timeout ${timeout}ms exceeded while waiting on the predicate`;
  139. const testMessage = `Test timeout of ${this.timeout}ms exceeded`;
  140. return { deadline: Math.min(testDeadline, matcherDeadline), timeoutMessage: testDeadline < matcherDeadline ? testMessage : matcherMessage };
  141. }
  142. static _defaultDeadlineForMatcher(timeout) {
  143. return { deadline: timeout ? (0, import_utils.monotonicTime)() + timeout : 0, timeoutMessage: `Timeout ${timeout}ms exceeded while waiting on the predicate` };
  144. }
  145. _modifier(type, location, modifierArgs) {
  146. if (typeof modifierArgs[1] === "function") {
  147. throw new Error([
  148. "It looks like you are calling test.skip() inside the test and pass a callback.",
  149. "Pass a condition instead and optional description instead:",
  150. `test('my test', async ({ page, isMobile }) => {`,
  151. ` test.skip(isMobile, 'This test is not applicable on mobile');`,
  152. `});`
  153. ].join("\n"));
  154. }
  155. if (modifierArgs.length >= 1 && !modifierArgs[0])
  156. return;
  157. const description = modifierArgs[1];
  158. this.annotations.push({ type, description, location });
  159. if (type === "slow") {
  160. this._timeoutManager.slow();
  161. } else if (type === "skip" || type === "fixme") {
  162. this.expectedStatus = "skipped";
  163. throw new TestSkipError("Test is skipped: " + (description || ""));
  164. } else if (type === "fail") {
  165. if (this.expectedStatus !== "skipped")
  166. this.expectedStatus = "failed";
  167. }
  168. }
  169. _findLastPredefinedStep(steps) {
  170. for (let i = steps.length - 1; i >= 0; i--) {
  171. const child = this._findLastPredefinedStep(steps[i].steps);
  172. if (child)
  173. return child;
  174. if ((steps[i].category === "hook" || steps[i].category === "fixture") && !steps[i].endWallTime)
  175. return steps[i];
  176. }
  177. }
  178. _parentStep() {
  179. return (0, import_utils.currentZone)().data("stepZone") ?? this._findLastPredefinedStep(this._steps);
  180. }
  181. _addStep(data, parentStep) {
  182. const stepId = `${data.category}@${++this._lastStepId}`;
  183. if (data.category === "hook" || data.category === "fixture") {
  184. parentStep = this._findLastPredefinedStep(this._steps);
  185. } else {
  186. if (!parentStep)
  187. parentStep = this._parentStep();
  188. }
  189. const filteredStack = (0, import_util.filteredStackTrace)((0, import_utils.captureRawStack)());
  190. let boxedStack = parentStep?.boxedStack;
  191. let location = data.location;
  192. if (!boxedStack && data.box) {
  193. boxedStack = filteredStack.slice(1);
  194. location = location || boxedStack[0];
  195. }
  196. location = location || filteredStack[0];
  197. const step = {
  198. ...data,
  199. stepId,
  200. group: parentStep?.group ?? data.group,
  201. boxedStack,
  202. location,
  203. steps: [],
  204. attachmentIndices: [],
  205. info: new TestStepInfoImpl(this, stepId, data.title, parentStep?.info),
  206. recoverFromStepError: async (error) => {
  207. if (!this._recoverFromStepErrorResults)
  208. return { stepId, status: "failed" };
  209. const payload = {
  210. testId: this.testId,
  211. stepId,
  212. error: (0, import_util.serializeError)(error)
  213. };
  214. this._onStepRecoverFromError(payload);
  215. const recoveryPromise = new import_utils.ManualPromise();
  216. this._recoverFromStepErrorResults.set(stepId, recoveryPromise);
  217. const recoveryResult = await recoveryPromise;
  218. if (recoveryResult.stepId !== stepId)
  219. return { stepId, status: "failed" };
  220. return recoveryResult;
  221. },
  222. complete: (result) => {
  223. if (step.endWallTime)
  224. return;
  225. step.endWallTime = Date.now();
  226. if (result.error) {
  227. if (typeof result.error === "object" && !result.error?.[stepSymbol])
  228. result.error[stepSymbol] = step;
  229. const error = (0, import_util2.testInfoError)(result.error);
  230. if (step.boxedStack)
  231. error.stack = `${error.message}
  232. ${(0, import_utils.stringifyStackFrames)(step.boxedStack).join("\n")}`;
  233. step.error = error;
  234. }
  235. if (!step.error) {
  236. for (const childStep of step.steps) {
  237. if (childStep.error && childStep.infectParentStepsWithError) {
  238. step.error = childStep.error;
  239. step.infectParentStepsWithError = true;
  240. break;
  241. }
  242. }
  243. }
  244. if (!step.group) {
  245. const payload = {
  246. testId: this.testId,
  247. stepId,
  248. wallTime: step.endWallTime,
  249. error: step.error,
  250. suggestedRebaseline: result.suggestedRebaseline,
  251. annotations: step.info.annotations
  252. };
  253. this._onStepEnd(payload);
  254. }
  255. if (step.group !== "internal") {
  256. const errorForTrace = step.error ? { name: "", message: step.error.message || "", stack: step.error.stack } : void 0;
  257. const attachments = step.attachmentIndices.map((i) => this.attachments[i]);
  258. this._tracing.appendAfterActionForStep(stepId, errorForTrace, attachments, step.info.annotations);
  259. }
  260. }
  261. };
  262. const parentStepList = parentStep ? parentStep.steps : this._steps;
  263. parentStepList.push(step);
  264. this._stepMap.set(stepId, step);
  265. if (!step.group) {
  266. const payload = {
  267. testId: this.testId,
  268. stepId,
  269. parentStepId: parentStep ? parentStep.stepId : void 0,
  270. title: step.title,
  271. category: step.category,
  272. wallTime: Date.now(),
  273. location: step.location
  274. };
  275. this._onStepBegin(payload);
  276. }
  277. if (step.group !== "internal") {
  278. this._tracing.appendBeforeActionForStep({
  279. stepId,
  280. parentId: parentStep?.stepId,
  281. title: step.title,
  282. category: step.category,
  283. params: step.params,
  284. stack: step.location ? [step.location] : [],
  285. group: step.group
  286. });
  287. }
  288. return step;
  289. }
  290. resumeAfterStepError(result) {
  291. const recoveryPromise = this._recoverFromStepErrorResults?.get(result.stepId);
  292. if (recoveryPromise)
  293. recoveryPromise.resolve(result);
  294. }
  295. _interrupt() {
  296. this._wasInterrupted = true;
  297. this._timeoutManager.interrupt();
  298. if (this.status === "passed")
  299. this.status = "interrupted";
  300. }
  301. _failWithError(error) {
  302. if (this.status === "passed" || this.status === "skipped")
  303. this.status = error instanceof import_timeoutManager.TimeoutManagerError ? "timedOut" : "failed";
  304. const serialized = (0, import_util2.testInfoError)(error);
  305. const step = typeof error === "object" ? error?.[stepSymbol] : void 0;
  306. if (step && step.boxedStack)
  307. serialized.stack = `${error.name}: ${error.message}
  308. ${(0, import_utils.stringifyStackFrames)(step.boxedStack).join("\n")}`;
  309. this.errors.push(serialized);
  310. this._tracing.appendForError(serialized);
  311. }
  312. async _runAsStep(stepInfo, cb) {
  313. const step = this._addStep(stepInfo);
  314. try {
  315. await cb();
  316. step.complete({});
  317. } catch (error) {
  318. step.complete({ error });
  319. throw error;
  320. }
  321. }
  322. async _runWithTimeout(runnable, cb) {
  323. try {
  324. await this._timeoutManager.withRunnable(runnable, async () => {
  325. try {
  326. await cb();
  327. } catch (e) {
  328. if (this._allowSkips && e instanceof TestSkipError) {
  329. if (this.status === "passed")
  330. this.status = "skipped";
  331. } else {
  332. this._failWithError(e);
  333. }
  334. throw e;
  335. }
  336. });
  337. } catch (error) {
  338. if (!this._wasInterrupted && error instanceof import_timeoutManager.TimeoutManagerError)
  339. this._failWithError(error);
  340. throw error;
  341. }
  342. }
  343. _isFailure() {
  344. return this.status !== "skipped" && this.status !== this.expectedStatus;
  345. }
  346. _currentHookType() {
  347. const type = this._timeoutManager.currentSlotType();
  348. return ["beforeAll", "afterAll", "beforeEach", "afterEach"].includes(type) ? type : void 0;
  349. }
  350. _setDebugMode() {
  351. this._timeoutManager.setIgnoreTimeouts();
  352. }
  353. // ------------ TestInfo methods ------------
  354. async attach(name, options = {}) {
  355. const step = this._addStep({
  356. title: `Attach ${(0, import_utils.escapeWithQuotes)(name, '"')}`,
  357. category: "test.attach"
  358. });
  359. this._attach(await (0, import_util.normalizeAndSaveAttachment)(this.outputPath(), name, options), step.stepId);
  360. step.complete({});
  361. }
  362. _attach(attachment, stepId) {
  363. const index = this._attachmentsPush(attachment) - 1;
  364. if (stepId) {
  365. this._stepMap.get(stepId).attachmentIndices.push(index);
  366. } else {
  367. const stepId2 = `attach@${(0, import_utils.createGuid)()}`;
  368. this._tracing.appendBeforeActionForStep({ stepId: stepId2, title: `Attach ${(0, import_utils.escapeWithQuotes)(attachment.name, '"')}`, category: "test.attach", stack: [] });
  369. this._tracing.appendAfterActionForStep(stepId2, void 0, [attachment]);
  370. }
  371. this._onAttach({
  372. testId: this.testId,
  373. name: attachment.name,
  374. contentType: attachment.contentType,
  375. path: attachment.path,
  376. body: attachment.body?.toString("base64"),
  377. stepId
  378. });
  379. }
  380. outputPath(...pathSegments) {
  381. const outputPath = this._getOutputPath(...pathSegments);
  382. import_fs.default.mkdirSync(this.outputDir, { recursive: true });
  383. return outputPath;
  384. }
  385. _getOutputPath(...pathSegments) {
  386. const joinedPath = import_path.default.join(...pathSegments);
  387. const outputPath = (0, import_util.getContainedPath)(this.outputDir, joinedPath);
  388. if (outputPath)
  389. return outputPath;
  390. throw new Error(`The outputPath is not allowed outside of the parent directory. Please fix the defined path.
  391. outputPath: ${joinedPath}`);
  392. }
  393. _fsSanitizedTestName() {
  394. const fullTitleWithoutSpec = this.titlePath.slice(1).join(" ");
  395. return (0, import_utils.sanitizeForFilePath)((0, import_util.trimLongString)(fullTitleWithoutSpec));
  396. }
  397. _resolveSnapshotPaths(kind, name, updateSnapshotIndex, anonymousExtension) {
  398. const snapshotNames = kind === "aria" ? this._ariaSnapshotNames : this._snapshotNames;
  399. const defaultExtensions = { "aria": ".aria.yml", "screenshot": ".png", "snapshot": ".txt" };
  400. const ariaAwareExtname = (filePath) => kind === "aria" && filePath.endsWith(".aria.yml") ? ".aria.yml" : import_path.default.extname(filePath);
  401. let subPath;
  402. let ext;
  403. let relativeOutputPath;
  404. if (!name) {
  405. const index = snapshotNames.lastAnonymousSnapshotIndex + 1;
  406. if (updateSnapshotIndex === "updateSnapshotIndex")
  407. snapshotNames.lastAnonymousSnapshotIndex = index;
  408. const fullTitleWithoutSpec = [...this.titlePath.slice(1), index].join(" ");
  409. ext = anonymousExtension ?? defaultExtensions[kind];
  410. subPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(fullTitleWithoutSpec) + ext, ext);
  411. relativeOutputPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(fullTitleWithoutSpec, import_util.windowsFilesystemFriendlyLength) + ext, ext);
  412. } else {
  413. if (Array.isArray(name)) {
  414. subPath = import_path.default.join(...name);
  415. relativeOutputPath = import_path.default.join(...name);
  416. ext = ariaAwareExtname(subPath);
  417. } else {
  418. ext = ariaAwareExtname(name);
  419. subPath = (0, import_util.sanitizeFilePathBeforeExtension)(name, ext);
  420. relativeOutputPath = (0, import_util.sanitizeFilePathBeforeExtension)((0, import_util.trimLongString)(name, import_util.windowsFilesystemFriendlyLength), ext);
  421. }
  422. const index = (snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] || 0) + 1;
  423. if (updateSnapshotIndex === "updateSnapshotIndex")
  424. snapshotNames.lastNamedSnapshotIndex[relativeOutputPath] = index;
  425. if (index > 1)
  426. relativeOutputPath = (0, import_util.addSuffixToFilePath)(relativeOutputPath, `-${index - 1}`);
  427. }
  428. const absoluteSnapshotPath = this._applyPathTemplate(kind, subPath, ext);
  429. return { absoluteSnapshotPath, relativeOutputPath };
  430. }
  431. _applyPathTemplate(kind, relativePath, ext) {
  432. const legacyTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
  433. let template;
  434. if (kind === "screenshot") {
  435. template = this._projectInternal.expect?.toHaveScreenshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || legacyTemplate;
  436. } else if (kind === "aria") {
  437. const ariaDefaultTemplate = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{ext}";
  438. template = this._projectInternal.expect?.toMatchAriaSnapshot?.pathTemplate || this._projectInternal.snapshotPathTemplate || ariaDefaultTemplate;
  439. } else {
  440. template = this._projectInternal.snapshotPathTemplate || legacyTemplate;
  441. }
  442. const dir = import_path.default.dirname(relativePath);
  443. const name = import_path.default.basename(relativePath, ext);
  444. const relativeTestFilePath = import_path.default.relative(this.project.testDir, this._requireFile);
  445. const parsedRelativeTestFilePath = import_path.default.parse(relativeTestFilePath);
  446. const projectNamePathSegment = (0, import_utils.sanitizeForFilePath)(this.project.name);
  447. 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 : "");
  448. return import_path.default.normalize(import_path.default.resolve(this._configInternal.configDir, snapshotPath));
  449. }
  450. snapshotPath(...args) {
  451. let name = args;
  452. let kind = "snapshot";
  453. const options = args[args.length - 1];
  454. if (options && typeof options === "object") {
  455. kind = options.kind ?? kind;
  456. name = args.slice(0, -1);
  457. }
  458. if (!["snapshot", "screenshot", "aria"].includes(kind))
  459. throw new Error(`testInfo.snapshotPath: unknown kind "${kind}", must be one of "snapshot", "screenshot" or "aria"`);
  460. return this._resolveSnapshotPaths(kind, name.length <= 1 ? name[0] : name, "dontUpdateSnapshotIndex").absoluteSnapshotPath;
  461. }
  462. setTimeout(timeout) {
  463. this._timeoutManager.setTimeout(timeout);
  464. }
  465. }
  466. class TestStepInfoImpl {
  467. constructor(testInfo, stepId, title, parentStep) {
  468. this.annotations = [];
  469. this._testInfo = testInfo;
  470. this._stepId = stepId;
  471. this._title = title;
  472. this._parentStep = parentStep;
  473. this.skip = (0, import_transform.wrapFunctionWithLocation)((location, ...args) => {
  474. if (args.length > 0 && !args[0])
  475. return;
  476. const description = args[1];
  477. this.annotations.push({ type: "skip", description, location });
  478. throw new StepSkipError(description);
  479. });
  480. }
  481. async _runStepBody(skip, body, location) {
  482. if (skip) {
  483. this.annotations.push({ type: "skip", location });
  484. return void 0;
  485. }
  486. try {
  487. return await body(this);
  488. } catch (e) {
  489. if (e instanceof StepSkipError)
  490. return void 0;
  491. throw e;
  492. }
  493. }
  494. _attachToStep(attachment) {
  495. this._testInfo._attach(attachment, this._stepId);
  496. }
  497. async attach(name, options) {
  498. this._attachToStep(await (0, import_util.normalizeAndSaveAttachment)(this._testInfo.outputPath(), name, options));
  499. }
  500. get titlePath() {
  501. const parent = this._parentStep ?? this._testInfo;
  502. return [...parent.titlePath, this._title];
  503. }
  504. }
  505. class TestSkipError extends Error {
  506. }
  507. class StepSkipError extends Error {
  508. }
  509. const stepSymbol = Symbol("step");
  510. // Annotate the CommonJS export names for ESM import in node:
  511. 0 && (module.exports = {
  512. StepSkipError,
  513. TestInfoImpl,
  514. TestSkipError,
  515. TestStepInfoImpl
  516. });