ScuttleBot

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

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button