|
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
|
|