ScuttleBot

Source Blame History 343 lines
f7eb47b… lmata 1 "use strict";
f7eb47b… lmata 2 var __defProp = Object.defineProperty;
f7eb47b… lmata 3 var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
f7eb47b… lmata 4 var __getOwnPropNames = Object.getOwnPropertyNames;
f7eb47b… lmata 5 var __hasOwnProp = Object.prototype.hasOwnProperty;
f7eb47b… lmata 6 var __export = (target, all) => {
f7eb47b… lmata 7 for (var name in all)
f7eb47b… lmata 8 __defProp(target, name, { get: all[name], enumerable: true });
f7eb47b… lmata 9 };
f7eb47b… lmata 10 var __copyProps = (to, from, except, desc) => {
f7eb47b… lmata 11 if (from && typeof from === "object" || typeof from === "function") {
f7eb47b… lmata 12 for (let key of __getOwnPropNames(from))
f7eb47b… lmata 13 if (!__hasOwnProp.call(to, key) && key !== except)
f7eb47b… lmata 14 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
f7eb47b… lmata 15 }
f7eb47b… lmata 16 return to;
f7eb47b… lmata 17 };
f7eb47b… lmata 18 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
f7eb47b… lmata 19 var tab_exports = {};
f7eb47b… lmata 20 __export(tab_exports, {
f7eb47b… lmata 21 Tab: () => Tab,
f7eb47b… lmata 22 TabEvents: () => TabEvents,
f7eb47b… lmata 23 renderModalStates: () => renderModalStates,
f7eb47b… lmata 24 shouldIncludeMessage: () => shouldIncludeMessage
f7eb47b… lmata 25 });
f7eb47b… lmata 26 module.exports = __toCommonJS(tab_exports);
f7eb47b… lmata 27 var import_events = require("events");
f7eb47b… lmata 28 var import_utils = require("playwright-core/lib/utils");
f7eb47b… lmata 29 var import_utils2 = require("./tools/utils");
f7eb47b… lmata 30 var import_log = require("../log");
f7eb47b… lmata 31 var import_dialogs = require("./tools/dialogs");
f7eb47b… lmata 32 var import_files = require("./tools/files");
f7eb47b… lmata 33 var import_transform = require("../../transform/transform");
f7eb47b… lmata 34 const TabEvents = {
f7eb47b… lmata 35 modalState: "modalState"
f7eb47b… lmata 36 };
f7eb47b… lmata 37 class Tab extends import_events.EventEmitter {
f7eb47b… lmata 38 constructor(context, page, onPageClose) {
f7eb47b… lmata 39 super();
f7eb47b… lmata 40 this._lastHeader = { title: "about:blank", url: "about:blank", current: false };
f7eb47b… lmata 41 this._consoleMessages = [];
f7eb47b… lmata 42 this._downloads = [];
f7eb47b… lmata 43 this._requests = /* @__PURE__ */ new Set();
f7eb47b… lmata 44 this._modalStates = [];
f7eb47b… lmata 45 this._needsFullSnapshot = false;
f7eb47b… lmata 46 this._eventEntries = [];
f7eb47b… lmata 47 this._recentEventEntries = [];
f7eb47b… lmata 48 this.context = context;
f7eb47b… lmata 49 this.page = page;
f7eb47b… lmata 50 this._onPageClose = onPageClose;
f7eb47b… lmata 51 page.on("console", (event) => this._handleConsoleMessage(messageToConsoleMessage(event)));
f7eb47b… lmata 52 page.on("pageerror", (error) => this._handleConsoleMessage(pageErrorToConsoleMessage(error)));
f7eb47b… lmata 53 page.on("request", (request) => this._handleRequest(request));
f7eb47b… lmata 54 page.on("close", () => this._onClose());
f7eb47b… lmata 55 page.on("filechooser", (chooser) => {
f7eb47b… lmata 56 this.setModalState({
f7eb47b… lmata 57 type: "fileChooser",
f7eb47b… lmata 58 description: "File chooser",
f7eb47b… lmata 59 fileChooser: chooser,
f7eb47b… lmata 60 clearedBy: import_files.uploadFile.schema.name
f7eb47b… lmata 61 });
f7eb47b… lmata 62 });
f7eb47b… lmata 63 page.on("dialog", (dialog) => this._dialogShown(dialog));
f7eb47b… lmata 64 page.on("download", (download) => {
f7eb47b… lmata 65 void this._downloadStarted(download);
f7eb47b… lmata 66 });
f7eb47b… lmata 67 page.setDefaultNavigationTimeout(this.context.config.timeouts.navigation);
f7eb47b… lmata 68 page.setDefaultTimeout(this.context.config.timeouts.action);
f7eb47b… lmata 69 page[tabSymbol] = this;
f7eb47b… lmata 70 this._initializedPromise = this._initialize();
f7eb47b… lmata 71 }
f7eb47b… lmata 72 static forPage(page) {
f7eb47b… lmata 73 return page[tabSymbol];
f7eb47b… lmata 74 }
f7eb47b… lmata 75 static async collectConsoleMessages(page) {
f7eb47b… lmata 76 const result = [];
f7eb47b… lmata 77 const messages = await page.consoleMessages().catch(() => []);
f7eb47b… lmata 78 for (const message of messages)
f7eb47b… lmata 79 result.push(messageToConsoleMessage(message));
f7eb47b… lmata 80 const errors = await page.pageErrors().catch(() => []);
f7eb47b… lmata 81 for (const error of errors)
f7eb47b… lmata 82 result.push(pageErrorToConsoleMessage(error));
f7eb47b… lmata 83 return result;
f7eb47b… lmata 84 }
f7eb47b… lmata 85 async _initialize() {
f7eb47b… lmata 86 for (const message of await Tab.collectConsoleMessages(this.page))
f7eb47b… lmata 87 this._handleConsoleMessage(message);
f7eb47b… lmata 88 const requests = await this.page.requests().catch(() => []);
f7eb47b… lmata 89 for (const request of requests)
f7eb47b… lmata 90 this._requests.add(request);
f7eb47b… lmata 91 for (const initPage of this.context.config.browser.initPage || []) {
f7eb47b… lmata 92 try {
f7eb47b… lmata 93 const { default: func } = await (0, import_transform.requireOrImport)(initPage);
f7eb47b… lmata 94 await func({ page: this.page });
f7eb47b… lmata 95 } catch (e) {
f7eb47b… lmata 96 (0, import_log.logUnhandledError)(e);
f7eb47b… lmata 97 }
f7eb47b… lmata 98 }
f7eb47b… lmata 99 }
f7eb47b… lmata 100 modalStates() {
f7eb47b… lmata 101 return this._modalStates;
f7eb47b… lmata 102 }
f7eb47b… lmata 103 setModalState(modalState) {
f7eb47b… lmata 104 this._modalStates.push(modalState);
f7eb47b… lmata 105 this.emit(TabEvents.modalState, modalState);
f7eb47b… lmata 106 }
f7eb47b… lmata 107 clearModalState(modalState) {
f7eb47b… lmata 108 this._modalStates = this._modalStates.filter((state) => state !== modalState);
f7eb47b… lmata 109 }
f7eb47b… lmata 110 _dialogShown(dialog) {
f7eb47b… lmata 111 this.setModalState({
f7eb47b… lmata 112 type: "dialog",
f7eb47b… lmata 113 description: `"${dialog.type()}" dialog with message "${dialog.message()}"`,
f7eb47b… lmata 114 dialog,
f7eb47b… lmata 115 clearedBy: import_dialogs.handleDialog.schema.name
f7eb47b… lmata 116 });
f7eb47b… lmata 117 }
f7eb47b… lmata 118 async _downloadStarted(download) {
f7eb47b… lmata 119 const entry = {
f7eb47b… lmata 120 download,
f7eb47b… lmata 121 finished: false,
f7eb47b… lmata 122 outputFile: await this.context.outputFile(download.suggestedFilename(), { origin: "web", title: "Saving download" })
f7eb47b… lmata 123 };
f7eb47b… lmata 124 this._downloads.push(entry);
f7eb47b… lmata 125 this._addLogEntry({ type: "download-start", wallTime: Date.now(), download: entry });
f7eb47b… lmata 126 await download.saveAs(entry.outputFile);
f7eb47b… lmata 127 entry.finished = true;
f7eb47b… lmata 128 this._addLogEntry({ type: "download-finish", wallTime: Date.now(), download: entry });
f7eb47b… lmata 129 }
f7eb47b… lmata 130 _clearCollectedArtifacts() {
f7eb47b… lmata 131 this._consoleMessages.length = 0;
f7eb47b… lmata 132 this._downloads.length = 0;
f7eb47b… lmata 133 this._requests.clear();
f7eb47b… lmata 134 this._eventEntries.length = 0;
f7eb47b… lmata 135 this._recentEventEntries.length = 0;
f7eb47b… lmata 136 }
f7eb47b… lmata 137 _handleRequest(request) {
f7eb47b… lmata 138 this._requests.add(request);
f7eb47b… lmata 139 this._addLogEntry({ type: "request", wallTime: Date.now(), request });
f7eb47b… lmata 140 }
f7eb47b… lmata 141 _handleConsoleMessage(message) {
f7eb47b… lmata 142 this._consoleMessages.push(message);
f7eb47b… lmata 143 this._addLogEntry({ type: "console", wallTime: Date.now(), message });
f7eb47b… lmata 144 }
f7eb47b… lmata 145 _addLogEntry(entry) {
f7eb47b… lmata 146 this._eventEntries.push(entry);
f7eb47b… lmata 147 this._recentEventEntries.push(entry);
f7eb47b… lmata 148 }
f7eb47b… lmata 149 _onClose() {
f7eb47b… lmata 150 this._clearCollectedArtifacts();
f7eb47b… lmata 151 this._onPageClose(this);
f7eb47b… lmata 152 }
f7eb47b… lmata 153 async headerSnapshot() {
f7eb47b… lmata 154 let title;
f7eb47b… lmata 155 await this._raceAgainstModalStates(async () => {
f7eb47b… lmata 156 title = await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.title());
f7eb47b… lmata 157 });
f7eb47b… lmata 158 if (this._lastHeader.title !== title || this._lastHeader.url !== this.page.url() || this._lastHeader.current !== this.isCurrentTab()) {
f7eb47b… lmata 159 this._lastHeader = { title: title ?? "", url: this.page.url(), current: this.isCurrentTab() };
f7eb47b… lmata 160 return { ...this._lastHeader, changed: true };
f7eb47b… lmata 161 }
f7eb47b… lmata 162 return { ...this._lastHeader, changed: false };
f7eb47b… lmata 163 }
f7eb47b… lmata 164 isCurrentTab() {
f7eb47b… lmata 165 return this === this.context.currentTab();
f7eb47b… lmata 166 }
f7eb47b… lmata 167 async waitForLoadState(state, options) {
f7eb47b… lmata 168 await this._initializedPromise;
f7eb47b… lmata 169 await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => page.waitForLoadState(state, options).catch(import_log.logUnhandledError));
f7eb47b… lmata 170 }
f7eb47b… lmata 171 async navigate(url) {
f7eb47b… lmata 172 await this._initializedPromise;
f7eb47b… lmata 173 this._clearCollectedArtifacts();
f7eb47b… lmata 174 const { promise: downloadEvent, abort: abortDownloadEvent } = (0, import_utils2.eventWaiter)(this.page, "download", 3e3);
f7eb47b… lmata 175 try {
f7eb47b… lmata 176 await this.page.goto(url, { waitUntil: "domcontentloaded" });
f7eb47b… lmata 177 abortDownloadEvent();
f7eb47b… lmata 178 } catch (_e) {
f7eb47b… lmata 179 const e = _e;
f7eb47b… lmata 180 const mightBeDownload = e.message.includes("net::ERR_ABORTED") || e.message.includes("Download is starting");
f7eb47b… lmata 181 if (!mightBeDownload)
f7eb47b… lmata 182 throw e;
f7eb47b… lmata 183 const download = await downloadEvent;
f7eb47b… lmata 184 if (!download)
f7eb47b… lmata 185 throw e;
f7eb47b… lmata 186 await new Promise((resolve) => setTimeout(resolve, 500));
f7eb47b… lmata 187 return;
f7eb47b… lmata 188 }
f7eb47b… lmata 189 await this.waitForLoadState("load", { timeout: 5e3 });
f7eb47b… lmata 190 }
f7eb47b… lmata 191 async consoleMessages(level) {
f7eb47b… lmata 192 await this._initializedPromise;
f7eb47b… lmata 193 return this._consoleMessages.filter((message) => shouldIncludeMessage(level, message.type));
f7eb47b… lmata 194 }
f7eb47b… lmata 195 async requests() {
f7eb47b… lmata 196 await this._initializedPromise;
f7eb47b… lmata 197 return this._requests;
f7eb47b… lmata 198 }
f7eb47b… lmata 199 async captureSnapshot() {
f7eb47b… lmata 200 await this._initializedPromise;
f7eb47b… lmata 201 let tabSnapshot;
f7eb47b… lmata 202 const modalStates = await this._raceAgainstModalStates(async () => {
f7eb47b… lmata 203 const snapshot = await this.page._snapshotForAI({ track: "response" });
f7eb47b… lmata 204 tabSnapshot = {
f7eb47b… lmata 205 ariaSnapshot: snapshot.full,
f7eb47b… lmata 206 ariaSnapshotDiff: this._needsFullSnapshot ? void 0 : snapshot.incremental,
f7eb47b… lmata 207 modalStates: [],
f7eb47b… lmata 208 events: []
f7eb47b… lmata 209 };
f7eb47b… lmata 210 });
f7eb47b… lmata 211 if (tabSnapshot) {
f7eb47b… lmata 212 tabSnapshot.events = this._recentEventEntries;
f7eb47b… lmata 213 this._recentEventEntries = [];
f7eb47b… lmata 214 }
f7eb47b… lmata 215 this._needsFullSnapshot = !tabSnapshot;
f7eb47b… lmata 216 return tabSnapshot ?? {
f7eb47b… lmata 217 ariaSnapshot: "",
f7eb47b… lmata 218 ariaSnapshotDiff: "",
f7eb47b… lmata 219 modalStates,
f7eb47b… lmata 220 events: []
f7eb47b… lmata 221 };
f7eb47b… lmata 222 }
f7eb47b… lmata 223 _javaScriptBlocked() {
f7eb47b… lmata 224 return this._modalStates.some((state) => state.type === "dialog");
f7eb47b… lmata 225 }
f7eb47b… lmata 226 async _raceAgainstModalStates(action) {
f7eb47b… lmata 227 if (this.modalStates().length)
f7eb47b… lmata 228 return this.modalStates();
f7eb47b… lmata 229 const promise = new import_utils.ManualPromise();
f7eb47b… lmata 230 const listener = (modalState) => promise.resolve([modalState]);
f7eb47b… lmata 231 this.once(TabEvents.modalState, listener);
f7eb47b… lmata 232 return await Promise.race([
f7eb47b… lmata 233 action().then(() => {
f7eb47b… lmata 234 this.off(TabEvents.modalState, listener);
f7eb47b… lmata 235 return [];
f7eb47b… lmata 236 }),
f7eb47b… lmata 237 promise
f7eb47b… lmata 238 ]);
f7eb47b… lmata 239 }
f7eb47b… lmata 240 async waitForCompletion(callback) {
f7eb47b… lmata 241 await this._initializedPromise;
f7eb47b… lmata 242 await this._raceAgainstModalStates(() => (0, import_utils2.waitForCompletion)(this, callback));
f7eb47b… lmata 243 }
f7eb47b… lmata 244 async refLocator(params) {
f7eb47b… lmata 245 await this._initializedPromise;
f7eb47b… lmata 246 return (await this.refLocators([params]))[0];
f7eb47b… lmata 247 }
f7eb47b… lmata 248 async refLocators(params) {
f7eb47b… lmata 249 await this._initializedPromise;
f7eb47b… lmata 250 return Promise.all(params.map(async (param) => {
f7eb47b… lmata 251 try {
f7eb47b… lmata 252 let locator = this.page.locator(`aria-ref=${param.ref}`);
f7eb47b… lmata 253 if (param.element)
f7eb47b… lmata 254 locator = locator.describe(param.element);
f7eb47b… lmata 255 const { resolvedSelector } = await locator._resolveSelector();
f7eb47b… lmata 256 return { locator, resolved: (0, import_utils.asLocator)("javascript", resolvedSelector) };
f7eb47b… lmata 257 } catch (e) {
f7eb47b… lmata 258 throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
f7eb47b… lmata 259 }
f7eb47b… lmata 260 }));
f7eb47b… lmata 261 }
f7eb47b… lmata 262 async waitForTimeout(time) {
f7eb47b… lmata 263 if (this._javaScriptBlocked()) {
f7eb47b… lmata 264 await new Promise((f) => setTimeout(f, time));
f7eb47b… lmata 265 return;
f7eb47b… lmata 266 }
f7eb47b… lmata 267 await (0, import_utils2.callOnPageNoTrace)(this.page, (page) => {
f7eb47b… lmata 268 return page.evaluate(() => new Promise((f) => setTimeout(f, 1e3))).catch(() => {
f7eb47b… lmata 269 });
f7eb47b… lmata 270 });
f7eb47b… lmata 271 }
f7eb47b… lmata 272 }
f7eb47b… lmata 273 function messageToConsoleMessage(message) {
f7eb47b… lmata 274 return {
f7eb47b… lmata 275 type: message.type(),
f7eb47b… lmata 276 text: message.text(),
f7eb47b… lmata 277 toString: () => `[${message.type().toUpperCase()}] ${message.text()} @ ${message.location().url}:${message.location().lineNumber}`
f7eb47b… lmata 278 };
f7eb47b… lmata 279 }
f7eb47b… lmata 280 function pageErrorToConsoleMessage(errorOrValue) {
f7eb47b… lmata 281 if (errorOrValue instanceof Error) {
f7eb47b… lmata 282 return {
f7eb47b… lmata 283 type: "error",
f7eb47b… lmata 284 text: errorOrValue.message,
f7eb47b… lmata 285 toString: () => errorOrValue.stack || errorOrValue.message
f7eb47b… lmata 286 };
f7eb47b… lmata 287 }
f7eb47b… lmata 288 return {
f7eb47b… lmata 289 type: "error",
f7eb47b… lmata 290 text: String(errorOrValue),
f7eb47b… lmata 291 toString: () => String(errorOrValue)
f7eb47b… lmata 292 };
f7eb47b… lmata 293 }
f7eb47b… lmata 294 function renderModalStates(modalStates) {
f7eb47b… lmata 295 const result = [];
f7eb47b… lmata 296 if (modalStates.length === 0)
f7eb47b… lmata 297 result.push("- There is no modal state present");
f7eb47b… lmata 298 for (const state of modalStates)
f7eb47b… lmata 299 result.push(`- [${state.description}]: can be handled by the "${state.clearedBy}" tool`);
f7eb47b… lmata 300 return result;
f7eb47b… lmata 301 }
f7eb47b… lmata 302 const consoleMessageLevels = ["error", "warning", "info", "debug"];
f7eb47b… lmata 303 function shouldIncludeMessage(thresholdLevel, type) {
f7eb47b… lmata 304 const messageLevel = consoleLevelForMessageType(type);
f7eb47b… lmata 305 return consoleMessageLevels.indexOf(messageLevel) <= consoleMessageLevels.indexOf(thresholdLevel);
f7eb47b… lmata 306 }
f7eb47b… lmata 307 function consoleLevelForMessageType(type) {
f7eb47b… lmata 308 switch (type) {
f7eb47b… lmata 309 case "assert":
f7eb47b… lmata 310 case "error":
f7eb47b… lmata 311 return "error";
f7eb47b… lmata 312 case "warning":
f7eb47b… lmata 313 return "warning";
f7eb47b… lmata 314 case "count":
f7eb47b… lmata 315 case "dir":
f7eb47b… lmata 316 case "dirxml":
f7eb47b… lmata 317 case "info":
f7eb47b… lmata 318 case "log":
f7eb47b… lmata 319 case "table":
f7eb47b… lmata 320 case "time":
f7eb47b… lmata 321 case "timeEnd":
f7eb47b… lmata 322 return "info";
f7eb47b… lmata 323 case "clear":
f7eb47b… lmata 324 case "debug":
f7eb47b… lmata 325 case "endGroup":
f7eb47b… lmata 326 case "profile":
f7eb47b… lmata 327 case "profileEnd":
f7eb47b… lmata 328 case "startGroup":
f7eb47b… lmata 329 case "startGroupCollapsed":
f7eb47b… lmata 330 case "trace":
f7eb47b… lmata 331 return "debug";
f7eb47b… lmata 332 default:
f7eb47b… lmata 333 return "info";
f7eb47b… lmata 334 }
f7eb47b… lmata 335 }
f7eb47b… lmata 336 const tabSymbol = Symbol("tabSymbol");
f7eb47b… lmata 337 // Annotate the CommonJS export names for ESM import in node:
f7eb47b… lmata 338 0 && (module.exports = {
f7eb47b… lmata 339 Tab,
f7eb47b… lmata 340 TabEvents,
f7eb47b… lmata 341 renderModalStates,
f7eb47b… lmata 342 shouldIncludeMessage
f7eb47b… lmata 343 });

Keyboard Shortcuts

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