playwright-core 1.54.0-alpha-2025-06-20 → 1.54.0-alpha-1750421372000
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/playwright.js +2 -2
- package/lib/inProcessFactory.js +2 -2
- package/lib/protocol/validator.js +2 -2
- package/lib/remote/playwrightConnection.js +27 -168
- package/lib/remote/playwrightServer.js +173 -27
- package/lib/server/android/android.js +31 -25
- package/lib/server/bidi/bidiChromium.js +1 -1
- package/lib/server/bidi/bidiFirefox.js +4 -1
- package/lib/server/browser.js +13 -12
- package/lib/server/browserContext.js +49 -46
- package/lib/server/browserType.js +26 -19
- package/lib/server/chromium/chromium.js +9 -7
- package/lib/server/chromium/videoRecorder.js +2 -1
- package/lib/server/debugController.js +1 -1
- package/lib/server/dispatchers/androidDispatcher.js +4 -4
- package/lib/server/dispatchers/browserContextDispatcher.js +1 -1
- package/lib/server/dispatchers/browserDispatcher.js +2 -2
- package/lib/server/dispatchers/electronDispatcher.js +2 -2
- package/lib/server/dispatchers/frameDispatcher.js +1 -1
- package/lib/server/dispatchers/localUtilsDispatcher.js +1 -2
- package/lib/server/dispatchers/playwrightDispatcher.js +5 -21
- package/lib/server/dom.js +3 -4
- package/lib/server/electron/electron.js +15 -16
- package/lib/server/fetch.js +12 -26
- package/lib/server/firefox/ffPage.js +0 -1
- package/lib/server/frames.js +93 -92
- package/lib/server/helper.js +1 -1
- package/lib/server/page.js +48 -42
- package/lib/server/playwright.js +2 -2
- package/lib/server/recorder/recorderApp.js +1 -1
- package/lib/server/registry/index.js +7 -20
- package/lib/server/screenshotter.js +17 -31
- package/lib/server/socksClientCertificatesInterceptor.js +7 -3
- package/lib/server/trace/viewer/traceViewer.js +1 -1
- package/lib/server/transport.js +3 -7
- package/lib/server/utils/processLauncher.js +1 -1
- package/lib/vite/htmlReport/index.html +9 -9
- package/package.json +1 -1
|
@@ -126,22 +126,20 @@ class ElectronApplication extends import_instrumentation.SdkObject {
|
|
|
126
126
|
class Electron extends import_instrumentation.SdkObject {
|
|
127
127
|
constructor(playwright) {
|
|
128
128
|
super(playwright, "electron");
|
|
129
|
+
this.logName = "browser";
|
|
129
130
|
}
|
|
130
|
-
async launch(options) {
|
|
131
|
-
const
|
|
132
|
-
args = []
|
|
133
|
-
} = options;
|
|
134
|
-
const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this);
|
|
135
|
-
controller.setLogName("browser");
|
|
131
|
+
async launch(metadata, options) {
|
|
132
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
136
133
|
return controller.run(async (progress) => {
|
|
137
134
|
let app = void 0;
|
|
138
|
-
let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...args];
|
|
135
|
+
let electronArguments = ["--inspect=0", "--remote-debugging-port=0", ...options.args || []];
|
|
139
136
|
if (import_os.default.platform() === "linux") {
|
|
140
137
|
const runningAsRoot = process.geteuid && process.geteuid() === 0;
|
|
141
138
|
if (runningAsRoot && electronArguments.indexOf("--no-sandbox") === -1)
|
|
142
139
|
electronArguments.unshift("--no-sandbox");
|
|
143
140
|
}
|
|
144
|
-
const artifactsDir = await import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER);
|
|
141
|
+
const artifactsDir = await progress.race(import_fs.default.promises.mkdtemp(ARTIFACTS_FOLDER));
|
|
142
|
+
progress.cleanupWhenAborted(() => (0, import_utils.removeFolders)([artifactsDir]));
|
|
145
143
|
const browserLogsCollector = new import_debugLogger.RecentLogsCollector();
|
|
146
144
|
const env = options.env ? (0, import_processLauncher.envArrayToObject)(options.env) : process.env;
|
|
147
145
|
let command;
|
|
@@ -186,8 +184,8 @@ class Electron extends import_instrumentation.SdkObject {
|
|
|
186
184
|
handleSIGHUP: true,
|
|
187
185
|
onExit: () => app?.emit(ElectronApplication.Events.Close)
|
|
188
186
|
});
|
|
189
|
-
const waitForXserverError =
|
|
190
|
-
|
|
187
|
+
const waitForXserverError = waitForLine(progress, launchedProcess, /Unable to open X display/).then(() => {
|
|
188
|
+
throw new Error([
|
|
191
189
|
"Unable to open X display!",
|
|
192
190
|
`================================`,
|
|
193
191
|
"Most likely this is because there is no X server available.",
|
|
@@ -195,14 +193,14 @@ class Electron extends import_instrumentation.SdkObject {
|
|
|
195
193
|
"For example: 'xvfb-run npm run test:e2e'",
|
|
196
194
|
`================================`,
|
|
197
195
|
progress.metadata.log
|
|
198
|
-
].join("\n"))
|
|
199
|
-
});
|
|
196
|
+
].join("\n"));
|
|
200
197
|
});
|
|
201
198
|
const nodeMatchPromise = waitForLine(progress, launchedProcess, /^Debugger listening on (ws:\/\/.*)$/);
|
|
202
199
|
const chromeMatchPromise = waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
|
|
203
200
|
const debuggerDisconnectPromise = waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
|
|
204
201
|
const nodeMatch = await nodeMatchPromise;
|
|
205
202
|
const nodeTransport = await import_transport.WebSocketTransport.connect(progress, nodeMatch[1]);
|
|
203
|
+
progress.cleanupWhenAborted(() => nodeTransport.close());
|
|
206
204
|
const nodeConnection = new import_crConnection.CRConnection(this, nodeTransport, import_helper.helper.debugProtocolLogger(), browserLogsCollector);
|
|
207
205
|
debuggerDisconnectPromise.then(() => {
|
|
208
206
|
nodeTransport.close();
|
|
@@ -213,6 +211,7 @@ class Electron extends import_instrumentation.SdkObject {
|
|
|
213
211
|
waitForXserverError
|
|
214
212
|
]);
|
|
215
213
|
const chromeTransport = await import_transport.WebSocketTransport.connect(progress, chromeMatch[1]);
|
|
214
|
+
progress.cleanupWhenAborted(() => chromeTransport.close());
|
|
216
215
|
const browserProcess = {
|
|
217
216
|
onclose: void 0,
|
|
218
217
|
process: launchedProcess,
|
|
@@ -237,15 +236,15 @@ class Electron extends import_instrumentation.SdkObject {
|
|
|
237
236
|
originalLaunchOptions: { timeout: options.timeout }
|
|
238
237
|
};
|
|
239
238
|
(0, import_browserContext.validateBrowserContextOptions)(contextOptions, browserOptions);
|
|
240
|
-
const browser = await import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
|
|
239
|
+
const browser = await progress.race(import_crBrowser.CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
|
|
241
240
|
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
|
|
242
|
-
await app.initialize();
|
|
241
|
+
await progress.race(app.initialize());
|
|
243
242
|
return app;
|
|
244
243
|
}, options.timeout);
|
|
245
244
|
}
|
|
246
245
|
}
|
|
247
246
|
function waitForLine(progress, process2, regex) {
|
|
248
|
-
return new Promise((resolve, reject) => {
|
|
247
|
+
return progress.race(new Promise((resolve, reject) => {
|
|
249
248
|
const rl = readline.createInterface({ input: process2.stderr });
|
|
250
249
|
const failError = new Error("Process failed to launch!");
|
|
251
250
|
const listeners = [
|
|
@@ -266,7 +265,7 @@ function waitForLine(progress, process2, regex) {
|
|
|
266
265
|
function cleanup() {
|
|
267
266
|
import_eventsHelper.eventsHelper.removeEventListeners(listeners);
|
|
268
267
|
}
|
|
269
|
-
});
|
|
268
|
+
}));
|
|
270
269
|
}
|
|
271
270
|
// Annotate the CommonJS export names for ESM import in node:
|
|
272
271
|
0 && (module.exports = {
|
package/lib/server/fetch.js
CHANGED
|
@@ -122,15 +122,11 @@ class APIRequestContext extends import_instrumentation.SdkObject {
|
|
|
122
122
|
agent = (0, import_utils.createProxyAgent)(proxy, requestUrl);
|
|
123
123
|
let maxRedirects = params.maxRedirects ?? (defaults.maxRedirects ?? 20);
|
|
124
124
|
maxRedirects = maxRedirects === 0 ? -1 : maxRedirects;
|
|
125
|
-
const timeout = params.timeout;
|
|
126
|
-
const deadline = timeout && (0, import_utils.monotonicTime)() + timeout;
|
|
127
125
|
const options = {
|
|
128
126
|
method,
|
|
129
127
|
headers,
|
|
130
128
|
agent,
|
|
131
129
|
maxRedirects,
|
|
132
|
-
timeout,
|
|
133
|
-
deadline,
|
|
134
130
|
...(0, import_socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, requestUrl.origin),
|
|
135
131
|
__testHookLookup: params.__testHookLookup
|
|
136
132
|
};
|
|
@@ -139,10 +135,10 @@ class APIRequestContext extends import_instrumentation.SdkObject {
|
|
|
139
135
|
const postData = serializePostData(params, headers);
|
|
140
136
|
if (postData)
|
|
141
137
|
setHeader(headers, "content-length", String(postData.byteLength));
|
|
142
|
-
const controller = new import_progress.ProgressController(metadata, this);
|
|
138
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
143
139
|
const fetchResponse = await controller.run((progress) => {
|
|
144
140
|
return this._sendRequestWithRetries(progress, requestUrl, options, postData, params.maxRetries);
|
|
145
|
-
}, timeout);
|
|
141
|
+
}, params.timeout);
|
|
146
142
|
const fetchUid = this._storeResponseBody(fetchResponse.body);
|
|
147
143
|
this.fetchLog.set(fetchUid, controller.metadata.log);
|
|
148
144
|
const failOnStatusCode = params.failOnStatusCode !== void 0 ? params.failOnStatusCode : !!defaults.failOnStatusCode;
|
|
@@ -182,10 +178,10 @@ ${text}`;
|
|
|
182
178
|
}
|
|
183
179
|
return cookies;
|
|
184
180
|
}
|
|
185
|
-
async _updateRequestCookieHeader(url, headers) {
|
|
181
|
+
async _updateRequestCookieHeader(progress, url, headers) {
|
|
186
182
|
if (getHeader(headers, "cookie") !== void 0)
|
|
187
183
|
return;
|
|
188
|
-
const contextCookies = await this._cookies(url);
|
|
184
|
+
const contextCookies = await progress.race(this._cookies(url));
|
|
189
185
|
const cookies = contextCookies.filter((c) => new import_cookieStore.Cookie(c).matches(url));
|
|
190
186
|
if (cookies.length) {
|
|
191
187
|
const valueArray = cookies.map((c) => `${c.name}=${c.value}`);
|
|
@@ -199,22 +195,24 @@ ${text}`;
|
|
|
199
195
|
try {
|
|
200
196
|
return await this._sendRequest(progress, url, options, postData);
|
|
201
197
|
} catch (e) {
|
|
198
|
+
if ((0, import_progress.isAbortError)(e))
|
|
199
|
+
throw e;
|
|
202
200
|
e = (0, import_socksClientCertificatesInterceptor.rewriteOpenSSLErrorIfNeeded)(e);
|
|
203
201
|
if (maxRetries === 0)
|
|
204
202
|
throw e;
|
|
205
|
-
if (i === maxRetries
|
|
203
|
+
if (i === maxRetries)
|
|
206
204
|
throw new Error(`Failed after ${i + 1} attempt(s): ${e}`);
|
|
207
205
|
if (e.code !== "ECONNRESET")
|
|
208
206
|
throw e;
|
|
209
207
|
progress.log(` Received ECONNRESET, will retry after ${backoff}ms.`);
|
|
210
|
-
await
|
|
208
|
+
await progress.wait(backoff);
|
|
211
209
|
backoff *= 2;
|
|
212
210
|
}
|
|
213
211
|
}
|
|
214
212
|
throw new Error("Unreachable");
|
|
215
213
|
}
|
|
216
214
|
async _sendRequest(progress, url, options, postData) {
|
|
217
|
-
await this._updateRequestCookieHeader(url, options.headers);
|
|
215
|
+
await this._updateRequestCookieHeader(progress, url, options.headers);
|
|
218
216
|
const requestCookies = getHeader(options.headers, "cookie")?.split(";").map((p) => {
|
|
219
217
|
const [name, value] = p.split("=").map((v) => v.trim());
|
|
220
218
|
return { name, value };
|
|
@@ -227,7 +225,7 @@ ${text}`;
|
|
|
227
225
|
postData
|
|
228
226
|
};
|
|
229
227
|
this.emit(APIRequestContext.Events.Request, requestEvent);
|
|
230
|
-
|
|
228
|
+
const resultPromise = new Promise((fulfill, reject) => {
|
|
231
229
|
const requestConstructor = (url.protocol === "https:" ? import_https.default : import_http.default).request;
|
|
232
230
|
const agent = options.agent || (url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent);
|
|
233
231
|
const requestOptions = { ...options, agent };
|
|
@@ -308,8 +306,6 @@ ${text}`;
|
|
|
308
306
|
headers,
|
|
309
307
|
agent: options.agent,
|
|
310
308
|
maxRedirects: options.maxRedirects - 1,
|
|
311
|
-
timeout: options.timeout,
|
|
312
|
-
deadline: options.deadline,
|
|
313
309
|
...(0, import_socksClientCertificatesInterceptor.getMatchingTLSOptionsForOrigin)(this._defaultOptions().clientCertificates, url.origin),
|
|
314
310
|
__testHookLookup: options.__testHookLookup
|
|
315
311
|
};
|
|
@@ -387,6 +383,7 @@ ${text}`;
|
|
|
387
383
|
body.on("end", notifyBodyFinished);
|
|
388
384
|
});
|
|
389
385
|
request.on("error", reject);
|
|
386
|
+
progress.cleanupWhenAborted(() => request.destroy());
|
|
390
387
|
listeners.push(
|
|
391
388
|
import_utils.eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
|
|
392
389
|
reject(new Error("Request context disposed."));
|
|
@@ -434,22 +431,11 @@ ${text}`;
|
|
|
434
431
|
for (const [name, value] of Object.entries(options.headers))
|
|
435
432
|
progress.log(` ${name}: ${value}`);
|
|
436
433
|
}
|
|
437
|
-
if (options.deadline) {
|
|
438
|
-
const rejectOnTimeout = () => {
|
|
439
|
-
reject(new Error(`Request timed out after ${options.timeout}ms`));
|
|
440
|
-
request.destroy();
|
|
441
|
-
};
|
|
442
|
-
const remaining = options.deadline - (0, import_utils.monotonicTime)();
|
|
443
|
-
if (remaining <= 0) {
|
|
444
|
-
rejectOnTimeout();
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
request.setTimeout(remaining, rejectOnTimeout);
|
|
448
|
-
}
|
|
449
434
|
if (postData)
|
|
450
435
|
request.write(postData);
|
|
451
436
|
request.end();
|
|
452
437
|
});
|
|
438
|
+
return progress.race(resultPromise);
|
|
453
439
|
}
|
|
454
440
|
_getHttpCredentials(url) {
|
|
455
441
|
if (!this._defaultOptions().httpCredentials?.origin || url.origin.toLowerCase() === this._defaultOptions().httpCredentials?.origin?.toLowerCase())
|
package/lib/server/frames.js
CHANGED
|
@@ -468,7 +468,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
468
468
|
this.emit(Frame.Events.RemoveLifecycle, "networkidle");
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
|
-
async raceNavigationAction(progress,
|
|
471
|
+
async raceNavigationAction(progress, action) {
|
|
472
472
|
return import_utils.LongStandingScope.raceMultiple([
|
|
473
473
|
this._detachedScope,
|
|
474
474
|
this._page.openScope
|
|
@@ -477,14 +477,14 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
477
477
|
const data = this._redirectedNavigations.get(e.documentId);
|
|
478
478
|
if (data) {
|
|
479
479
|
progress.log(`waiting for redirected navigation to "${data.url}"`);
|
|
480
|
-
return data.gotoPromise;
|
|
480
|
+
return progress.race(data.gotoPromise);
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
throw e;
|
|
484
484
|
}));
|
|
485
485
|
}
|
|
486
486
|
redirectNavigation(url, documentId, referer) {
|
|
487
|
-
const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this);
|
|
487
|
+
const controller = new import_progress.ProgressController((0, import_instrumentation.serverSideCallMetadata)(), this, "strict");
|
|
488
488
|
const data = {
|
|
489
489
|
url,
|
|
490
490
|
gotoPromise: controller.run((progress) => this.gotoImpl(progress, url, { referer }), 0)
|
|
@@ -493,10 +493,10 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
493
493
|
data.gotoPromise.finally(() => this._redirectedNavigations.delete(documentId));
|
|
494
494
|
}
|
|
495
495
|
async goto(metadata, url, options) {
|
|
496
|
-
const
|
|
497
|
-
const controller = new import_progress.ProgressController(metadata, this);
|
|
496
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
498
497
|
return controller.run((progress) => {
|
|
499
|
-
|
|
498
|
+
const constructedNavigationURL = (0, import_utils.constructURLBasedOnBaseURL)(this._page.browserContext._options.baseURL, url);
|
|
499
|
+
return this.raceNavigationAction(progress, async () => this.gotoImpl(progress, constructedNavigationURL, options));
|
|
500
500
|
}, options.timeout);
|
|
501
501
|
}
|
|
502
502
|
async gotoImpl(progress, url, options) {
|
|
@@ -514,7 +514,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
514
514
|
const navigationEvents = [];
|
|
515
515
|
const collectNavigations = (arg) => navigationEvents.push(arg);
|
|
516
516
|
this.on(Frame.Events.InternalNavigation, collectNavigations);
|
|
517
|
-
const navigateResult = await this._page.delegate.navigateFrame(this, url, referer).finally(
|
|
517
|
+
const navigateResult = await progress.race(this._page.delegate.navigateFrame(this, url, referer)).finally(
|
|
518
518
|
() => this.off(Frame.Events.InternalNavigation, collectNavigations)
|
|
519
519
|
);
|
|
520
520
|
let event;
|
|
@@ -543,7 +543,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
543
543
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
|
544
544
|
await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
|
|
545
545
|
const request = event.newDocument ? event.newDocument.request : void 0;
|
|
546
|
-
const response = request ? request._finalRequest().response() : null;
|
|
546
|
+
const response = request ? progress.race(request._finalRequest().response()) : null;
|
|
547
547
|
return response;
|
|
548
548
|
}
|
|
549
549
|
async _waitForNavigation(progress, requiresNewDocument, options) {
|
|
@@ -562,7 +562,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
562
562
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
|
563
563
|
await import_helper.helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e) => e === waitUntil).promise;
|
|
564
564
|
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : void 0;
|
|
565
|
-
return request ? request._finalRequest().response() : null;
|
|
565
|
+
return request ? progress.race(request._finalRequest().response()) : null;
|
|
566
566
|
}
|
|
567
567
|
async _waitForLoadState(progress, state) {
|
|
568
568
|
const waitUntil = verifyLifecycle("state", state);
|
|
@@ -714,31 +714,32 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
714
714
|
return retVal;
|
|
715
715
|
});
|
|
716
716
|
} catch (e) {
|
|
717
|
-
if (
|
|
717
|
+
if (this.isNonRetriableError(e))
|
|
718
718
|
throw e;
|
|
719
719
|
throw new Error(`Unable to retrieve content because the page is navigating and changing the content.`);
|
|
720
720
|
}
|
|
721
721
|
}
|
|
722
722
|
async setContent(metadata, html, options) {
|
|
723
|
-
const controller = new import_progress.ProgressController(metadata, this);
|
|
723
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
724
724
|
return controller.run(async (progress) => {
|
|
725
|
-
await this.raceNavigationAction(progress,
|
|
725
|
+
await this.raceNavigationAction(progress, async () => {
|
|
726
726
|
const waitUntil = options.waitUntil === void 0 ? "load" : options.waitUntil;
|
|
727
727
|
progress.log(`setting frame content, waiting until "${waitUntil}"`);
|
|
728
728
|
const tag = `--playwright--set--content--${this._id}--${++this._setContentCounter}--`;
|
|
729
|
-
const context = await this._utilityContext();
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
});
|
|
729
|
+
const context = await progress.race(this._utilityContext());
|
|
730
|
+
const tagPromise = new import_manualPromise.ManualPromise();
|
|
731
|
+
this._page.frameManager._consoleMessageTags.set(tag, () => {
|
|
732
|
+
this._onClearLifecycle();
|
|
733
|
+
tagPromise.resolve();
|
|
735
734
|
});
|
|
736
|
-
|
|
735
|
+
progress.cleanupWhenAborted(() => this._page.frameManager._consoleMessageTags.delete(tag));
|
|
736
|
+
const lifecyclePromise = progress.race(tagPromise).then(() => this._waitForLoadState(progress, waitUntil));
|
|
737
|
+
const contentPromise = progress.race(context.evaluate(({ html: html2, tag: tag2 }) => {
|
|
737
738
|
document.open();
|
|
738
739
|
console.debug(tag2);
|
|
739
740
|
document.write(html2);
|
|
740
741
|
document.close();
|
|
741
|
-
}, { html, tag });
|
|
742
|
+
}, { html, tag }));
|
|
742
743
|
await Promise.all([contentPromise, lifecyclePromise]);
|
|
743
744
|
return null;
|
|
744
745
|
});
|
|
@@ -884,13 +885,13 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
884
885
|
continue;
|
|
885
886
|
return result;
|
|
886
887
|
} catch (e) {
|
|
887
|
-
if (this.
|
|
888
|
+
if (this.isNonRetriableError(e))
|
|
888
889
|
throw e;
|
|
889
890
|
continue;
|
|
890
891
|
}
|
|
891
892
|
}
|
|
892
893
|
}
|
|
893
|
-
|
|
894
|
+
isNonRetriableError(e) {
|
|
894
895
|
if ((0, import_progress.isAbortError)(e))
|
|
895
896
|
return true;
|
|
896
897
|
if (js.isJavaScriptErrorInEvaluate(e) || (0, import_protocolError.isSessionClosedError)(e))
|
|
@@ -947,7 +948,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
947
948
|
}
|
|
948
949
|
async rafrafTimeoutScreenshotElementWithProgress(progress, selector, timeout, options) {
|
|
949
950
|
return await this._retryWithProgressIfNotConnected(progress, selector, true, true, async (handle) => {
|
|
950
|
-
await handle._frame.rafrafTimeout(timeout);
|
|
951
|
+
await handle._frame.rafrafTimeout(progress, timeout);
|
|
951
952
|
return await this._page.screenshotter.screenshotElement(progress, handle, options);
|
|
952
953
|
});
|
|
953
954
|
}
|
|
@@ -1081,7 +1082,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1081
1082
|
return state.matches;
|
|
1082
1083
|
}, { info: resolved.info, root: resolved.frame === this ? scope : void 0 }));
|
|
1083
1084
|
} catch (e) {
|
|
1084
|
-
if (
|
|
1085
|
+
if (this.isNonRetriableError(e))
|
|
1085
1086
|
throw e;
|
|
1086
1087
|
return false;
|
|
1087
1088
|
}
|
|
@@ -1149,9 +1150,9 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1149
1150
|
return controller.run((progress) => progress.wait(timeout));
|
|
1150
1151
|
}
|
|
1151
1152
|
async ariaSnapshot(metadata, selector, options) {
|
|
1152
|
-
const controller = new import_progress.ProgressController(metadata, this);
|
|
1153
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
1153
1154
|
return controller.run(async (progress) => {
|
|
1154
|
-
return await this._retryWithProgressIfNotConnected(progress, selector, true, true, (handle) => handle.ariaSnapshot(options));
|
|
1155
|
+
return await this._retryWithProgressIfNotConnected(progress, selector, true, true, (handle) => progress.race(handle.ariaSnapshot(options)));
|
|
1155
1156
|
}, options.timeout);
|
|
1156
1157
|
}
|
|
1157
1158
|
async expect(metadata, selector, options) {
|
|
@@ -1165,20 +1166,20 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1165
1166
|
try {
|
|
1166
1167
|
let timeout = options.timeout;
|
|
1167
1168
|
const start = timeout > 0 ? (0, import_utils.monotonicTime)() : 0;
|
|
1168
|
-
await new import_progress.ProgressController(metadata, this).run(async (progress) => {
|
|
1169
|
+
await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
|
|
1169
1170
|
progress.log(`${(0, import_utils.renderTitleForCall)(metadata)}${timeout ? ` with timeout ${timeout}ms` : ""}`);
|
|
1170
1171
|
if (selector)
|
|
1171
1172
|
progress.log(`waiting for ${this._asLocator(selector)}`);
|
|
1172
1173
|
await this._page.performActionPreChecks(progress);
|
|
1173
1174
|
}, timeout);
|
|
1174
1175
|
try {
|
|
1175
|
-
const resultOneShot = await new import_progress.ProgressController(metadata, this).run(async (progress) => {
|
|
1176
|
+
const resultOneShot = await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
|
|
1176
1177
|
return await this._expectInternal(progress, selector, options, lastIntermediateResult);
|
|
1177
1178
|
});
|
|
1178
1179
|
if (resultOneShot.matches !== options.isNot)
|
|
1179
1180
|
return resultOneShot;
|
|
1180
1181
|
} catch (e) {
|
|
1181
|
-
if (
|
|
1182
|
+
if (this.isNonRetriableError(e))
|
|
1182
1183
|
throw e;
|
|
1183
1184
|
}
|
|
1184
1185
|
if (timeout > 0) {
|
|
@@ -1187,7 +1188,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1187
1188
|
}
|
|
1188
1189
|
if (timeout < 0)
|
|
1189
1190
|
return { matches: options.isNot, log: (0, import_callLog.compressCallLog)(metadata.log), timedOut: true, received: lastIntermediateResult.received };
|
|
1190
|
-
return await new import_progress.ProgressController(metadata, this).run(async (progress) => {
|
|
1191
|
+
return await new import_progress.ProgressController(metadata, this, "strict").run(async (progress) => {
|
|
1191
1192
|
return await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
|
|
1192
1193
|
await this._page.performActionPreChecks(progress);
|
|
1193
1194
|
const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult);
|
|
@@ -1209,14 +1210,12 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1209
1210
|
}
|
|
1210
1211
|
}
|
|
1211
1212
|
async _expectInternal(progress, selector, options, lastIntermediateResult) {
|
|
1212
|
-
const selectorInFrame = selector ? await this.selectors.resolveFrameForSelector(selector, { strict: true }) : void 0;
|
|
1213
|
-
progress.throwIfAborted();
|
|
1213
|
+
const selectorInFrame = selector ? await progress.race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
|
|
1214
1214
|
const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
|
|
1215
1215
|
const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility";
|
|
1216
|
-
const context = await frame._context(world);
|
|
1217
|
-
const injected = await context.injectedScript();
|
|
1218
|
-
progress.
|
|
1219
|
-
const { log, matches, received, missingReceived } = await injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
|
|
1216
|
+
const context = await progress.race(frame._context(world));
|
|
1217
|
+
const injected = await progress.race(context.injectedScript());
|
|
1218
|
+
const { log, matches, received, missingReceived } = await progress.race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
|
|
1220
1219
|
const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
|
|
1221
1220
|
if (callId)
|
|
1222
1221
|
injected2.markTargetElements(new Set(elements), callId);
|
|
@@ -1229,7 +1228,7 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1229
1228
|
else if (elements.length)
|
|
1230
1229
|
log2 = ` locator resolved to ${injected2.previewNode(elements[0])}`;
|
|
1231
1230
|
return { log: log2, ...await injected2.expect(elements[0], options2, elements) };
|
|
1232
|
-
}, { info, options, callId: progress.metadata.id });
|
|
1231
|
+
}, { info, options, callId: progress.metadata.id }));
|
|
1233
1232
|
if (log)
|
|
1234
1233
|
progress.log(log);
|
|
1235
1234
|
if (matches === options.isNot) {
|
|
@@ -1240,60 +1239,62 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1240
1239
|
}
|
|
1241
1240
|
return { matches, received };
|
|
1242
1241
|
}
|
|
1243
|
-
async
|
|
1244
|
-
const controller = new import_progress.ProgressController(metadata, this);
|
|
1242
|
+
async waitForFunctionExpression(metadata, expression, isFunction, arg, options) {
|
|
1243
|
+
const controller = new import_progress.ProgressController(metadata, this, "strict");
|
|
1244
|
+
return controller.run((progress) => this.waitForFunctionExpressionImpl(progress, expression, isFunction, arg, options, "main"), options.timeout);
|
|
1245
|
+
}
|
|
1246
|
+
async waitForFunctionExpressionImpl(progress, expression, isFunction, arg, options, world = "main") {
|
|
1245
1247
|
if (typeof options.pollingInterval === "number")
|
|
1246
1248
|
(0, import_utils.assert)(options.pollingInterval > 0, "Cannot poll with non-positive interval: " + options.pollingInterval);
|
|
1247
1249
|
expression = js.normalizeEvaluationExpression(expression, isFunction);
|
|
1248
|
-
return
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1250
|
+
return this.retryWithProgressAndTimeouts(progress, [100], async () => {
|
|
1251
|
+
const context = world === "main" ? await progress.race(this._mainContext()) : await progress.race(this._utilityContext());
|
|
1252
|
+
const injectedScript = await progress.race(context.injectedScript());
|
|
1253
|
+
const handle = await progress.race(injectedScript.evaluateHandle((injected, { expression: expression2, isFunction: isFunction2, polling, arg: arg2 }) => {
|
|
1254
|
+
const predicate = () => {
|
|
1255
|
+
let result3 = globalThis.eval(expression2);
|
|
1256
|
+
if (isFunction2 === true) {
|
|
1257
|
+
result3 = result3(arg2);
|
|
1258
|
+
} else if (isFunction2 === false) {
|
|
1259
|
+
result3 = result3;
|
|
1260
|
+
} else {
|
|
1261
|
+
if (typeof result3 === "function")
|
|
1262
|
+
result3 = result3(arg2);
|
|
1263
|
+
}
|
|
1264
|
+
return result3;
|
|
1265
|
+
};
|
|
1266
|
+
let fulfill;
|
|
1267
|
+
let reject;
|
|
1268
|
+
let aborted = false;
|
|
1269
|
+
const result2 = new Promise((f, r) => {
|
|
1270
|
+
fulfill = f;
|
|
1271
|
+
reject = r;
|
|
1272
|
+
});
|
|
1273
|
+
const next = () => {
|
|
1274
|
+
if (aborted)
|
|
1275
|
+
return;
|
|
1276
|
+
try {
|
|
1277
|
+
const success = predicate();
|
|
1278
|
+
if (success) {
|
|
1279
|
+
fulfill(success);
|
|
1274
1280
|
return;
|
|
1275
|
-
try {
|
|
1276
|
-
const success = predicate();
|
|
1277
|
-
if (success) {
|
|
1278
|
-
fulfill(success);
|
|
1279
|
-
return;
|
|
1280
|
-
}
|
|
1281
|
-
if (typeof polling !== "number")
|
|
1282
|
-
injected.utils.builtins.requestAnimationFrame(next);
|
|
1283
|
-
else
|
|
1284
|
-
injected.utils.builtins.setTimeout(next, polling);
|
|
1285
|
-
} catch (e) {
|
|
1286
|
-
reject(e);
|
|
1287
1281
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1282
|
+
if (typeof polling !== "number")
|
|
1283
|
+
injected.utils.builtins.requestAnimationFrame(next);
|
|
1284
|
+
else
|
|
1285
|
+
injected.utils.builtins.setTimeout(next, polling);
|
|
1286
|
+
} catch (e) {
|
|
1287
|
+
reject(e);
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
next();
|
|
1291
|
+
return { result: result2, abort: () => aborted = true };
|
|
1292
|
+
}, { expression, isFunction, polling: options.pollingInterval, arg }));
|
|
1293
|
+
progress.cleanupWhenAborted(() => handle.evaluate((h) => h.abort()).finally(() => handle.dispose()));
|
|
1294
|
+
const result = await progress.race(handle.evaluateHandle((h) => h.result));
|
|
1295
|
+
handle.dispose();
|
|
1296
|
+
return result;
|
|
1297
|
+
});
|
|
1297
1298
|
}
|
|
1298
1299
|
async waitForFunctionValueInUtility(progress, pageFunction) {
|
|
1299
1300
|
const expression = `() => {
|
|
@@ -1302,25 +1303,25 @@ class Frame extends import_instrumentation.SdkObject {
|
|
|
1302
1303
|
return result;
|
|
1303
1304
|
return JSON.stringify(result);
|
|
1304
1305
|
}`;
|
|
1305
|
-
const handle = await this.
|
|
1306
|
+
const handle = await this.waitForFunctionExpressionImpl(progress, expression, true, void 0, {}, "utility");
|
|
1306
1307
|
return JSON.parse(handle.rawValue());
|
|
1307
1308
|
}
|
|
1308
1309
|
async title() {
|
|
1309
1310
|
const context = await this._utilityContext();
|
|
1310
1311
|
return context.evaluate(() => document.title);
|
|
1311
1312
|
}
|
|
1312
|
-
async rafrafTimeout(timeout) {
|
|
1313
|
+
async rafrafTimeout(progress, timeout) {
|
|
1313
1314
|
if (timeout === 0)
|
|
1314
1315
|
return;
|
|
1315
|
-
const context = await this._utilityContext();
|
|
1316
|
+
const context = await progress.race(this._utilityContext());
|
|
1316
1317
|
await Promise.all([
|
|
1317
1318
|
// wait for double raf
|
|
1318
|
-
context.evaluate(() => new Promise((x) => {
|
|
1319
|
+
progress.race(context.evaluate(() => new Promise((x) => {
|
|
1319
1320
|
requestAnimationFrame(() => {
|
|
1320
1321
|
requestAnimationFrame(x);
|
|
1321
1322
|
});
|
|
1322
|
-
})),
|
|
1323
|
-
|
|
1323
|
+
}))),
|
|
1324
|
+
progress.wait(timeout)
|
|
1324
1325
|
]);
|
|
1325
1326
|
}
|
|
1326
1327
|
_onDetached() {
|
package/lib/server/helper.js
CHANGED
|
@@ -66,7 +66,7 @@ class Helper {
|
|
|
66
66
|
});
|
|
67
67
|
const dispose = () => import_eventsHelper.eventsHelper.removeEventListeners(listeners);
|
|
68
68
|
progress.cleanupWhenAborted(dispose);
|
|
69
|
-
return { promise, dispose };
|
|
69
|
+
return { promise: progress.race(promise), dispose };
|
|
70
70
|
}
|
|
71
71
|
static secondsToRoundishMillis(value) {
|
|
72
72
|
return (value * 1e6 | 0) / 1e3;
|