ScuttleBot

scuttlebot / tests / e2e / node_modules / playwright / lib / worker / testTracing.js
Source Blame History 345 lines
f7eb47b… lmata 1 "use strict";
f7eb47b… lmata 2 var __create = Object.create;
f7eb47b… lmata 3 var __defProp = Object.defineProperty;
f7eb47b… lmata 4 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
f7eb47b… lmata 5 var __getOwnPropNames = Object.getOwnPropertyNames;
f7eb47b… lmata 6 var __getProtoOf = Object.getPrototypeOf;
f7eb47b… lmata 7 var __hasOwnProp = Object.prototype.hasOwnProperty;
f7eb47b… lmata 8 var __export = (target, all) => {
f7eb47b… lmata 9 for (var name in all)
f7eb47b… lmata 10 __defProp(target, name, { get: all[name], enumerable: true });
f7eb47b… lmata 11 };
f7eb47b… lmata 12 var __copyProps = (to, from, except, desc) => {
f7eb47b… lmata 13 if (from && typeof from === "object" || typeof from === "function") {
f7eb47b… lmata 14 for (let key of __getOwnPropNames(from))
f7eb47b… lmata 15 if (!__hasOwnProp.call(to, key) && key !== except)
f7eb47b… lmata 16 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
f7eb47b… lmata 17 }
f7eb47b… lmata 18 return to;
f7eb47b… lmata 19 };
f7eb47b… lmata 20 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
f7eb47b… lmata 21 // If the importer is in node compatibility mode or this is not an ESM
f7eb47b… lmata 22 // file that has been converted to a CommonJS file using a Babel-
f7eb47b… lmata 23 // compatible transform (i.e. "__esModule" has not been set), then set
f7eb47b… lmata 24 // "default" to the CommonJS "module.exports" for node compatibility.
f7eb47b… lmata 25 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
f7eb47b… lmata 26 mod
f7eb47b… lmata 27 ));
f7eb47b… lmata 28 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
f7eb47b… lmata 29 var testTracing_exports = {};
f7eb47b… lmata 30 __export(testTracing_exports, {
f7eb47b… lmata 31 TestTracing: () => TestTracing,
f7eb47b… lmata 32 testTraceEntryName: () => testTraceEntryName
f7eb47b… lmata 33 });
f7eb47b… lmata 34 module.exports = __toCommonJS(testTracing_exports);
f7eb47b… lmata 35 var import_fs = __toESM(require("fs"));
f7eb47b… lmata 36 var import_path = __toESM(require("path"));
f7eb47b… lmata 37 var import_utils = require("playwright-core/lib/utils");
f7eb47b… lmata 38 var import_zipBundle = require("playwright-core/lib/zipBundle");
f7eb47b… lmata 39 var import_util = require("../util");
f7eb47b… lmata 40 const testTraceEntryName = "test.trace";
f7eb47b… lmata 41 const version = 8;
f7eb47b… lmata 42 let traceOrdinal = 0;
f7eb47b… lmata 43 class TestTracing {
f7eb47b… lmata 44 constructor(testInfo, artifactsDir) {
f7eb47b… lmata 45 this._traceEvents = [];
f7eb47b… lmata 46 this._temporaryTraceFiles = [];
f7eb47b… lmata 47 this._didFinishTestFunctionAndAfterEachHooks = false;
f7eb47b… lmata 48 this._testInfo = testInfo;
f7eb47b… lmata 49 this._artifactsDir = artifactsDir;
f7eb47b… lmata 50 this._tracesDir = import_path.default.join(this._artifactsDir, "traces");
f7eb47b… lmata 51 this._contextCreatedEvent = {
f7eb47b… lmata 52 version,
f7eb47b… lmata 53 type: "context-options",
f7eb47b… lmata 54 origin: "testRunner",
f7eb47b… lmata 55 browserName: "",
f7eb47b… lmata 56 playwrightVersion: (0, import_utils.getPlaywrightVersion)(),
f7eb47b… lmata 57 options: {},
f7eb47b… lmata 58 platform: process.platform,
f7eb47b… lmata 59 wallTime: Date.now(),
f7eb47b… lmata 60 monotonicTime: (0, import_utils.monotonicTime)(),
f7eb47b… lmata 61 sdkLanguage: "javascript"
f7eb47b… lmata 62 };
f7eb47b… lmata 63 this._appendTraceEvent(this._contextCreatedEvent);
f7eb47b… lmata 64 }
f7eb47b… lmata 65 _shouldCaptureTrace() {
f7eb47b… lmata 66 if (this._options?.mode === "on")
f7eb47b… lmata 67 return true;
f7eb47b… lmata 68 if (this._options?.mode === "retain-on-failure")
f7eb47b… lmata 69 return true;
f7eb47b… lmata 70 if (this._options?.mode === "on-first-retry" && this._testInfo.retry === 1)
f7eb47b… lmata 71 return true;
f7eb47b… lmata 72 if (this._options?.mode === "on-all-retries" && this._testInfo.retry > 0)
f7eb47b… lmata 73 return true;
f7eb47b… lmata 74 if (this._options?.mode === "retain-on-first-failure" && this._testInfo.retry === 0)
f7eb47b… lmata 75 return true;
f7eb47b… lmata 76 return false;
f7eb47b… lmata 77 }
f7eb47b… lmata 78 async startIfNeeded(value) {
f7eb47b… lmata 79 const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true, _live: false, mode: "off" };
f7eb47b… lmata 80 if (!value) {
f7eb47b… lmata 81 this._options = defaultTraceOptions;
f7eb47b… lmata 82 } else if (typeof value === "string") {
f7eb47b… lmata 83 this._options = { ...defaultTraceOptions, mode: value === "retry-with-trace" ? "on-first-retry" : value };
f7eb47b… lmata 84 } else {
f7eb47b… lmata 85 const mode = value.mode || "off";
f7eb47b… lmata 86 this._options = { ...defaultTraceOptions, ...value, mode: mode === "retry-with-trace" ? "on-first-retry" : mode };
f7eb47b… lmata 87 }
f7eb47b… lmata 88 if (!this._shouldCaptureTrace()) {
f7eb47b… lmata 89 this._options = void 0;
f7eb47b… lmata 90 return;
f7eb47b… lmata 91 }
f7eb47b… lmata 92 if (!this._liveTraceFile && this._options._live) {
f7eb47b… lmata 93 this._liveTraceFile = { file: import_path.default.join(this._tracesDir, `${this._testInfo.testId}-test.trace`), fs: new import_utils.SerializedFS() };
f7eb47b… lmata 94 this._liveTraceFile.fs.mkdir(import_path.default.dirname(this._liveTraceFile.file));
f7eb47b… lmata 95 const data = this._traceEvents.map((e) => JSON.stringify(e)).join("\n") + "\n";
f7eb47b… lmata 96 this._liveTraceFile.fs.writeFile(this._liveTraceFile.file, data);
f7eb47b… lmata 97 }
f7eb47b… lmata 98 }
f7eb47b… lmata 99 didFinishTestFunctionAndAfterEachHooks() {
f7eb47b… lmata 100 this._didFinishTestFunctionAndAfterEachHooks = true;
f7eb47b… lmata 101 }
f7eb47b… lmata 102 artifactsDir() {
f7eb47b… lmata 103 return this._artifactsDir;
f7eb47b… lmata 104 }
f7eb47b… lmata 105 tracesDir() {
f7eb47b… lmata 106 return this._tracesDir;
f7eb47b… lmata 107 }
f7eb47b… lmata 108 traceTitle() {
f7eb47b… lmata 109 return [import_path.default.relative(this._testInfo.project.testDir, this._testInfo.file) + ":" + this._testInfo.line, ...this._testInfo.titlePath.slice(1)].join(" \u203A ");
f7eb47b… lmata 110 }
f7eb47b… lmata 111 generateNextTraceRecordingName() {
f7eb47b… lmata 112 const ordinalSuffix = traceOrdinal ? `-recording${traceOrdinal}` : "";
f7eb47b… lmata 113 ++traceOrdinal;
f7eb47b… lmata 114 const retrySuffix = this._testInfo.retry ? `-retry${this._testInfo.retry}` : "";
f7eb47b… lmata 115 return `${this._testInfo.testId}${retrySuffix}${ordinalSuffix}`;
f7eb47b… lmata 116 }
f7eb47b… lmata 117 _generateNextTraceRecordingPath() {
f7eb47b… lmata 118 const file = import_path.default.join(this._artifactsDir, (0, import_utils.createGuid)() + ".zip");
f7eb47b… lmata 119 this._temporaryTraceFiles.push(file);
f7eb47b… lmata 120 return file;
f7eb47b… lmata 121 }
f7eb47b… lmata 122 traceOptions() {
f7eb47b… lmata 123 return this._options;
f7eb47b… lmata 124 }
f7eb47b… lmata 125 maybeGenerateNextTraceRecordingPath() {
f7eb47b… lmata 126 if (this._didFinishTestFunctionAndAfterEachHooks && this._shouldAbandonTrace())
f7eb47b… lmata 127 return;
f7eb47b… lmata 128 return this._generateNextTraceRecordingPath();
f7eb47b… lmata 129 }
f7eb47b… lmata 130 _shouldAbandonTrace() {
f7eb47b… lmata 131 if (!this._options)
f7eb47b… lmata 132 return true;
f7eb47b… lmata 133 const testFailed = this._testInfo.status !== this._testInfo.expectedStatus;
f7eb47b… lmata 134 return !testFailed && (this._options.mode === "retain-on-failure" || this._options.mode === "retain-on-first-failure");
f7eb47b… lmata 135 }
f7eb47b… lmata 136 async stopIfNeeded() {
f7eb47b… lmata 137 if (!this._options)
f7eb47b… lmata 138 return;
f7eb47b… lmata 139 const error = await this._liveTraceFile?.fs.syncAndGetError();
f7eb47b… lmata 140 if (error)
f7eb47b… lmata 141 throw error;
f7eb47b… lmata 142 if (this._shouldAbandonTrace()) {
f7eb47b… lmata 143 for (const file of this._temporaryTraceFiles)
f7eb47b… lmata 144 await import_fs.default.promises.unlink(file).catch(() => {
f7eb47b… lmata 145 });
f7eb47b… lmata 146 return;
f7eb47b… lmata 147 }
f7eb47b… lmata 148 const zipFile = new import_zipBundle.yazl.ZipFile();
f7eb47b… lmata 149 if (!this._options?.attachments) {
f7eb47b… lmata 150 for (const event of this._traceEvents) {
f7eb47b… lmata 151 if (event.type === "after")
f7eb47b… lmata 152 delete event.attachments;
f7eb47b… lmata 153 }
f7eb47b… lmata 154 }
f7eb47b… lmata 155 if (this._options?.sources) {
f7eb47b… lmata 156 const sourceFiles = /* @__PURE__ */ new Set();
f7eb47b… lmata 157 for (const event of this._traceEvents) {
f7eb47b… lmata 158 if (event.type === "before") {
f7eb47b… lmata 159 for (const frame of event.stack || [])
f7eb47b… lmata 160 sourceFiles.add(frame.file);
f7eb47b… lmata 161 }
f7eb47b… lmata 162 }
f7eb47b… lmata 163 for (const sourceFile of sourceFiles) {
f7eb47b… lmata 164 await import_fs.default.promises.readFile(sourceFile, "utf8").then((source) => {
f7eb47b… lmata 165 zipFile.addBuffer(Buffer.from(source), "resources/src@" + (0, import_utils.calculateSha1)(sourceFile) + ".txt");
f7eb47b… lmata 166 }).catch(() => {
f7eb47b… lmata 167 });
f7eb47b… lmata 168 }
f7eb47b… lmata 169 }
f7eb47b… lmata 170 const sha1s = /* @__PURE__ */ new Set();
f7eb47b… lmata 171 for (const event of this._traceEvents.filter((e) => e.type === "after")) {
f7eb47b… lmata 172 for (const attachment of event.attachments || []) {
f7eb47b… lmata 173 let contentPromise;
f7eb47b… lmata 174 if (attachment.path)
f7eb47b… lmata 175 contentPromise = import_fs.default.promises.readFile(attachment.path).catch(() => void 0);
f7eb47b… lmata 176 else if (attachment.base64)
f7eb47b… lmata 177 contentPromise = Promise.resolve(Buffer.from(attachment.base64, "base64"));
f7eb47b… lmata 178 const content = await contentPromise;
f7eb47b… lmata 179 if (content === void 0)
f7eb47b… lmata 180 continue;
f7eb47b… lmata 181 const sha1 = (0, import_utils.calculateSha1)(content);
f7eb47b… lmata 182 attachment.sha1 = sha1;
f7eb47b… lmata 183 delete attachment.path;
f7eb47b… lmata 184 delete attachment.base64;
f7eb47b… lmata 185 if (sha1s.has(sha1))
f7eb47b… lmata 186 continue;
f7eb47b… lmata 187 sha1s.add(sha1);
f7eb47b… lmata 188 zipFile.addBuffer(content, "resources/" + sha1);
f7eb47b… lmata 189 }
f7eb47b… lmata 190 }
f7eb47b… lmata 191 const traceContent = Buffer.from(this._traceEvents.map((e) => JSON.stringify(e)).join("\n"));
f7eb47b… lmata 192 zipFile.addBuffer(traceContent, testTraceEntryName);
f7eb47b… lmata 193 await new Promise((f) => {
f7eb47b… lmata 194 zipFile.end(void 0, () => {
f7eb47b… lmata 195 zipFile.outputStream.pipe(import_fs.default.createWriteStream(this._generateNextTraceRecordingPath())).on("close", f);
f7eb47b… lmata 196 });
f7eb47b… lmata 197 });
f7eb47b… lmata 198 const tracePath = this._testInfo.outputPath("trace.zip");
f7eb47b… lmata 199 await mergeTraceFiles(tracePath, this._temporaryTraceFiles);
f7eb47b… lmata 200 this._testInfo.attachments.push({ name: "trace", path: tracePath, contentType: "application/zip" });
f7eb47b… lmata 201 }
f7eb47b… lmata 202 appendForError(error) {
f7eb47b… lmata 203 const rawStack = error.stack?.split("\n") || [];
f7eb47b… lmata 204 const stack = rawStack ? (0, import_util.filteredStackTrace)(rawStack) : [];
f7eb47b… lmata 205 this._appendTraceEvent({
f7eb47b… lmata 206 type: "error",
f7eb47b… lmata 207 message: this._formatError(error),
f7eb47b… lmata 208 stack
f7eb47b… lmata 209 });
f7eb47b… lmata 210 }
f7eb47b… lmata 211 _formatError(error) {
f7eb47b… lmata 212 const parts = [error.message || String(error.value)];
f7eb47b… lmata 213 if (error.cause)
f7eb47b… lmata 214 parts.push("[cause]: " + this._formatError(error.cause));
f7eb47b… lmata 215 return parts.join("\n");
f7eb47b… lmata 216 }
f7eb47b… lmata 217 appendStdioToTrace(type, chunk) {
f7eb47b… lmata 218 this._appendTraceEvent({
f7eb47b… lmata 219 type,
f7eb47b… lmata 220 timestamp: (0, import_utils.monotonicTime)(),
f7eb47b… lmata 221 text: typeof chunk === "string" ? chunk : void 0,
f7eb47b… lmata 222 base64: typeof chunk === "string" ? void 0 : chunk.toString("base64")
f7eb47b… lmata 223 });
f7eb47b… lmata 224 }
f7eb47b… lmata 225 appendBeforeActionForStep(options) {
f7eb47b… lmata 226 this._appendTraceEvent({
f7eb47b… lmata 227 type: "before",
f7eb47b… lmata 228 callId: options.stepId,
f7eb47b… lmata 229 stepId: options.stepId,
f7eb47b… lmata 230 parentId: options.parentId,
f7eb47b… lmata 231 startTime: (0, import_utils.monotonicTime)(),
f7eb47b… lmata 232 class: "Test",
f7eb47b… lmata 233 method: options.category,
f7eb47b… lmata 234 title: options.title,
f7eb47b… lmata 235 params: Object.fromEntries(Object.entries(options.params || {}).map(([name, value]) => [name, generatePreview(value)])),
f7eb47b… lmata 236 stack: options.stack,
f7eb47b… lmata 237 group: options.group
f7eb47b… lmata 238 });
f7eb47b… lmata 239 }
f7eb47b… lmata 240 appendAfterActionForStep(callId, error, attachments = [], annotations) {
f7eb47b… lmata 241 this._appendTraceEvent({
f7eb47b… lmata 242 type: "after",
f7eb47b… lmata 243 callId,
f7eb47b… lmata 244 endTime: (0, import_utils.monotonicTime)(),
f7eb47b… lmata 245 attachments: serializeAttachments(attachments),
f7eb47b… lmata 246 annotations,
f7eb47b… lmata 247 error
f7eb47b… lmata 248 });
f7eb47b… lmata 249 }
f7eb47b… lmata 250 _appendTraceEvent(event) {
f7eb47b… lmata 251 this._traceEvents.push(event);
f7eb47b… lmata 252 if (this._liveTraceFile)
f7eb47b… lmata 253 this._liveTraceFile.fs.appendFile(this._liveTraceFile.file, JSON.stringify(event) + "\n", true);
f7eb47b… lmata 254 }
f7eb47b… lmata 255 }
f7eb47b… lmata 256 function serializeAttachments(attachments) {
f7eb47b… lmata 257 if (attachments.length === 0)
f7eb47b… lmata 258 return void 0;
f7eb47b… lmata 259 return attachments.filter((a) => a.name !== "trace").map((a) => {
f7eb47b… lmata 260 return {
f7eb47b… lmata 261 name: a.name,
f7eb47b… lmata 262 contentType: a.contentType,
f7eb47b… lmata 263 path: a.path,
f7eb47b… lmata 264 base64: a.body?.toString("base64")
f7eb47b… lmata 265 };
f7eb47b… lmata 266 });
f7eb47b… lmata 267 }
f7eb47b… lmata 268 function generatePreview(value, visited = /* @__PURE__ */ new Set()) {
f7eb47b… lmata 269 if (visited.has(value))
f7eb47b… lmata 270 return "";
f7eb47b… lmata 271 visited.add(value);
f7eb47b… lmata 272 if (typeof value === "string")
f7eb47b… lmata 273 return value;
f7eb47b… lmata 274 if (typeof value === "number")
f7eb47b… lmata 275 return value.toString();
f7eb47b… lmata 276 if (typeof value === "boolean")
f7eb47b… lmata 277 return value.toString();
f7eb47b… lmata 278 if (value === null)
f7eb47b… lmata 279 return "null";
f7eb47b… lmata 280 if (value === void 0)
f7eb47b… lmata 281 return "undefined";
f7eb47b… lmata 282 if (Array.isArray(value))
f7eb47b… lmata 283 return "[" + value.map((v) => generatePreview(v, visited)).join(", ") + "]";
f7eb47b… lmata 284 if (typeof value === "object")
f7eb47b… lmata 285 return "Object";
f7eb47b… lmata 286 return String(value);
f7eb47b… lmata 287 }
f7eb47b… lmata 288 async function mergeTraceFiles(fileName, temporaryTraceFiles) {
f7eb47b… lmata 289 temporaryTraceFiles = temporaryTraceFiles.filter((file) => import_fs.default.existsSync(file));
f7eb47b… lmata 290 if (temporaryTraceFiles.length === 1) {
f7eb47b… lmata 291 await import_fs.default.promises.rename(temporaryTraceFiles[0], fileName);
f7eb47b… lmata 292 return;
f7eb47b… lmata 293 }
f7eb47b… lmata 294 const mergePromise = new import_utils.ManualPromise();
f7eb47b… lmata 295 const zipFile = new import_zipBundle.yazl.ZipFile();
f7eb47b… lmata 296 const entryNames = /* @__PURE__ */ new Set();
f7eb47b… lmata 297 zipFile.on("error", (error) => mergePromise.reject(error));
f7eb47b… lmata 298 for (let i = temporaryTraceFiles.length - 1; i >= 0; --i) {
f7eb47b… lmata 299 const tempFile = temporaryTraceFiles[i];
f7eb47b… lmata 300 const promise = new import_utils.ManualPromise();
f7eb47b… lmata 301 import_zipBundle.yauzl.open(tempFile, (err, inZipFile) => {
f7eb47b… lmata 302 if (err) {
f7eb47b… lmata 303 promise.reject(err);
f7eb47b… lmata 304 return;
f7eb47b… lmata 305 }
f7eb47b… lmata 306 let pendingEntries = inZipFile.entryCount;
f7eb47b… lmata 307 inZipFile.on("entry", (entry) => {
f7eb47b… lmata 308 let entryName = entry.fileName;
f7eb47b… lmata 309 if (entry.fileName === testTraceEntryName) {
f7eb47b… lmata 310 } else if (entry.fileName.match(/trace\.[a-z]*$/)) {
f7eb47b… lmata 311 entryName = i + "-" + entry.fileName;
f7eb47b… lmata 312 }
f7eb47b… lmata 313 if (entryNames.has(entryName)) {
f7eb47b… lmata 314 if (--pendingEntries === 0)
f7eb47b… lmata 315 promise.resolve();
f7eb47b… lmata 316 return;
f7eb47b… lmata 317 }
f7eb47b… lmata 318 entryNames.add(entryName);
f7eb47b… lmata 319 inZipFile.openReadStream(entry, (err2, readStream) => {
f7eb47b… lmata 320 if (err2) {
f7eb47b… lmata 321 promise.reject(err2);
f7eb47b… lmata 322 return;
f7eb47b… lmata 323 }
f7eb47b… lmata 324 zipFile.addReadStream(readStream, entryName);
f7eb47b… lmata 325 if (--pendingEntries === 0)
f7eb47b… lmata 326 promise.resolve();
f7eb47b… lmata 327 });
f7eb47b… lmata 328 });
f7eb47b… lmata 329 });
f7eb47b… lmata 330 await promise;
f7eb47b… lmata 331 }
f7eb47b… lmata 332 zipFile.end(void 0, () => {
f7eb47b… lmata 333 zipFile.outputStream.pipe(import_fs.default.createWriteStream(fileName)).on("close", () => {
f7eb47b… lmata 334 void Promise.all(temporaryTraceFiles.map((tempFile) => import_fs.default.promises.unlink(tempFile))).then(() => {
f7eb47b… lmata 335 mergePromise.resolve();
f7eb47b… lmata 336 }).catch((error) => mergePromise.reject(error));
f7eb47b… lmata 337 }).on("error", (error) => mergePromise.reject(error));
f7eb47b… lmata 338 });
f7eb47b… lmata 339 await mergePromise;
f7eb47b… lmata 340 }
f7eb47b… lmata 341 // Annotate the CommonJS export names for ESM import in node:
f7eb47b… lmata 342 0 && (module.exports = {
f7eb47b… lmata 343 TestTracing,
f7eb47b… lmata 344 testTraceEntryName
f7eb47b… lmata 345 });

Keyboard Shortcuts

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