1 | /**
|
2 | * @license
|
3 | * Copyright 2017 Google Inc.
|
4 | * SPDX-License-Identifier: Apache-2.0
|
5 | */
|
6 |
|
7 | import type {Protocol} from 'devtools-protocol';
|
8 |
|
9 | import {
|
10 | concat,
|
11 | EMPTY,
|
12 | filter,
|
13 | first,
|
14 | firstValueFrom,
|
15 | from,
|
16 | map,
|
17 | merge,
|
18 | mergeMap,
|
19 | mergeScan,
|
20 | of,
|
21 | raceWith,
|
22 | ReplaySubject,
|
23 | startWith,
|
24 | switchMap,
|
25 | take,
|
26 | takeUntil,
|
27 | timer,
|
28 | type Observable,
|
29 | } from '../../third_party/rxjs/rxjs.js';
|
30 | import type {HTTPRequest} from '../api/HTTPRequest.js';
|
31 | import type {HTTPResponse} from '../api/HTTPResponse.js';
|
32 | import type {Accessibility} from '../cdp/Accessibility.js';
|
33 | import type {Coverage} from '../cdp/Coverage.js';
|
34 | import type {DeviceRequestPrompt} from '../cdp/DeviceRequestPrompt.js';
|
35 | import type {NetworkConditions} from '../cdp/NetworkManager.js';
|
36 | import type {Tracing} from '../cdp/Tracing.js';
|
37 | import type {ConsoleMessage} from '../common/ConsoleMessage.js';
|
38 | import type {
|
39 | Cookie,
|
40 | CookieParam,
|
41 | DeleteCookiesRequest,
|
42 | } from '../common/Cookie.js';
|
43 | import type {Device} from '../common/Device.js';
|
44 | import {TargetCloseError} from '../common/Errors.js';
|
45 | import {
|
46 | EventEmitter,
|
47 | type EventsWithWildcard,
|
48 | type EventType,
|
49 | type Handler,
|
50 | } from '../common/EventEmitter.js';
|
51 | import type {FileChooser} from '../common/FileChooser.js';
|
52 | import type {PDFOptions} from '../common/PDFOptions.js';
|
53 | import {TimeoutSettings} from '../common/TimeoutSettings.js';
|
54 | import type {
|
55 | Awaitable,
|
56 | AwaitablePredicate,
|
57 | EvaluateFunc,
|
58 | EvaluateFuncWith,
|
59 | HandleFor,
|
60 | NodeFor,
|
61 | } from '../common/types.js';
|
62 | import {
|
63 | debugError,
|
64 | fromEmitterEvent,
|
65 | filterAsync,
|
66 | isString,
|
67 | NETWORK_IDLE_TIME,
|
68 | timeout,
|
69 | withSourcePuppeteerURLIfNone,
|
70 | fromAbortSignal,
|
71 | } from '../common/util.js';
|
72 | import type {Viewport} from '../common/Viewport.js';
|
73 | import {environment} from '../environment.js';
|
74 | import type {ScreenRecorder} from '../node/ScreenRecorder.js';
|
75 | import {guarded} from '../util/decorators.js';
|
76 | import {
|
77 | AsyncDisposableStack,
|
78 | asyncDisposeSymbol,
|
79 | DisposableStack,
|
80 | disposeSymbol,
|
81 | } from '../util/disposable.js';
|
82 | import {stringToTypedArray} from '../util/encoding.js';
|
83 |
|
84 | import type {Browser} from './Browser.js';
|
85 | import type {BrowserContext} from './BrowserContext.js';
|
86 | import type {CDPSession} from './CDPSession.js';
|
87 | import type {Dialog} from './Dialog.js';
|
88 | import type {
|
89 | BoundingBox,
|
90 | ClickOptions,
|
91 | ElementHandle,
|
92 | } from './ElementHandle.js';
|
93 | import type {
|
94 | Frame,
|
95 | FrameAddScriptTagOptions,
|
96 | FrameAddStyleTagOptions,
|
97 | FrameWaitForFunctionOptions,
|
98 | GoToOptions,
|
99 | WaitForOptions,
|
100 | } from './Frame.js';
|
101 | import type {
|
102 | Keyboard,
|
103 | KeyboardTypeOptions,
|
104 | Mouse,
|
105 | Touchscreen,
|
106 | } from './Input.js';
|
107 | import type {JSHandle} from './JSHandle.js';
|
108 | import {
|
109 | FunctionLocator,
|
110 | Locator,
|
111 | NodeLocator,
|
112 | type AwaitedLocator,
|
113 | } from './locators/locators.js';
|
114 | import type {Target} from './Target.js';
|
115 | import type {WebWorker} from './WebWorker.js';
|
116 |
|
117 | /**
|
118 | * @public
|
119 | */
|
120 | export interface Metrics {
|
121 | Timestamp?: number;
|
122 | Documents?: number;
|
123 | Frames?: number;
|
124 | JSEventListeners?: number;
|
125 | Nodes?: number;
|
126 | LayoutCount?: number;
|
127 | RecalcStyleCount?: number;
|
128 | LayoutDuration?: number;
|
129 | RecalcStyleDuration?: number;
|
130 | ScriptDuration?: number;
|
131 | TaskDuration?: number;
|
132 | JSHeapUsedSize?: number;
|
133 | JSHeapTotalSize?: number;
|
134 | }
|
135 |
|
136 | /**
|
137 | * @public
|
138 | */
|
139 | export interface Credentials {
|
140 | username: string;
|
141 | password: string;
|
142 | }
|
143 |
|
144 | /**
|
145 | * @public
|
146 | */
|
147 | export interface WaitForNetworkIdleOptions extends WaitTimeoutOptions {
|
148 | /**
|
149 | * Time (in milliseconds) the network should be idle.
|
150 | *
|
151 | * @defaultValue `500`
|
152 | */
|
153 | idleTime?: number;
|
154 | /**
|
155 | * Maximum number concurrent of network connections to be considered inactive.
|
156 | *
|
157 | * @defaultValue `0`
|
158 | */
|
159 | concurrency?: number;
|
160 | }
|
161 |
|
162 | /**
|
163 | * @public
|
164 | */
|
165 | export interface WaitTimeoutOptions {
|
166 | /**
|
167 | * Maximum wait time in milliseconds. Pass 0 to disable the timeout.
|
168 | *
|
169 | * The default value can be changed by using the
|
170 | * {@link Page.setDefaultTimeout} method.
|
171 | *
|
172 | * @defaultValue `30_000`
|
173 | */
|
174 | timeout?: number;
|
175 | /**
|
176 | * A signal object that allows you to cancel a waitFor call.
|
177 | */
|
178 | signal?: AbortSignal;
|
179 | }
|
180 |
|
181 | /**
|
182 | * @public
|
183 | */
|
184 | export interface WaitForSelectorOptions {
|
185 | /**
|
186 | * Wait for the selected element to be present in DOM and to be visible. See
|
187 | * {@link ElementHandle.isVisible} for the definition of element visibility.
|
188 | *
|
189 | * @defaultValue `false`
|
190 | */
|
191 | visible?: boolean;
|
192 | /**
|
193 | * Wait for the selected element to not be found in the DOM or to be hidden.
|
194 | * See {@link ElementHandle.isHidden} for the definition of element
|
195 | * invisibility.
|
196 | *
|
197 | * @defaultValue `false`
|
198 | */
|
199 | hidden?: boolean;
|
200 | /**
|
201 | * Maximum time to wait in milliseconds. Pass `0` to disable timeout.
|
202 | *
|
203 | * The default value can be changed by using {@link Page.setDefaultTimeout}
|
204 | *
|
205 | * @defaultValue `30_000` (30 seconds)
|
206 | */
|
207 | timeout?: number;
|
208 | /**
|
209 | * A signal object that allows you to cancel a waitForSelector call.
|
210 | */
|
211 | signal?: AbortSignal;
|
212 | }
|
213 |
|
214 | /**
|
215 | * @public
|
216 | */
|
217 | export interface GeolocationOptions {
|
218 | /**
|
219 | * Latitude between `-90` and `90`.
|
220 | */
|
221 | longitude: number;
|
222 | /**
|
223 | * Longitude between `-180` and `180`.
|
224 | */
|
225 | latitude: number;
|
226 | /**
|
227 | * Optional non-negative accuracy value.
|
228 | */
|
229 | accuracy?: number;
|
230 | }
|
231 |
|
232 | /**
|
233 | * A media feature to emulate.
|
234 | *
|
235 | * @public
|
236 | */
|
237 | export interface MediaFeature {
|
238 | /**
|
239 | * A name of the feature, for example, 'prefers-reduced-motion'.
|
240 | */
|
241 | name: string;
|
242 | /**
|
243 | * A value for the feature, for example, 'reduce'.
|
244 | */
|
245 | value: string;
|
246 | }
|
247 |
|
248 | /**
|
249 | * @public
|
250 | */
|
251 | export interface ScreenshotClip extends BoundingBox {
|
252 | /**
|
253 | * @defaultValue `1`
|
254 | */
|
255 | scale?: number;
|
256 | }
|
257 |
|
258 | /**
|
259 | * @public
|
260 | */
|
261 | export interface ScreenshotOptions {
|
262 | /**
|
263 | * @defaultValue `false`
|
264 | */
|
265 | optimizeForSpeed?: boolean;
|
266 | /**
|
267 | * @defaultValue `'png'`
|
268 | */
|
269 | type?: 'png' | 'jpeg' | 'webp';
|
270 | /**
|
271 | * Quality of the image, between 0-100. Not applicable to `png` images.
|
272 | */
|
273 | quality?: number;
|
274 | /**
|
275 | * Capture the screenshot from the surface, rather than the view.
|
276 | *
|
277 | * @defaultValue `true`
|
278 | */
|
279 | fromSurface?: boolean;
|
280 | /**
|
281 | * When `true`, takes a screenshot of the full page.
|
282 | *
|
283 | * @defaultValue `false`
|
284 | */
|
285 | fullPage?: boolean;
|
286 | /**
|
287 | * Hides default white background and allows capturing screenshots with transparency.
|
288 | *
|
289 | * @defaultValue `false`
|
290 | */
|
291 | omitBackground?: boolean;
|
292 | /**
|
293 | * The file path to save the image to. The screenshot type will be inferred
|
294 | * from file extension. If path is a relative path, then it is resolved
|
295 | * relative to current working directory. If no path is provided, the image
|
296 | * won't be saved to the disk.
|
297 | */
|
298 | path?: string;
|
299 | /**
|
300 | * Specifies the region of the page/element to clip.
|
301 | */
|
302 | clip?: ScreenshotClip;
|
303 | /**
|
304 | * Encoding of the image.
|
305 | *
|
306 | * @defaultValue `'binary'`
|
307 | */
|
308 | encoding?: 'base64' | 'binary';
|
309 | /**
|
310 | * Capture the screenshot beyond the viewport.
|
311 | *
|
312 | * @defaultValue `false` if there is no `clip`. `true` otherwise.
|
313 | */
|
314 | captureBeyondViewport?: boolean;
|
315 | }
|
316 |
|
317 | /**
|
318 | * @public
|
319 | * @experimental
|
320 | */
|
321 | export interface ScreencastOptions {
|
322 | /**
|
323 | * File path to save the screencast to.
|
324 | */
|
325 | path?: `${string}.webm`;
|
326 | /**
|
327 | * Specifies the region of the viewport to crop.
|
328 | */
|
329 | crop?: BoundingBox;
|
330 | /**
|
331 | * Scales the output video.
|
332 | *
|
333 | * For example, `0.5` will shrink the width and height of the output video by
|
334 | * half. `2` will double the width and height of the output video.
|
335 | *
|
336 | * @defaultValue `1`
|
337 | */
|
338 | scale?: number;
|
339 | /**
|
340 | * Specifies the speed to record at.
|
341 | *
|
342 | * For example, `0.5` will slowdown the output video by 50%. `2` will double the
|
343 | * speed of the output video.
|
344 | *
|
345 | * @defaultValue `1`
|
346 | */
|
347 | speed?: number;
|
348 | /**
|
349 | * Path to the {@link https://ffmpeg.org/ | ffmpeg}.
|
350 | *
|
351 | * Required if `ffmpeg` is not in your PATH.
|
352 | */
|
353 | ffmpegPath?: string;
|
354 | }
|
355 |
|
356 | /**
|
357 | * @public
|
358 | */
|
359 | export interface QueryOptions {
|
360 | /**
|
361 | * Whether to run the query in isolation. When returning many elements
|
362 | * from {@link Page.$$} or similar methods, it might be useful to turn
|
363 | * off the isolation to improve performance. By default, the querying
|
364 | * code will be executed in a separate sandbox realm.
|
365 | *
|
366 | * @defaultValue `true`
|
367 | */
|
368 | isolate: boolean;
|
369 | }
|
370 |
|
371 | /**
|
372 | * All the events that a page instance may emit.
|
373 | *
|
374 | * @public
|
375 | */
|
376 | export const enum PageEvent {
|
377 | /**
|
378 | * Emitted when the page closes.
|
379 | */
|
380 | Close = 'close',
|
381 | /**
|
382 | * Emitted when JavaScript within the page calls one of console API methods,
|
383 | * e.g. `console.log` or `console.dir`. Also emitted if the page throws an
|
384 | * error or a warning.
|
385 | *
|
386 | * @remarks
|
387 | * A `console` event provides a {@link ConsoleMessage} representing the
|
388 | * console message that was logged.
|
389 | *
|
390 | * @example
|
391 | * An example of handling `console` event:
|
392 | *
|
393 | * ```ts
|
394 | * page.on('console', msg => {
|
395 | * for (let i = 0; i < msg.args().length; ++i)
|
396 | * console.log(`${i}: ${msg.args()[i]}`);
|
397 | * });
|
398 | * page.evaluate(() => console.log('hello', 5, {foo: 'bar'}));
|
399 | * ```
|
400 | */
|
401 | Console = 'console',
|
402 | /**
|
403 | * Emitted when a JavaScript dialog appears, such as `alert`, `prompt`,
|
404 | * `confirm` or `beforeunload`. Puppeteer can respond to the dialog via
|
405 | * {@link Dialog.accept} or {@link Dialog.dismiss}.
|
406 | */
|
407 | Dialog = 'dialog',
|
408 | /**
|
409 | * Emitted when the JavaScript
|
410 | * {@link https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded | DOMContentLoaded }
|
411 | * event is dispatched.
|
412 | */
|
413 | DOMContentLoaded = 'domcontentloaded',
|
414 | /**
|
415 | * Emitted when the page crashes. Will contain an `Error`.
|
416 | */
|
417 | Error = 'error',
|
418 | /** Emitted when a frame is attached. Will contain a {@link Frame}. */
|
419 | FrameAttached = 'frameattached',
|
420 | /** Emitted when a frame is detached. Will contain a {@link Frame}. */
|
421 | FrameDetached = 'framedetached',
|
422 | /**
|
423 | * Emitted when a frame is navigated to a new URL. Will contain a
|
424 | * {@link Frame}.
|
425 | */
|
426 | FrameNavigated = 'framenavigated',
|
427 | /**
|
428 | * Emitted when the JavaScript
|
429 | * {@link https://developer.mozilla.org/en-US/docs/Web/Events/load | load}
|
430 | * event is dispatched.
|
431 | */
|
432 | Load = 'load',
|
433 | /**
|
434 | * Emitted when the JavaScript code makes a call to `console.timeStamp`. For
|
435 | * the list of metrics see {@link Page.metrics | page.metrics}.
|
436 | *
|
437 | * @remarks
|
438 | * Contains an object with two properties:
|
439 | *
|
440 | * - `title`: the title passed to `console.timeStamp`
|
441 | * - `metrics`: object containing metrics as key/value pairs. The values will
|
442 | * be `number`s.
|
443 | */
|
444 | Metrics = 'metrics',
|
445 | /**
|
446 | * Emitted when an uncaught exception happens within the page. Contains an
|
447 | * `Error`.
|
448 | */
|
449 | PageError = 'pageerror',
|
450 | /**
|
451 | * Emitted when the page opens a new tab or window.
|
452 | *
|
453 | * Contains a {@link Page} corresponding to the popup window.
|
454 | *
|
455 | * @example
|
456 | *
|
457 | * ```ts
|
458 | * const [popup] = await Promise.all([
|
459 | * new Promise(resolve => page.once('popup', resolve)),
|
460 | * page.click('a[target=_blank]'),
|
461 | * ]);
|
462 | * ```
|
463 | *
|
464 | * ```ts
|
465 | * const [popup] = await Promise.all([
|
466 | * new Promise(resolve => page.once('popup', resolve)),
|
467 | * page.evaluate(() => window.open('https://example.com')),
|
468 | * ]);
|
469 | * ```
|
470 | */
|
471 | Popup = 'popup',
|
472 | /**
|
473 | * Emitted when a page issues a request and contains a {@link HTTPRequest}.
|
474 | *
|
475 | * @remarks
|
476 | * The object is readonly. See {@link Page.setRequestInterception} for
|
477 | * intercepting and mutating requests.
|
478 | */
|
479 | Request = 'request',
|
480 | /**
|
481 | * Emitted when a request ended up loading from cache. Contains a
|
482 | * {@link HTTPRequest}.
|
483 | *
|
484 | * @remarks
|
485 | * For certain requests, might contain undefined.
|
486 | * {@link https://crbug.com/750469}
|
487 | */
|
488 | RequestServedFromCache = 'requestservedfromcache',
|
489 | /**
|
490 | * Emitted when a request fails, for example by timing out.
|
491 | *
|
492 | * Contains a {@link HTTPRequest}.
|
493 | *
|
494 | * @remarks
|
495 | * HTTP Error responses, such as 404 or 503, are still successful responses
|
496 | * from HTTP standpoint, so request will complete with `requestfinished` event
|
497 | * and not with `requestfailed`.
|
498 | */
|
499 | RequestFailed = 'requestfailed',
|
500 | /**
|
501 | * Emitted when a request finishes successfully. Contains a
|
502 | * {@link HTTPRequest}.
|
503 | */
|
504 | RequestFinished = 'requestfinished',
|
505 | /**
|
506 | * Emitted when a response is received. Contains a {@link HTTPResponse}.
|
507 | */
|
508 | Response = 'response',
|
509 | /**
|
510 | * Emitted when a dedicated
|
511 | * {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
|
512 | * is spawned by the page.
|
513 | */
|
514 | WorkerCreated = 'workercreated',
|
515 | /**
|
516 | * Emitted when a dedicated
|
517 | * {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API | WebWorker}
|
518 | * is destroyed by the page.
|
519 | */
|
520 | WorkerDestroyed = 'workerdestroyed',
|
521 | }
|
522 |
|
523 | /**
|
524 | * Denotes the objects received by callback functions for page events.
|
525 | *
|
526 | * See {@link PageEvent} for more detail on the events and when they are
|
527 | * emitted.
|
528 | *
|
529 | * @public
|
530 | */
|
531 | export interface PageEvents extends Record<EventType, unknown> {
|
532 | [PageEvent.Close]: undefined;
|
533 | [PageEvent.Console]: ConsoleMessage;
|
534 | [PageEvent.Dialog]: Dialog;
|
535 | [PageEvent.DOMContentLoaded]: undefined;
|
536 | [PageEvent.Error]: Error;
|
537 | [PageEvent.FrameAttached]: Frame;
|
538 | [PageEvent.FrameDetached]: Frame;
|
539 | [PageEvent.FrameNavigated]: Frame;
|
540 | [PageEvent.Load]: undefined;
|
541 | [PageEvent.Metrics]: {title: string; metrics: Metrics};
|
542 | [PageEvent.PageError]: Error;
|
543 | [PageEvent.Popup]: Page | null;
|
544 | [PageEvent.Request]: HTTPRequest;
|
545 | [PageEvent.Response]: HTTPResponse;
|
546 | [PageEvent.RequestFailed]: HTTPRequest;
|
547 | [PageEvent.RequestFinished]: HTTPRequest;
|
548 | [PageEvent.RequestServedFromCache]: HTTPRequest;
|
549 | [PageEvent.WorkerCreated]: WebWorker;
|
550 | [PageEvent.WorkerDestroyed]: WebWorker;
|
551 | }
|
552 |
|
553 | /**
|
554 | * @public
|
555 | */
|
556 | export interface NewDocumentScriptEvaluation {
|
557 | identifier: string;
|
558 | }
|
559 |
|
560 | /**
|
561 | * @internal
|
562 | */
|
563 | export function setDefaultScreenshotOptions(options: ScreenshotOptions): void {
|
564 | options.optimizeForSpeed ??= false;
|
565 | options.type ??= 'png';
|
566 | options.fromSurface ??= true;
|
567 | options.fullPage ??= false;
|
568 | options.omitBackground ??= false;
|
569 | options.encoding ??= 'binary';
|
570 | options.captureBeyondViewport ??= true;
|
571 | }
|
572 |
|
573 | /**
|
574 | * Page provides methods to interact with a single tab or
|
575 | * {@link https://developer.chrome.com/extensions/background_pages | extension background page}
|
576 | * in the browser.
|
577 | *
|
578 | * :::note
|
579 | *
|
580 | * One Browser instance might have multiple Page instances.
|
581 | *
|
582 | * :::
|
583 | *
|
584 | * @example
|
585 | * This example creates a page, navigates it to a URL, and then saves a screenshot:
|
586 | *
|
587 | * ```ts
|
588 | * import puppeteer from 'puppeteer';
|
589 | *
|
590 | * (async () => {
|
591 | * const browser = await puppeteer.launch();
|
592 | * const page = await browser.newPage();
|
593 | * await page.goto('https://example.com');
|
594 | * await page.screenshot({path: 'screenshot.png'});
|
595 | * await browser.close();
|
596 | * })();
|
597 | * ```
|
598 | *
|
599 | * The Page class extends from Puppeteer's {@link EventEmitter} class and will
|
600 | * emit various events which are documented in the {@link PageEvent} enum.
|
601 | *
|
602 | * @example
|
603 | * This example logs a message for a single page `load` event:
|
604 | *
|
605 | * ```ts
|
606 | * page.once('load', () => console.log('Page loaded!'));
|
607 | * ```
|
608 | *
|
609 | * To unsubscribe from events use the {@link EventEmitter.off} method:
|
610 | *
|
611 | * ```ts
|
612 | * function logRequest(interceptedRequest) {
|
613 | * console.log('A request was made:', interceptedRequest.url());
|
614 | * }
|
615 | * page.on('request', logRequest);
|
616 | * // Sometime later...
|
617 | * page.off('request', logRequest);
|
618 | * ```
|
619 | *
|
620 | * @public
|
621 | */
|
622 | export abstract class Page extends EventEmitter<PageEvents> {
|
623 | /**
|
624 | * @internal
|
625 | */
|
626 | _isDragging = false;
|
627 | /**
|
628 | * @internal
|
629 | */
|
630 | _timeoutSettings = new TimeoutSettings();
|
631 |
|
632 | #requestHandlers = new WeakMap<Handler<HTTPRequest>, Handler<HTTPRequest>>();
|
633 |
|
634 | #inflight$ = new ReplaySubject<number>(1);
|
635 |
|
636 | /**
|
637 | * @internal
|
638 | */
|
639 | constructor() {
|
640 | super();
|
641 |
|
642 | fromEmitterEvent(this, PageEvent.Request)
|
643 | .pipe(
|
644 | mergeMap(originalRequest => {
|
645 | return concat(
|
646 | of(1),
|
647 | merge(
|
648 | fromEmitterEvent(this, PageEvent.RequestFailed),
|
649 | fromEmitterEvent(this, PageEvent.RequestFinished),
|
650 | fromEmitterEvent(this, PageEvent.Response).pipe(
|
651 | map(response => {
|
652 | return response.request();
|
653 | }),
|
654 | ),
|
655 | ).pipe(
|
656 | filter(request => {
|
657 | return request.id === originalRequest.id;
|
658 | }),
|
659 | take(1),
|
660 | map(() => {
|
661 | return -1;
|
662 | }),
|
663 | ),
|
664 | );
|
665 | }),
|
666 | mergeScan((acc, addend) => {
|
667 | return of(acc + addend);
|
668 | }, 0),
|
669 | takeUntil(fromEmitterEvent(this, PageEvent.Close)),
|
670 | startWith(0),
|
671 | )
|
672 | .subscribe(this.#inflight$);
|
673 | }
|
674 |
|
675 | /**
|
676 | * `true` if the service worker are being bypassed, `false` otherwise.
|
677 | */
|
678 | abstract isServiceWorkerBypassed(): boolean;
|
679 |
|
680 | /**
|
681 | * `true` if drag events are being intercepted, `false` otherwise.
|
682 | *
|
683 | * @deprecated We no longer support intercepting drag payloads. Use the new
|
684 | * drag APIs found on {@link ElementHandle} to drag (or just use the
|
685 | * {@link Page.mouse}).
|
686 | */
|
687 | abstract isDragInterceptionEnabled(): boolean;
|
688 |
|
689 | /**
|
690 | * `true` if the page has JavaScript enabled, `false` otherwise.
|
691 | */
|
692 | abstract isJavaScriptEnabled(): boolean;
|
693 |
|
694 | /**
|
695 | * Listen to page events.
|
696 | *
|
697 | * @remarks
|
698 | * This method exists to define event typings and handle proper wireup of
|
699 | * cooperative request interception. Actual event listening and dispatching is
|
700 | * delegated to {@link EventEmitter}.
|
701 | *
|
702 | * @internal
|
703 | */
|
704 | override on<K extends keyof EventsWithWildcard<PageEvents>>(
|
705 | type: K,
|
706 | handler: (event: EventsWithWildcard<PageEvents>[K]) => void,
|
707 | ): this {
|
708 | if (type !== PageEvent.Request) {
|
709 | return super.on(type, handler);
|
710 | }
|
711 | let wrapper = this.#requestHandlers.get(
|
712 | handler as (event: PageEvents[PageEvent.Request]) => void,
|
713 | );
|
714 | if (wrapper === undefined) {
|
715 | wrapper = (event: HTTPRequest) => {
|
716 | event.enqueueInterceptAction(() => {
|
717 | return handler(event as EventsWithWildcard<PageEvents>[K]);
|
718 | });
|
719 | };
|
720 | this.#requestHandlers.set(
|
721 | handler as (event: PageEvents[PageEvent.Request]) => void,
|
722 | wrapper,
|
723 | );
|
724 | }
|
725 | return super.on(
|
726 | type,
|
727 | wrapper as (event: EventsWithWildcard<PageEvents>[K]) => void,
|
728 | );
|
729 | }
|
730 |
|
731 | /**
|
732 | * @internal
|
733 | */
|
734 | override off<K extends keyof EventsWithWildcard<PageEvents>>(
|
735 | type: K,
|
736 | handler: (event: EventsWithWildcard<PageEvents>[K]) => void,
|
737 | ): this {
|
738 | if (type === PageEvent.Request) {
|
739 | handler =
|
740 | (this.#requestHandlers.get(
|
741 | handler as (
|
742 | event: EventsWithWildcard<PageEvents>[PageEvent.Request],
|
743 | ) => void,
|
744 | ) as (event: EventsWithWildcard<PageEvents>[K]) => void) || handler;
|
745 | }
|
746 | return super.off(type, handler);
|
747 | }
|
748 |
|
749 | /**
|
750 | * This method is typically coupled with an action that triggers file
|
751 | * choosing.
|
752 | *
|
753 | * :::caution
|
754 | *
|
755 | * This must be called before the file chooser is launched. It will not return
|
756 | * a currently active file chooser.
|
757 | *
|
758 | * :::
|
759 | *
|
760 | * :::caution
|
761 | *
|
762 | * Interception of file dialogs triggered via DOM APIs such as
|
763 | * window.showOpenFilePicker is currently not supported.
|
764 | *
|
765 | * :::
|
766 | *
|
767 | * @remarks
|
768 | * In the "headful" browser, this method results in the native file picker
|
769 | * dialog `not showing up` for the user.
|
770 | *
|
771 | * @example
|
772 | * The following example clicks a button that issues a file chooser
|
773 | * and then responds with `/tmp/myfile.pdf` as if a user has selected this file.
|
774 | *
|
775 | * ```ts
|
776 | * const [fileChooser] = await Promise.all([
|
777 | * page.waitForFileChooser(),
|
778 | * page.click('#upload-file-button'),
|
779 | * // some button that triggers file selection
|
780 | * ]);
|
781 | * await fileChooser.accept(['/tmp/myfile.pdf']);
|
782 | * ```
|
783 | */
|
784 | abstract waitForFileChooser(
|
785 | options?: WaitTimeoutOptions,
|
786 | ): Promise<FileChooser>;
|
787 |
|
788 | /**
|
789 | * Sets the page's geolocation.
|
790 | *
|
791 | * @remarks
|
792 | * Consider using {@link BrowserContext.overridePermissions} to grant
|
793 | * permissions for the page to read its geolocation.
|
794 | *
|
795 | * @example
|
796 | *
|
797 | * ```ts
|
798 | * await page.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
799 | * ```
|
800 | */
|
801 | abstract setGeolocation(options: GeolocationOptions): Promise<void>;
|
802 |
|
803 | /**
|
804 | * A target this page was created from.
|
805 | *
|
806 | * @deprecated Use {@link Page.createCDPSession} directly.
|
807 | */
|
808 | abstract target(): Target;
|
809 |
|
810 | /**
|
811 | * Get the browser the page belongs to.
|
812 | */
|
813 | abstract browser(): Browser;
|
814 |
|
815 | /**
|
816 | * Get the browser context that the page belongs to.
|
817 | */
|
818 | abstract browserContext(): BrowserContext;
|
819 |
|
820 | /**
|
821 | * The page's main frame.
|
822 | */
|
823 | abstract mainFrame(): Frame;
|
824 |
|
825 | /**
|
826 | * Creates a Chrome Devtools Protocol session attached to the page.
|
827 | */
|
828 | abstract createCDPSession(): Promise<CDPSession>;
|
829 |
|
830 | /**
|
831 | * {@inheritDoc Keyboard}
|
832 | */
|
833 | abstract get keyboard(): Keyboard;
|
834 |
|
835 | /**
|
836 | * {@inheritDoc Touchscreen}
|
837 | */
|
838 | abstract get touchscreen(): Touchscreen;
|
839 |
|
840 | /**
|
841 | * {@inheritDoc Coverage}
|
842 | */
|
843 | abstract get coverage(): Coverage;
|
844 |
|
845 | /**
|
846 | * {@inheritDoc Tracing}
|
847 | */
|
848 | abstract get tracing(): Tracing;
|
849 |
|
850 | /**
|
851 | * {@inheritDoc Accessibility}
|
852 | */
|
853 | get accessibility(): Accessibility {
|
854 | return this.mainFrame().accessibility;
|
855 | }
|
856 |
|
857 | /**
|
858 | * An array of all frames attached to the page.
|
859 | */
|
860 | abstract frames(): Frame[];
|
861 |
|
862 | /**
|
863 | * All of the dedicated {@link
|
864 | * https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API |
|
865 | * WebWorkers} associated with the page.
|
866 | *
|
867 | * @remarks
|
868 | * This does not contain ServiceWorkers
|
869 | */
|
870 | abstract workers(): WebWorker[];
|
871 |
|
872 | /**
|
873 | * Activating request interception enables {@link HTTPRequest.abort},
|
874 | * {@link HTTPRequest.continue} and {@link HTTPRequest.respond} methods. This
|
875 | * provides the capability to modify network requests that are made by a page.
|
876 | *
|
877 | * Once request interception is enabled, every request will stall unless it's
|
878 | * continued, responded or aborted; or completed using the browser cache.
|
879 | *
|
880 | * See the
|
881 | * {@link https://pptr.dev/guides/network-interception|Request interception guide}
|
882 | * for more details.
|
883 | *
|
884 | * @example
|
885 | * An example of a naïve request interceptor that aborts all image requests:
|
886 | *
|
887 | * ```ts
|
888 | * import puppeteer from 'puppeteer';
|
889 | * (async () => {
|
890 | * const browser = await puppeteer.launch();
|
891 | * const page = await browser.newPage();
|
892 | * await page.setRequestInterception(true);
|
893 | * page.on('request', interceptedRequest => {
|
894 | * if (
|
895 | * interceptedRequest.url().endsWith('.png') ||
|
896 | * interceptedRequest.url().endsWith('.jpg')
|
897 | * )
|
898 | * interceptedRequest.abort();
|
899 | * else interceptedRequest.continue();
|
900 | * });
|
901 | * await page.goto('https://example.com');
|
902 | * await browser.close();
|
903 | * })();
|
904 | * ```
|
905 | *
|
906 | * @param value - Whether to enable request interception.
|
907 | */
|
908 | abstract setRequestInterception(value: boolean): Promise<void>;
|
909 |
|
910 | /**
|
911 | * Toggles ignoring of service worker for each request.
|
912 | *
|
913 | * @param bypass - Whether to bypass service worker and load from network.
|
914 | */
|
915 | abstract setBypassServiceWorker(bypass: boolean): Promise<void>;
|
916 |
|
917 | /**
|
918 | * @param enabled - Whether to enable drag interception.
|
919 | *
|
920 | * @deprecated We no longer support intercepting drag payloads. Use the new
|
921 | * drag APIs found on {@link ElementHandle} to drag (or just use the
|
922 | * {@link Page.mouse}).
|
923 | */
|
924 | abstract setDragInterception(enabled: boolean): Promise<void>;
|
925 |
|
926 | /**
|
927 | * Sets the network connection to offline.
|
928 | *
|
929 | * It does not change the parameters used in {@link Page.emulateNetworkConditions}
|
930 | *
|
931 | * @param enabled - When `true`, enables offline mode for the page.
|
932 | */
|
933 | abstract setOfflineMode(enabled: boolean): Promise<void>;
|
934 |
|
935 | /**
|
936 | * This does not affect WebSockets and WebRTC PeerConnections (see
|
937 | * https://crbug.com/563644). To set the page offline, you can use
|
938 | * {@link Page.setOfflineMode}.
|
939 | *
|
940 | * A list of predefined network conditions can be used by importing
|
941 | * {@link PredefinedNetworkConditions}.
|
942 | *
|
943 | * @example
|
944 | *
|
945 | * ```ts
|
946 | * import {PredefinedNetworkConditions} from 'puppeteer';
|
947 | * const slow3G = PredefinedNetworkConditions['Slow 3G'];
|
948 | *
|
949 | * (async () => {
|
950 | * const browser = await puppeteer.launch();
|
951 | * const page = await browser.newPage();
|
952 | * await page.emulateNetworkConditions(slow3G);
|
953 | * await page.goto('https://www.google.com');
|
954 | * // other actions...
|
955 | * await browser.close();
|
956 | * })();
|
957 | * ```
|
958 | *
|
959 | * @param networkConditions - Passing `null` disables network condition
|
960 | * emulation.
|
961 | */
|
962 | abstract emulateNetworkConditions(
|
963 | networkConditions: NetworkConditions | null,
|
964 | ): Promise<void>;
|
965 |
|
966 | /**
|
967 | * This setting will change the default maximum navigation time for the
|
968 | * following methods and related shortcuts:
|
969 | *
|
970 | * - {@link Page.goBack | page.goBack(options)}
|
971 | *
|
972 | * - {@link Page.goForward | page.goForward(options)}
|
973 | *
|
974 | * - {@link Page.goto | page.goto(url,options)}
|
975 | *
|
976 | * - {@link Page.reload | page.reload(options)}
|
977 | *
|
978 | * - {@link Page.setContent | page.setContent(html,options)}
|
979 | *
|
980 | * - {@link Page.waitForNavigation | page.waitForNavigation(options)}
|
981 | * @param timeout - Maximum navigation time in milliseconds.
|
982 | */
|
983 | abstract setDefaultNavigationTimeout(timeout: number): void;
|
984 |
|
985 | /**
|
986 | * @param timeout - Maximum time in milliseconds.
|
987 | */
|
988 | abstract setDefaultTimeout(timeout: number): void;
|
989 |
|
990 | /**
|
991 | * Maximum time in milliseconds.
|
992 | */
|
993 | abstract getDefaultTimeout(): number;
|
994 |
|
995 | /**
|
996 | * Maximum navigation time in milliseconds.
|
997 | */
|
998 | abstract getDefaultNavigationTimeout(): number;
|
999 |
|
1000 | /**
|
1001 | * Creates a locator for the provided selector. See {@link Locator} for
|
1002 | * details and supported actions.
|
1003 | *
|
1004 | * @param selector -
|
1005 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1006 | * to query the page for.
|
1007 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1008 | * can be passed as-is and a
|
1009 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1010 | * allows quering by
|
1011 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1012 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1013 | * and
|
1014 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1015 | * and
|
1016 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1017 | * Alternatively, you can specify the selector type using a
|
1018 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1019 | */
|
1020 | locator<Selector extends string>(
|
1021 | selector: Selector,
|
1022 | ): Locator<NodeFor<Selector>>;
|
1023 |
|
1024 | /**
|
1025 | * Creates a locator for the provided function. See {@link Locator} for
|
1026 | * details and supported actions.
|
1027 | *
|
1028 | * @param selector -
|
1029 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1030 | * to query the page for.
|
1031 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1032 | * can be passed as-is and a
|
1033 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1034 | * allows quering by
|
1035 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1036 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1037 | * and
|
1038 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1039 | * and
|
1040 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1041 | * Alternatively, you can specify the selector type using a
|
1042 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1043 | */
|
1044 | locator<Ret>(func: () => Awaitable<Ret>): Locator<Ret>;
|
1045 | locator<Selector extends string, Ret>(
|
1046 | selectorOrFunc: Selector | (() => Awaitable<Ret>),
|
1047 | ): Locator<NodeFor<Selector>> | Locator<Ret> {
|
1048 | if (typeof selectorOrFunc === 'string') {
|
1049 | return NodeLocator.create(this, selectorOrFunc);
|
1050 | } else {
|
1051 | return FunctionLocator.create(this, selectorOrFunc);
|
1052 | }
|
1053 | }
|
1054 |
|
1055 | /**
|
1056 | * A shortcut for {@link Locator.race} that does not require static imports.
|
1057 | *
|
1058 | * @internal
|
1059 | */
|
1060 | locatorRace<Locators extends readonly unknown[] | []>(
|
1061 | locators: Locators,
|
1062 | ): Locator<AwaitedLocator<Locators[number]>> {
|
1063 | return Locator.race(locators);
|
1064 | }
|
1065 |
|
1066 | /**
|
1067 | * Finds the first element that matches the selector. If no element matches
|
1068 | * the selector, the return value resolves to `null`.
|
1069 | *
|
1070 | * @param selector -
|
1071 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1072 | * to query the page for.
|
1073 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1074 | * can be passed as-is and a
|
1075 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1076 | * allows quering by
|
1077 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1078 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1079 | * and
|
1080 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1081 | * and
|
1082 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1083 | * Alternatively, you can specify the selector type using a
|
1084 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1085 | *
|
1086 | * @remarks
|
1087 | *
|
1088 | * Shortcut for {@link Frame.$ | Page.mainFrame().$(selector) }.
|
1089 | */
|
1090 | async $<Selector extends string>(
|
1091 | selector: Selector,
|
1092 | ): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
1093 | return await this.mainFrame().$(selector);
|
1094 | }
|
1095 |
|
1096 | /**
|
1097 | * Finds elements on the page that match the selector. If no elements
|
1098 | * match the selector, the return value resolves to `[]`.
|
1099 | *
|
1100 | * @param selector -
|
1101 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1102 | * to query the page for.
|
1103 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1104 | * can be passed as-is and a
|
1105 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1106 | * allows quering by
|
1107 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1108 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1109 | * and
|
1110 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1111 | * and
|
1112 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1113 | * Alternatively, you can specify the selector type using a
|
1114 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1115 | *
|
1116 | * @remarks
|
1117 | *
|
1118 | * Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }.
|
1119 | */
|
1120 | async $$<Selector extends string>(
|
1121 | selector: Selector,
|
1122 | options?: QueryOptions,
|
1123 | ): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
|
1124 | return await this.mainFrame().$$(selector, options);
|
1125 | }
|
1126 |
|
1127 | /**
|
1128 | * @remarks
|
1129 | *
|
1130 | * The only difference between {@link Page.evaluate | page.evaluate} and
|
1131 | * `page.evaluateHandle` is that `evaluateHandle` will return the value
|
1132 | * wrapped in an in-page object.
|
1133 | *
|
1134 | * If the function passed to `page.evaluateHandle` returns a Promise, the
|
1135 | * function will wait for the promise to resolve and return its value.
|
1136 | *
|
1137 | * You can pass a string instead of a function (although functions are
|
1138 | * recommended as they are easier to debug and use with TypeScript):
|
1139 | *
|
1140 | * @example
|
1141 | *
|
1142 | * ```ts
|
1143 | * const aHandle = await page.evaluateHandle('document');
|
1144 | * ```
|
1145 | *
|
1146 | * @example
|
1147 | * {@link JSHandle} instances can be passed as arguments to the `pageFunction`:
|
1148 | *
|
1149 | * ```ts
|
1150 | * const aHandle = await page.evaluateHandle(() => document.body);
|
1151 | * const resultHandle = await page.evaluateHandle(
|
1152 | * body => body.innerHTML,
|
1153 | * aHandle,
|
1154 | * );
|
1155 | * console.log(await resultHandle.jsonValue());
|
1156 | * await resultHandle.dispose();
|
1157 | * ```
|
1158 | *
|
1159 | * Most of the time this function returns a {@link JSHandle},
|
1160 | * but if `pageFunction` returns a reference to an element,
|
1161 | * you instead get an {@link ElementHandle} back:
|
1162 | *
|
1163 | * @example
|
1164 | *
|
1165 | * ```ts
|
1166 | * const button = await page.evaluateHandle(() =>
|
1167 | * document.querySelector('button'),
|
1168 | * );
|
1169 | * // can call `click` because `button` is an `ElementHandle`
|
1170 | * await button.click();
|
1171 | * ```
|
1172 | *
|
1173 | * The TypeScript definitions assume that `evaluateHandle` returns
|
1174 | * a `JSHandle`, but if you know it's going to return an
|
1175 | * `ElementHandle`, pass it as the generic argument:
|
1176 | *
|
1177 | * ```ts
|
1178 | * const button = await page.evaluateHandle<ElementHandle>(...);
|
1179 | * ```
|
1180 | *
|
1181 | * @param pageFunction - a function that is run within the page
|
1182 | * @param args - arguments to be passed to the pageFunction
|
1183 | */
|
1184 | async evaluateHandle<
|
1185 | Params extends unknown[],
|
1186 | Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
1187 | >(
|
1188 | pageFunction: Func | string,
|
1189 | ...args: Params
|
1190 | ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
1191 | pageFunction = withSourcePuppeteerURLIfNone(
|
1192 | this.evaluateHandle.name,
|
1193 | pageFunction,
|
1194 | );
|
1195 | return await this.mainFrame().evaluateHandle(pageFunction, ...args);
|
1196 | }
|
1197 |
|
1198 | /**
|
1199 | * This method iterates the JavaScript heap and finds all objects with the
|
1200 | * given prototype.
|
1201 | *
|
1202 | * @example
|
1203 | *
|
1204 | * ```ts
|
1205 | * // Create a Map object
|
1206 | * await page.evaluate(() => (window.map = new Map()));
|
1207 | * // Get a handle to the Map object prototype
|
1208 | * const mapPrototype = await page.evaluateHandle(() => Map.prototype);
|
1209 | * // Query all map instances into an array
|
1210 | * const mapInstances = await page.queryObjects(mapPrototype);
|
1211 | * // Count amount of map objects in heap
|
1212 | * const count = await page.evaluate(maps => maps.length, mapInstances);
|
1213 | * await mapInstances.dispose();
|
1214 | * await mapPrototype.dispose();
|
1215 | * ```
|
1216 | *
|
1217 | * @param prototypeHandle - a handle to the object prototype.
|
1218 | * @returns Promise which resolves to a handle to an array of objects with
|
1219 | * this prototype.
|
1220 | */
|
1221 | abstract queryObjects<Prototype>(
|
1222 | prototypeHandle: JSHandle<Prototype>,
|
1223 | ): Promise<JSHandle<Prototype[]>>;
|
1224 |
|
1225 | /**
|
1226 | * This method finds the first element within the page that matches the selector
|
1227 | * and passes the result as the first argument to the `pageFunction`.
|
1228 | *
|
1229 | * @remarks
|
1230 | *
|
1231 | * If no element is found matching `selector`, the method will throw an error.
|
1232 | *
|
1233 | * If `pageFunction` returns a promise `$eval` will wait for the promise to
|
1234 | * resolve and then return its value.
|
1235 | *
|
1236 | * @example
|
1237 | *
|
1238 | * ```ts
|
1239 | * const searchValue = await page.$eval('#search', el => el.value);
|
1240 | * const preloadHref = await page.$eval('link[rel=preload]', el => el.href);
|
1241 | * const html = await page.$eval('.main-container', el => el.outerHTML);
|
1242 | * ```
|
1243 | *
|
1244 | * If you are using TypeScript, you may have to provide an explicit type to the
|
1245 | * first argument of the `pageFunction`.
|
1246 | * By default it is typed as `Element`, but you may need to provide a more
|
1247 | * specific sub-type:
|
1248 | *
|
1249 | * @example
|
1250 | *
|
1251 | * ```ts
|
1252 | * // if you don't provide HTMLInputElement here, TS will error
|
1253 | * // as `value` is not on `Element`
|
1254 | * const searchValue = await page.$eval(
|
1255 | * '#search',
|
1256 | * (el: HTMLInputElement) => el.value,
|
1257 | * );
|
1258 | * ```
|
1259 | *
|
1260 | * The compiler should be able to infer the return type
|
1261 | * from the `pageFunction` you provide. If it is unable to, you can use the generic
|
1262 | * type to tell the compiler what return type you expect from `$eval`:
|
1263 | *
|
1264 | * @example
|
1265 | *
|
1266 | * ```ts
|
1267 | * // The compiler can infer the return type in this case, but if it can't
|
1268 | * // or if you want to be more explicit, provide it as the generic type.
|
1269 | * const searchValue = await page.$eval<string>(
|
1270 | * '#search',
|
1271 | * (el: HTMLInputElement) => el.value,
|
1272 | * );
|
1273 | * ```
|
1274 | *
|
1275 | * @param selector -
|
1276 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1277 | * to query the page for.
|
1278 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1279 | * can be passed as-is and a
|
1280 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1281 | * allows quering by
|
1282 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1283 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1284 | * and
|
1285 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1286 | * and
|
1287 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1288 | * Alternatively, you can specify the selector type using a
|
1289 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1290 | * @param pageFunction - the function to be evaluated in the page context.
|
1291 | * Will be passed the result of the element matching the selector as its
|
1292 | * first argument.
|
1293 | * @param args - any additional arguments to pass through to `pageFunction`.
|
1294 | *
|
1295 | * @returns The result of calling `pageFunction`. If it returns an element it
|
1296 | * is wrapped in an {@link ElementHandle}, else the raw value itself is
|
1297 | * returned.
|
1298 | */
|
1299 | async $eval<
|
1300 | Selector extends string,
|
1301 | Params extends unknown[],
|
1302 | Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
1303 | NodeFor<Selector>,
|
1304 | Params
|
1305 | >,
|
1306 | >(
|
1307 | selector: Selector,
|
1308 | pageFunction: Func | string,
|
1309 | ...args: Params
|
1310 | ): Promise<Awaited<ReturnType<Func>>> {
|
1311 | pageFunction = withSourcePuppeteerURLIfNone(this.$eval.name, pageFunction);
|
1312 | return await this.mainFrame().$eval(selector, pageFunction, ...args);
|
1313 | }
|
1314 |
|
1315 | /**
|
1316 | * This method returns all elements matching the selector and passes the
|
1317 | * resulting array as the first argument to the `pageFunction`.
|
1318 | *
|
1319 | * @remarks
|
1320 | * If `pageFunction` returns a promise `$$eval` will wait for the promise to
|
1321 | * resolve and then return its value.
|
1322 | *
|
1323 | * @example
|
1324 | *
|
1325 | * ```ts
|
1326 | * // get the amount of divs on the page
|
1327 | * const divCount = await page.$$eval('div', divs => divs.length);
|
1328 | *
|
1329 | * // get the text content of all the `.options` elements:
|
1330 | * const options = await page.$$eval('div > span.options', options => {
|
1331 | * return options.map(option => option.textContent);
|
1332 | * });
|
1333 | * ```
|
1334 | *
|
1335 | * If you are using TypeScript, you may have to provide an explicit type to the
|
1336 | * first argument of the `pageFunction`.
|
1337 | * By default it is typed as `Element[]`, but you may need to provide a more
|
1338 | * specific sub-type:
|
1339 | *
|
1340 | * @example
|
1341 | *
|
1342 | * ```ts
|
1343 | * await page.$$eval('input', elements => {
|
1344 | * return elements.map(e => e.value);
|
1345 | * });
|
1346 | * ```
|
1347 | *
|
1348 | * The compiler should be able to infer the return type
|
1349 | * from the `pageFunction` you provide. If it is unable to, you can use the generic
|
1350 | * type to tell the compiler what return type you expect from `$$eval`:
|
1351 | *
|
1352 | * @example
|
1353 | *
|
1354 | * ```ts
|
1355 | * const allInputValues = await page.$$eval('input', elements =>
|
1356 | * elements.map(e => e.textContent),
|
1357 | * );
|
1358 | * ```
|
1359 | *
|
1360 | * @param selector -
|
1361 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
1362 | * to query the page for.
|
1363 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
1364 | * can be passed as-is and a
|
1365 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
1366 | * allows quering by
|
1367 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
1368 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
1369 | * and
|
1370 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
1371 | * and
|
1372 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
1373 | * Alternatively, you can specify the selector type using a
|
1374 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
1375 | * @param pageFunction - the function to be evaluated in the page context.
|
1376 | * Will be passed an array of matching elements as its first argument.
|
1377 | * @param args - any additional arguments to pass through to `pageFunction`.
|
1378 | *
|
1379 | * @returns The result of calling `pageFunction`. If it returns an element it
|
1380 | * is wrapped in an {@link ElementHandle}, else the raw value itself is
|
1381 | * returned.
|
1382 | */
|
1383 | async $$eval<
|
1384 | Selector extends string,
|
1385 | Params extends unknown[],
|
1386 | Func extends EvaluateFuncWith<
|
1387 | Array<NodeFor<Selector>>,
|
1388 | Params
|
1389 | > = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>,
|
1390 | >(
|
1391 | selector: Selector,
|
1392 | pageFunction: Func | string,
|
1393 | ...args: Params
|
1394 | ): Promise<Awaited<ReturnType<Func>>> {
|
1395 | pageFunction = withSourcePuppeteerURLIfNone(this.$$eval.name, pageFunction);
|
1396 | return await this.mainFrame().$$eval(selector, pageFunction, ...args);
|
1397 | }
|
1398 |
|
1399 | /**
|
1400 | * If no URLs are specified, this method returns cookies for the
|
1401 | * current page URL. If URLs are specified, only cookies for those
|
1402 | * URLs are returned.
|
1403 | *
|
1404 | * @deprecated Page-level cookie API is deprecated. Use
|
1405 | * {@link Browser.cookies} or {@link BrowserContext.cookies} instead.
|
1406 | */
|
1407 | abstract cookies(...urls: string[]): Promise<Cookie[]>;
|
1408 |
|
1409 | /**
|
1410 | * @deprecated Page-level cookie API is deprecated. Use
|
1411 | * {@link Browser.deleteCookie} or {@link BrowserContext.deleteCookie}
|
1412 | * instead.
|
1413 | */
|
1414 | abstract deleteCookie(...cookies: DeleteCookiesRequest[]): Promise<void>;
|
1415 |
|
1416 | /**
|
1417 | * @example
|
1418 | *
|
1419 | *```ts
|
1420 | * await page.setCookie(cookieObject1, cookieObject2);
|
1421 | *```
|
1422 | *
|
1423 | * @deprecated Page-level cookie API is deprecated. Use
|
1424 | * {@link Browser.setCookie} or {@link BrowserContext.setCookie}
|
1425 | * instead.
|
1426 | */
|
1427 | abstract setCookie(...cookies: CookieParam[]): Promise<void>;
|
1428 |
|
1429 | /**
|
1430 | * Adds a `<script>` tag into the page with the desired URL or content.
|
1431 | *
|
1432 | * @remarks
|
1433 | * Shortcut for
|
1434 | * {@link Frame.addScriptTag | page.mainFrame().addScriptTag(options)}.
|
1435 | *
|
1436 | * @param options - Options for the script.
|
1437 | * @returns An {@link ElementHandle | element handle} to the injected
|
1438 | * `<script>` element.
|
1439 | */
|
1440 | async addScriptTag(
|
1441 | options: FrameAddScriptTagOptions,
|
1442 | ): Promise<ElementHandle<HTMLScriptElement>> {
|
1443 | return await this.mainFrame().addScriptTag(options);
|
1444 | }
|
1445 |
|
1446 | /**
|
1447 | * Adds a `<link rel="stylesheet">` tag into the page with the desired URL or
|
1448 | * a `<style type="text/css">` tag with the content.
|
1449 | *
|
1450 | * Shortcut for
|
1451 | * {@link Frame.(addStyleTag:2) | page.mainFrame().addStyleTag(options)}.
|
1452 | *
|
1453 | * @returns An {@link ElementHandle | element handle} to the injected `<link>`
|
1454 | * or `<style>` element.
|
1455 | */
|
1456 | async addStyleTag(
|
1457 | options: Omit<FrameAddStyleTagOptions, 'url'>,
|
1458 | ): Promise<ElementHandle<HTMLStyleElement>>;
|
1459 | async addStyleTag(
|
1460 | options: FrameAddStyleTagOptions,
|
1461 | ): Promise<ElementHandle<HTMLLinkElement>>;
|
1462 | async addStyleTag(
|
1463 | options: FrameAddStyleTagOptions,
|
1464 | ): Promise<ElementHandle<HTMLStyleElement | HTMLLinkElement>> {
|
1465 | return await this.mainFrame().addStyleTag(options);
|
1466 | }
|
1467 |
|
1468 | /**
|
1469 | * The method adds a function called `name` on the page's `window` object.
|
1470 | * When called, the function executes `puppeteerFunction` in node.js and
|
1471 | * returns a `Promise` which resolves to the return value of
|
1472 | * `puppeteerFunction`.
|
1473 | *
|
1474 | * If the puppeteerFunction returns a `Promise`, it will be awaited.
|
1475 | *
|
1476 | * :::note
|
1477 | *
|
1478 | * Functions installed via `page.exposeFunction` survive navigations.
|
1479 | *
|
1480 | * :::
|
1481 | *
|
1482 | * @example
|
1483 | * An example of adding an `md5` function into the page:
|
1484 | *
|
1485 | * ```ts
|
1486 | * import puppeteer from 'puppeteer';
|
1487 | * import crypto from 'crypto';
|
1488 | *
|
1489 | * (async () => {
|
1490 | * const browser = await puppeteer.launch();
|
1491 | * const page = await browser.newPage();
|
1492 | * page.on('console', msg => console.log(msg.text()));
|
1493 | * await page.exposeFunction('md5', text =>
|
1494 | * crypto.createHash('md5').update(text).digest('hex'),
|
1495 | * );
|
1496 | * await page.evaluate(async () => {
|
1497 | * // use window.md5 to compute hashes
|
1498 | * const myString = 'PUPPETEER';
|
1499 | * const myHash = await window.md5(myString);
|
1500 | * console.log(`md5 of ${myString} is ${myHash}`);
|
1501 | * });
|
1502 | * await browser.close();
|
1503 | * })();
|
1504 | * ```
|
1505 | *
|
1506 | * @example
|
1507 | * An example of adding a `window.readfile` function into the page:
|
1508 | *
|
1509 | * ```ts
|
1510 | * import puppeteer from 'puppeteer';
|
1511 | * import fs from 'fs';
|
1512 | *
|
1513 | * (async () => {
|
1514 | * const browser = await puppeteer.launch();
|
1515 | * const page = await browser.newPage();
|
1516 | * page.on('console', msg => console.log(msg.text()));
|
1517 | * await page.exposeFunction('readfile', async filePath => {
|
1518 | * return new Promise((resolve, reject) => {
|
1519 | * fs.readFile(filePath, 'utf8', (err, text) => {
|
1520 | * if (err) reject(err);
|
1521 | * else resolve(text);
|
1522 | * });
|
1523 | * });
|
1524 | * });
|
1525 | * await page.evaluate(async () => {
|
1526 | * // use window.readfile to read contents of a file
|
1527 | * const content = await window.readfile('/etc/hosts');
|
1528 | * console.log(content);
|
1529 | * });
|
1530 | * await browser.close();
|
1531 | * })();
|
1532 | * ```
|
1533 | *
|
1534 | * @param name - Name of the function on the window object
|
1535 | * @param pptrFunction - Callback function which will be called in Puppeteer's
|
1536 | * context.
|
1537 | */
|
1538 | abstract exposeFunction(
|
1539 | name: string,
|
1540 | // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
1541 | pptrFunction: Function | {default: Function},
|
1542 | ): Promise<void>;
|
1543 |
|
1544 | /**
|
1545 | * The method removes a previously added function via ${@link Page.exposeFunction}
|
1546 | * called `name` from the page's `window` object.
|
1547 | */
|
1548 | abstract removeExposedFunction(name: string): Promise<void>;
|
1549 |
|
1550 | /**
|
1551 | * Provide credentials for `HTTP authentication`.
|
1552 | *
|
1553 | * :::note
|
1554 | *
|
1555 | * Request interception will be turned on behind the scenes to
|
1556 | * implement authentication. This might affect performance.
|
1557 | *
|
1558 | * :::
|
1559 | *
|
1560 | * @remarks
|
1561 | * To disable authentication, pass `null`.
|
1562 | */
|
1563 | abstract authenticate(credentials: Credentials | null): Promise<void>;
|
1564 |
|
1565 | /**
|
1566 | * The extra HTTP headers will be sent with every request the page initiates.
|
1567 | *
|
1568 | * :::tip
|
1569 | *
|
1570 | * All HTTP header names are lowercased. (HTTP headers are
|
1571 | * case-insensitive, so this shouldn’t impact your server code.)
|
1572 | *
|
1573 | * :::
|
1574 | *
|
1575 | * :::note
|
1576 | *
|
1577 | * page.setExtraHTTPHeaders does not guarantee the order of headers in
|
1578 | * the outgoing requests.
|
1579 | *
|
1580 | * :::
|
1581 | *
|
1582 | * @param headers - An object containing additional HTTP headers to be sent
|
1583 | * with every request. All header values must be strings.
|
1584 | */
|
1585 | abstract setExtraHTTPHeaders(headers: Record<string, string>): Promise<void>;
|
1586 |
|
1587 | /**
|
1588 | * @param userAgent - Specific user agent to use in this page
|
1589 | * @param userAgentData - Specific user agent client hint data to use in this
|
1590 | * page
|
1591 | * @returns Promise which resolves when the user agent is set.
|
1592 | */
|
1593 | abstract setUserAgent(
|
1594 | userAgent: string,
|
1595 | userAgentMetadata?: Protocol.Emulation.UserAgentMetadata,
|
1596 | ): Promise<void>;
|
1597 |
|
1598 | /**
|
1599 | * Object containing metrics as key/value pairs.
|
1600 | *
|
1601 | * @returns
|
1602 | *
|
1603 | * - `Timestamp` : The timestamp when the metrics sample was taken.
|
1604 | *
|
1605 | * - `Documents` : Number of documents in the page.
|
1606 | *
|
1607 | * - `Frames` : Number of frames in the page.
|
1608 | *
|
1609 | * - `JSEventListeners` : Number of events in the page.
|
1610 | *
|
1611 | * - `Nodes` : Number of DOM nodes in the page.
|
1612 | *
|
1613 | * - `LayoutCount` : Total number of full or partial page layout.
|
1614 | *
|
1615 | * - `RecalcStyleCount` : Total number of page style recalculations.
|
1616 | *
|
1617 | * - `LayoutDuration` : Combined durations of all page layouts.
|
1618 | *
|
1619 | * - `RecalcStyleDuration` : Combined duration of all page style
|
1620 | * recalculations.
|
1621 | *
|
1622 | * - `ScriptDuration` : Combined duration of JavaScript execution.
|
1623 | *
|
1624 | * - `TaskDuration` : Combined duration of all tasks performed by the browser.
|
1625 | *
|
1626 | * - `JSHeapUsedSize` : Used JavaScript heap size.
|
1627 | *
|
1628 | * - `JSHeapTotalSize` : Total JavaScript heap size.
|
1629 | *
|
1630 | * @remarks
|
1631 | * All timestamps are in monotonic time: monotonically increasing time
|
1632 | * in seconds since an arbitrary point in the past.
|
1633 | */
|
1634 | abstract metrics(): Promise<Metrics>;
|
1635 |
|
1636 | /**
|
1637 | * The page's URL.
|
1638 | *
|
1639 | * @remarks
|
1640 | *
|
1641 | * Shortcut for {@link Frame.url | page.mainFrame().url()}.
|
1642 | */
|
1643 | url(): string {
|
1644 | return this.mainFrame().url();
|
1645 | }
|
1646 |
|
1647 | /**
|
1648 | * The full HTML contents of the page, including the DOCTYPE.
|
1649 | */
|
1650 | async content(): Promise<string> {
|
1651 | return await this.mainFrame().content();
|
1652 | }
|
1653 |
|
1654 | /**
|
1655 | * Set the content of the page.
|
1656 | *
|
1657 | * @param html - HTML markup to assign to the page.
|
1658 | * @param options - Parameters that has some properties.
|
1659 | */
|
1660 | async setContent(html: string, options?: WaitForOptions): Promise<void> {
|
1661 | await this.mainFrame().setContent(html, options);
|
1662 | }
|
1663 |
|
1664 | /**
|
1665 | * {@inheritDoc Frame.goto}
|
1666 | */
|
1667 | async goto(url: string, options?: GoToOptions): Promise<HTTPResponse | null> {
|
1668 | return await this.mainFrame().goto(url, options);
|
1669 | }
|
1670 |
|
1671 | /**
|
1672 | * Reloads the page.
|
1673 | *
|
1674 | * @param options - Options to configure waiting behavior.
|
1675 | * @returns A promise which resolves to the main resource response. In case of
|
1676 | * multiple redirects, the navigation will resolve with the response of the
|
1677 | * last redirect.
|
1678 | */
|
1679 | abstract reload(options?: WaitForOptions): Promise<HTTPResponse | null>;
|
1680 |
|
1681 | /**
|
1682 | * Waits for the page to navigate to a new URL or to reload. It is useful when
|
1683 | * you run code that will indirectly cause the page to navigate.
|
1684 | *
|
1685 | * @example
|
1686 | *
|
1687 | * ```ts
|
1688 | * const [response] = await Promise.all([
|
1689 | * page.waitForNavigation(), // The promise resolves after navigation has finished
|
1690 | * page.click('a.my-link'), // Clicking the link will indirectly cause a navigation
|
1691 | * ]);
|
1692 | * ```
|
1693 | *
|
1694 | * @remarks
|
1695 | *
|
1696 | * Usage of the
|
1697 | * {@link https://developer.mozilla.org/en-US/docs/Web/API/History_API | History API}
|
1698 | * to change the URL is considered a navigation.
|
1699 | *
|
1700 | * @param options - Navigation parameters which might have the following
|
1701 | * properties:
|
1702 | * @returns A `Promise` which resolves to the main resource response.
|
1703 | *
|
1704 | * - In case of multiple redirects, the navigation will resolve with the
|
1705 | * response of the last redirect.
|
1706 | * - In case of navigation to a different anchor or navigation due to History
|
1707 | * API usage, the navigation will resolve with `null`.
|
1708 | */
|
1709 | async waitForNavigation(
|
1710 | options: WaitForOptions = {},
|
1711 | ): Promise<HTTPResponse | null> {
|
1712 | return await this.mainFrame().waitForNavigation(options);
|
1713 | }
|
1714 |
|
1715 | /**
|
1716 | * @param urlOrPredicate - A URL or predicate to wait for
|
1717 | * @param options - Optional waiting parameters
|
1718 | * @returns Promise which resolves to the matched request
|
1719 | * @example
|
1720 | *
|
1721 | * ```ts
|
1722 | * const firstRequest = await page.waitForRequest(
|
1723 | * 'https://example.com/resource',
|
1724 | * );
|
1725 | * const finalRequest = await page.waitForRequest(
|
1726 | * request => request.url() === 'https://example.com',
|
1727 | * );
|
1728 | * return finalRequest.response()?.ok();
|
1729 | * ```
|
1730 | *
|
1731 | * @remarks
|
1732 | * Optional Waiting Parameters have:
|
1733 | *
|
1734 | * - `timeout`: Maximum wait time in milliseconds, defaults to `30` seconds, pass
|
1735 | * `0` to disable the timeout. The default value can be changed by using the
|
1736 | * {@link Page.setDefaultTimeout} method.
|
1737 | */
|
1738 | waitForRequest(
|
1739 | urlOrPredicate: string | AwaitablePredicate<HTTPRequest>,
|
1740 | options: WaitTimeoutOptions = {},
|
1741 | ): Promise<HTTPRequest> {
|
1742 | const {timeout: ms = this._timeoutSettings.timeout(), signal} = options;
|
1743 | if (typeof urlOrPredicate === 'string') {
|
1744 | const url = urlOrPredicate;
|
1745 | urlOrPredicate = (request: HTTPRequest) => {
|
1746 | return request.url() === url;
|
1747 | };
|
1748 | }
|
1749 | const observable$ = fromEmitterEvent(this, PageEvent.Request).pipe(
|
1750 | filterAsync(urlOrPredicate),
|
1751 | raceWith(
|
1752 | timeout(ms),
|
1753 | fromAbortSignal(signal),
|
1754 | fromEmitterEvent(this, PageEvent.Close).pipe(
|
1755 | map(() => {
|
1756 | throw new TargetCloseError('Page closed!');
|
1757 | }),
|
1758 | ),
|
1759 | ),
|
1760 | );
|
1761 | return firstValueFrom(observable$);
|
1762 | }
|
1763 |
|
1764 | /**
|
1765 | * @param urlOrPredicate - A URL or predicate to wait for.
|
1766 | * @param options - Optional waiting parameters
|
1767 | * @returns Promise which resolves to the matched response.
|
1768 | * @example
|
1769 | *
|
1770 | * ```ts
|
1771 | * const firstResponse = await page.waitForResponse(
|
1772 | * 'https://example.com/resource',
|
1773 | * );
|
1774 | * const finalResponse = await page.waitForResponse(
|
1775 | * response =>
|
1776 | * response.url() === 'https://example.com' && response.status() === 200,
|
1777 | * );
|
1778 | * const finalResponse = await page.waitForResponse(async response => {
|
1779 | * return (await response.text()).includes('<html>');
|
1780 | * });
|
1781 | * return finalResponse.ok();
|
1782 | * ```
|
1783 | *
|
1784 | * @remarks
|
1785 | * Optional Parameter have:
|
1786 | *
|
1787 | * - `timeout`: Maximum wait time in milliseconds, defaults to `30` seconds,
|
1788 | * pass `0` to disable the timeout. The default value can be changed by using
|
1789 | * the {@link Page.setDefaultTimeout} method.
|
1790 | */
|
1791 | waitForResponse(
|
1792 | urlOrPredicate: string | AwaitablePredicate<HTTPResponse>,
|
1793 | options: WaitTimeoutOptions = {},
|
1794 | ): Promise<HTTPResponse> {
|
1795 | const {timeout: ms = this._timeoutSettings.timeout(), signal} = options;
|
1796 | if (typeof urlOrPredicate === 'string') {
|
1797 | const url = urlOrPredicate;
|
1798 | urlOrPredicate = (response: HTTPResponse) => {
|
1799 | return response.url() === url;
|
1800 | };
|
1801 | }
|
1802 | const observable$ = fromEmitterEvent(this, PageEvent.Response).pipe(
|
1803 | filterAsync(urlOrPredicate),
|
1804 | raceWith(
|
1805 | timeout(ms),
|
1806 | fromAbortSignal(signal),
|
1807 | fromEmitterEvent(this, PageEvent.Close).pipe(
|
1808 | map(() => {
|
1809 | throw new TargetCloseError('Page closed!');
|
1810 | }),
|
1811 | ),
|
1812 | ),
|
1813 | );
|
1814 | return firstValueFrom(observable$);
|
1815 | }
|
1816 |
|
1817 | /**
|
1818 | * Waits for the network to be idle.
|
1819 | *
|
1820 | * @param options - Options to configure waiting behavior.
|
1821 | * @returns A promise which resolves once the network is idle.
|
1822 | */
|
1823 | waitForNetworkIdle(options: WaitForNetworkIdleOptions = {}): Promise<void> {
|
1824 | return firstValueFrom(this.waitForNetworkIdle$(options));
|
1825 | }
|
1826 |
|
1827 | /**
|
1828 | * @internal
|
1829 | */
|
1830 | waitForNetworkIdle$(
|
1831 | options: WaitForNetworkIdleOptions = {},
|
1832 | ): Observable<void> {
|
1833 | const {
|
1834 | timeout: ms = this._timeoutSettings.timeout(),
|
1835 | idleTime = NETWORK_IDLE_TIME,
|
1836 | concurrency = 0,
|
1837 | signal,
|
1838 | } = options;
|
1839 |
|
1840 | return this.#inflight$.pipe(
|
1841 | switchMap(inflight => {
|
1842 | if (inflight > concurrency) {
|
1843 | return EMPTY;
|
1844 | }
|
1845 | return timer(idleTime);
|
1846 | }),
|
1847 | map(() => {}),
|
1848 | raceWith(
|
1849 | timeout(ms),
|
1850 | fromAbortSignal(signal),
|
1851 | fromEmitterEvent(this, PageEvent.Close).pipe(
|
1852 | map(() => {
|
1853 | throw new TargetCloseError('Page closed!');
|
1854 | }),
|
1855 | ),
|
1856 | ),
|
1857 | );
|
1858 | }
|
1859 |
|
1860 | /**
|
1861 | * Waits for a frame matching the given conditions to appear.
|
1862 | *
|
1863 | * @example
|
1864 | *
|
1865 | * ```ts
|
1866 | * const frame = await page.waitForFrame(async frame => {
|
1867 | * return frame.name() === 'Test';
|
1868 | * });
|
1869 | * ```
|
1870 | */
|
1871 | async waitForFrame(
|
1872 | urlOrPredicate: string | ((frame: Frame) => Awaitable<boolean>),
|
1873 | options: WaitTimeoutOptions = {},
|
1874 | ): Promise<Frame> {
|
1875 | const {timeout: ms = this.getDefaultTimeout(), signal} = options;
|
1876 |
|
1877 | const predicate = isString(urlOrPredicate)
|
1878 | ? (frame: Frame) => {
|
1879 | return urlOrPredicate === frame.url();
|
1880 | }
|
1881 | : urlOrPredicate;
|
1882 |
|
1883 | return await firstValueFrom(
|
1884 | merge(
|
1885 | fromEmitterEvent(this, PageEvent.FrameAttached),
|
1886 | fromEmitterEvent(this, PageEvent.FrameNavigated),
|
1887 | from(this.frames()),
|
1888 | ).pipe(
|
1889 | filterAsync(predicate),
|
1890 | first(),
|
1891 | raceWith(
|
1892 | timeout(ms),
|
1893 | fromAbortSignal(signal),
|
1894 | fromEmitterEvent(this, PageEvent.Close).pipe(
|
1895 | map(() => {
|
1896 | throw new TargetCloseError('Page closed.');
|
1897 | }),
|
1898 | ),
|
1899 | ),
|
1900 | ),
|
1901 | );
|
1902 | }
|
1903 |
|
1904 | /**
|
1905 | * This method navigate to the previous page in history.
|
1906 | * @param options - Navigation parameters
|
1907 | * @returns Promise which resolves to the main resource response. In case of
|
1908 | * multiple redirects, the navigation will resolve with the response of the
|
1909 | * last redirect. If can not go back, resolves to `null`.
|
1910 | */
|
1911 | abstract goBack(options?: WaitForOptions): Promise<HTTPResponse | null>;
|
1912 |
|
1913 | /**
|
1914 | * This method navigate to the next page in history.
|
1915 | * @param options - Navigation Parameter
|
1916 | * @returns Promise which resolves to the main resource response. In case of
|
1917 | * multiple redirects, the navigation will resolve with the response of the
|
1918 | * last redirect. If can not go forward, resolves to `null`.
|
1919 | */
|
1920 | abstract goForward(options?: WaitForOptions): Promise<HTTPResponse | null>;
|
1921 |
|
1922 | /**
|
1923 | * Brings page to front (activates tab).
|
1924 | */
|
1925 | abstract bringToFront(): Promise<void>;
|
1926 |
|
1927 | /**
|
1928 | * Emulates a given device's metrics and user agent.
|
1929 | *
|
1930 | * To aid emulation, Puppeteer provides a list of known devices that can be
|
1931 | * via {@link KnownDevices}.
|
1932 | *
|
1933 | * @remarks
|
1934 | * This method is a shortcut for calling two methods:
|
1935 | * {@link Page.setUserAgent} and {@link Page.setViewport}.
|
1936 | *
|
1937 | * This method will resize the page. A lot of websites don't expect phones to
|
1938 | * change size, so you should emulate before navigating to the page.
|
1939 | *
|
1940 | * @example
|
1941 | *
|
1942 | * ```ts
|
1943 | * import {KnownDevices} from 'puppeteer';
|
1944 | * const iPhone = KnownDevices['iPhone 15 Pro'];
|
1945 | *
|
1946 | * (async () => {
|
1947 | * const browser = await puppeteer.launch();
|
1948 | * const page = await browser.newPage();
|
1949 | * await page.emulate(iPhone);
|
1950 | * await page.goto('https://www.google.com');
|
1951 | * // other actions...
|
1952 | * await browser.close();
|
1953 | * })();
|
1954 | * ```
|
1955 | */
|
1956 | async emulate(device: Device): Promise<void> {
|
1957 | await Promise.all([
|
1958 | this.setUserAgent(device.userAgent),
|
1959 | this.setViewport(device.viewport),
|
1960 | ]);
|
1961 | }
|
1962 |
|
1963 | /**
|
1964 | * @param enabled - Whether or not to enable JavaScript on the page.
|
1965 | * @remarks
|
1966 | * NOTE: changing this value won't affect scripts that have already been run.
|
1967 | * It will take full effect on the next navigation.
|
1968 | */
|
1969 | abstract setJavaScriptEnabled(enabled: boolean): Promise<void>;
|
1970 |
|
1971 | /**
|
1972 | * Toggles bypassing page's Content-Security-Policy.
|
1973 | * @param enabled - sets bypassing of page's Content-Security-Policy.
|
1974 | * @remarks
|
1975 | * NOTE: CSP bypassing happens at the moment of CSP initialization rather than
|
1976 | * evaluation. Usually, this means that `page.setBypassCSP` should be called
|
1977 | * before navigating to the domain.
|
1978 | */
|
1979 | abstract setBypassCSP(enabled: boolean): Promise<void>;
|
1980 |
|
1981 | /**
|
1982 | * @param type - Changes the CSS media type of the page. The only allowed
|
1983 | * values are `screen`, `print` and `null`. Passing `null` disables CSS media
|
1984 | * emulation.
|
1985 | * @example
|
1986 | *
|
1987 | * ```ts
|
1988 | * await page.evaluate(() => matchMedia('screen').matches);
|
1989 | * // → true
|
1990 | * await page.evaluate(() => matchMedia('print').matches);
|
1991 | * // → false
|
1992 | *
|
1993 | * await page.emulateMediaType('print');
|
1994 | * await page.evaluate(() => matchMedia('screen').matches);
|
1995 | * // → false
|
1996 | * await page.evaluate(() => matchMedia('print').matches);
|
1997 | * // → true
|
1998 | *
|
1999 | * await page.emulateMediaType(null);
|
2000 | * await page.evaluate(() => matchMedia('screen').matches);
|
2001 | * // → true
|
2002 | * await page.evaluate(() => matchMedia('print').matches);
|
2003 | * // → false
|
2004 | * ```
|
2005 | */
|
2006 | abstract emulateMediaType(type?: string): Promise<void>;
|
2007 |
|
2008 | /**
|
2009 | * Enables CPU throttling to emulate slow CPUs.
|
2010 | * @param factor - slowdown factor (1 is no throttle, 2 is 2x slowdown, etc).
|
2011 | */
|
2012 | abstract emulateCPUThrottling(factor: number | null): Promise<void>;
|
2013 |
|
2014 | /**
|
2015 | * @param features - `<?Array<Object>>` Given an array of media feature
|
2016 | * objects, emulates CSS media features on the page. Each media feature object
|
2017 | * must have the following properties:
|
2018 | * @example
|
2019 | *
|
2020 | * ```ts
|
2021 | * await page.emulateMediaFeatures([
|
2022 | * {name: 'prefers-color-scheme', value: 'dark'},
|
2023 | * ]);
|
2024 | * await page.evaluate(
|
2025 | * () => matchMedia('(prefers-color-scheme: dark)').matches,
|
2026 | * );
|
2027 | * // → true
|
2028 | * await page.evaluate(
|
2029 | * () => matchMedia('(prefers-color-scheme: light)').matches,
|
2030 | * );
|
2031 | * // → false
|
2032 | *
|
2033 | * await page.emulateMediaFeatures([
|
2034 | * {name: 'prefers-reduced-motion', value: 'reduce'},
|
2035 | * ]);
|
2036 | * await page.evaluate(
|
2037 | * () => matchMedia('(prefers-reduced-motion: reduce)').matches,
|
2038 | * );
|
2039 | * // → true
|
2040 | * await page.evaluate(
|
2041 | * () => matchMedia('(prefers-reduced-motion: no-preference)').matches,
|
2042 | * );
|
2043 | * // → false
|
2044 | *
|
2045 | * await page.emulateMediaFeatures([
|
2046 | * {name: 'prefers-color-scheme', value: 'dark'},
|
2047 | * {name: 'prefers-reduced-motion', value: 'reduce'},
|
2048 | * ]);
|
2049 | * await page.evaluate(
|
2050 | * () => matchMedia('(prefers-color-scheme: dark)').matches,
|
2051 | * );
|
2052 | * // → true
|
2053 | * await page.evaluate(
|
2054 | * () => matchMedia('(prefers-color-scheme: light)').matches,
|
2055 | * );
|
2056 | * // → false
|
2057 | * await page.evaluate(
|
2058 | * () => matchMedia('(prefers-reduced-motion: reduce)').matches,
|
2059 | * );
|
2060 | * // → true
|
2061 | * await page.evaluate(
|
2062 | * () => matchMedia('(prefers-reduced-motion: no-preference)').matches,
|
2063 | * );
|
2064 | * // → false
|
2065 | *
|
2066 | * await page.emulateMediaFeatures([{name: 'color-gamut', value: 'p3'}]);
|
2067 | * await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches);
|
2068 | * // → true
|
2069 | * await page.evaluate(() => matchMedia('(color-gamut: p3)').matches);
|
2070 | * // → true
|
2071 | * await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches);
|
2072 | * // → false
|
2073 | * ```
|
2074 | */
|
2075 | abstract emulateMediaFeatures(features?: MediaFeature[]): Promise<void>;
|
2076 |
|
2077 | /**
|
2078 | * @param timezoneId - Changes the timezone of the page. See
|
2079 | * {@link https://source.chromium.org/chromium/chromium/deps/icu.git/+/faee8bc70570192d82d2978a71e2a615788597d1:source/data/misc/metaZones.txt | ICU’s metaZones.txt}
|
2080 | * for a list of supported timezone IDs. Passing
|
2081 | * `null` disables timezone emulation.
|
2082 | */
|
2083 | abstract emulateTimezone(timezoneId?: string): Promise<void>;
|
2084 |
|
2085 | /**
|
2086 | * Emulates the idle state.
|
2087 | * If no arguments set, clears idle state emulation.
|
2088 | *
|
2089 | * @example
|
2090 | *
|
2091 | * ```ts
|
2092 | * // set idle emulation
|
2093 | * await page.emulateIdleState({isUserActive: true, isScreenUnlocked: false});
|
2094 | *
|
2095 | * // do some checks here
|
2096 | * ...
|
2097 | *
|
2098 | * // clear idle emulation
|
2099 | * await page.emulateIdleState();
|
2100 | * ```
|
2101 | *
|
2102 | * @param overrides - Mock idle state. If not set, clears idle overrides
|
2103 | */
|
2104 | abstract emulateIdleState(overrides?: {
|
2105 | isUserActive: boolean;
|
2106 | isScreenUnlocked: boolean;
|
2107 | }): Promise<void>;
|
2108 |
|
2109 | /**
|
2110 | * Simulates the given vision deficiency on the page.
|
2111 | *
|
2112 | * @example
|
2113 | *
|
2114 | * ```ts
|
2115 | * import puppeteer from 'puppeteer';
|
2116 | *
|
2117 | * (async () => {
|
2118 | * const browser = await puppeteer.launch();
|
2119 | * const page = await browser.newPage();
|
2120 | * await page.goto('https://v8.dev/blog/10-years');
|
2121 | *
|
2122 | * await page.emulateVisionDeficiency('achromatopsia');
|
2123 | * await page.screenshot({path: 'achromatopsia.png'});
|
2124 | *
|
2125 | * await page.emulateVisionDeficiency('deuteranopia');
|
2126 | * await page.screenshot({path: 'deuteranopia.png'});
|
2127 | *
|
2128 | * await page.emulateVisionDeficiency('blurredVision');
|
2129 | * await page.screenshot({path: 'blurred-vision.png'});
|
2130 | *
|
2131 | * await browser.close();
|
2132 | * })();
|
2133 | * ```
|
2134 | *
|
2135 | * @param type - the type of deficiency to simulate, or `'none'` to reset.
|
2136 | */
|
2137 | abstract emulateVisionDeficiency(
|
2138 | type?: Protocol.Emulation.SetEmulatedVisionDeficiencyRequest['type'],
|
2139 | ): Promise<void>;
|
2140 |
|
2141 | /**
|
2142 | * `page.setViewport` will resize the page. A lot of websites don't expect
|
2143 | * phones to change size, so you should set the viewport before navigating to
|
2144 | * the page.
|
2145 | *
|
2146 | * In the case of multiple pages in a single browser, each page can have its
|
2147 | * own viewport size. Setting the viewport to `null` resets the viewport to
|
2148 | * its default value.
|
2149 | *
|
2150 | * @example
|
2151 | *
|
2152 | * ```ts
|
2153 | * const page = await browser.newPage();
|
2154 | * await page.setViewport({
|
2155 | * width: 640,
|
2156 | * height: 480,
|
2157 | * deviceScaleFactor: 1,
|
2158 | * });
|
2159 | * await page.goto('https://example.com');
|
2160 | * ```
|
2161 | *
|
2162 | * @param viewport -
|
2163 | * @remarks
|
2164 | * NOTE: in certain cases, setting viewport will reload the page in order to
|
2165 | * set the isMobile or hasTouch properties.
|
2166 | */
|
2167 | abstract setViewport(viewport: Viewport | null): Promise<void>;
|
2168 |
|
2169 | /**
|
2170 | * Returns the current page viewport settings without checking the actual page
|
2171 | * viewport.
|
2172 | *
|
2173 | * This is either the viewport set with the previous {@link Page.setViewport}
|
2174 | * call or the default viewport set via
|
2175 | * {@link ConnectOptions.defaultViewport |
|
2176 | * ConnectOptions.defaultViewport}.
|
2177 | */
|
2178 | abstract viewport(): Viewport | null;
|
2179 |
|
2180 | /**
|
2181 | * Evaluates a function in the page's context and returns the result.
|
2182 | *
|
2183 | * If the function passed to `page.evaluate` returns a Promise, the
|
2184 | * function will wait for the promise to resolve and return its value.
|
2185 | *
|
2186 | * @example
|
2187 | *
|
2188 | * ```ts
|
2189 | * const result = await frame.evaluate(() => {
|
2190 | * return Promise.resolve(8 * 7);
|
2191 | * });
|
2192 | * console.log(result); // prints "56"
|
2193 | * ```
|
2194 | *
|
2195 | * You can pass a string instead of a function (although functions are
|
2196 | * recommended as they are easier to debug and use with TypeScript):
|
2197 | *
|
2198 | * @example
|
2199 | *
|
2200 | * ```ts
|
2201 | * const aHandle = await page.evaluate('1 + 2');
|
2202 | * ```
|
2203 | *
|
2204 | * To get the best TypeScript experience, you should pass in as the
|
2205 | * generic the type of `pageFunction`:
|
2206 | *
|
2207 | * ```ts
|
2208 | * const aHandle = await page.evaluate(() => 2);
|
2209 | * ```
|
2210 | *
|
2211 | * @example
|
2212 | *
|
2213 | * {@link ElementHandle} instances (including {@link JSHandle}s) can be passed
|
2214 | * as arguments to the `pageFunction`:
|
2215 | *
|
2216 | * ```ts
|
2217 | * const bodyHandle = await page.$('body');
|
2218 | * const html = await page.evaluate(body => body.innerHTML, bodyHandle);
|
2219 | * await bodyHandle.dispose();
|
2220 | * ```
|
2221 | *
|
2222 | * @param pageFunction - a function that is run within the page
|
2223 | * @param args - arguments to be passed to the pageFunction
|
2224 | *
|
2225 | * @returns the return value of `pageFunction`.
|
2226 | */
|
2227 | async evaluate<
|
2228 | Params extends unknown[],
|
2229 | Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
2230 | >(
|
2231 | pageFunction: Func | string,
|
2232 | ...args: Params
|
2233 | ): Promise<Awaited<ReturnType<Func>>> {
|
2234 | pageFunction = withSourcePuppeteerURLIfNone(
|
2235 | this.evaluate.name,
|
2236 | pageFunction,
|
2237 | );
|
2238 | return await this.mainFrame().evaluate(pageFunction, ...args);
|
2239 | }
|
2240 |
|
2241 | /**
|
2242 | * Adds a function which would be invoked in one of the following scenarios:
|
2243 | *
|
2244 | * - whenever the page is navigated
|
2245 | *
|
2246 | * - whenever the child frame is attached or navigated. In this case, the
|
2247 | * function is invoked in the context of the newly attached frame.
|
2248 | *
|
2249 | * The function is invoked after the document was created but before any of
|
2250 | * its scripts were run. This is useful to amend the JavaScript environment,
|
2251 | * e.g. to seed `Math.random`.
|
2252 | * @param pageFunction - Function to be evaluated in browser context
|
2253 | * @param args - Arguments to pass to `pageFunction`
|
2254 | * @example
|
2255 | * An example of overriding the navigator.languages property before the page loads:
|
2256 | *
|
2257 | * ```ts
|
2258 | * // preload.js
|
2259 | *
|
2260 | * // overwrite the `languages` property to use a custom getter
|
2261 | * Object.defineProperty(navigator, 'languages', {
|
2262 | * get: function () {
|
2263 | * return ['en-US', 'en', 'bn'];
|
2264 | * },
|
2265 | * });
|
2266 | *
|
2267 | * // In your puppeteer script, assuming the preload.js file is
|
2268 | * // in same folder of our script.
|
2269 | * const preloadFile = fs.readFileSync('./preload.js', 'utf8');
|
2270 | * await page.evaluateOnNewDocument(preloadFile);
|
2271 | * ```
|
2272 | */
|
2273 | abstract evaluateOnNewDocument<
|
2274 | Params extends unknown[],
|
2275 | Func extends (...args: Params) => unknown = (...args: Params) => unknown,
|
2276 | >(
|
2277 | pageFunction: Func | string,
|
2278 | ...args: Params
|
2279 | ): Promise<NewDocumentScriptEvaluation>;
|
2280 |
|
2281 | /**
|
2282 | * Removes script that injected into page by Page.evaluateOnNewDocument.
|
2283 | *
|
2284 | * @param identifier - script identifier
|
2285 | */
|
2286 | abstract removeScriptToEvaluateOnNewDocument(
|
2287 | identifier: string,
|
2288 | ): Promise<void>;
|
2289 |
|
2290 | /**
|
2291 | * Toggles ignoring cache for each request based on the enabled state. By
|
2292 | * default, caching is enabled.
|
2293 | * @param enabled - sets the `enabled` state of cache
|
2294 | * @defaultValue `true`
|
2295 | */
|
2296 | abstract setCacheEnabled(enabled?: boolean): Promise<void>;
|
2297 |
|
2298 | /**
|
2299 | * @internal
|
2300 | */
|
2301 | async _maybeWriteTypedArrayToFile(
|
2302 | path: string | undefined,
|
2303 | typedArray: Uint8Array,
|
2304 | ): Promise<void> {
|
2305 | if (!path) {
|
2306 | return;
|
2307 | }
|
2308 |
|
2309 | await environment.value.fs.promises.writeFile(path, typedArray);
|
2310 | }
|
2311 |
|
2312 | /**
|
2313 | * Captures a screencast of this {@link Page | page}.
|
2314 | *
|
2315 | * @example
|
2316 | * Recording a {@link Page | page}:
|
2317 | *
|
2318 | * ```
|
2319 | * import puppeteer from 'puppeteer';
|
2320 | *
|
2321 | * // Launch a browser
|
2322 | * const browser = await puppeteer.launch();
|
2323 | *
|
2324 | * // Create a new page
|
2325 | * const page = await browser.newPage();
|
2326 | *
|
2327 | * // Go to your site.
|
2328 | * await page.goto("https://www.example.com");
|
2329 | *
|
2330 | * // Start recording.
|
2331 | * const recorder = await page.screencast({path: 'recording.webm'});
|
2332 | *
|
2333 | * // Do something.
|
2334 | *
|
2335 | * // Stop recording.
|
2336 | * await recorder.stop();
|
2337 | *
|
2338 | * browser.close();
|
2339 | * ```
|
2340 | *
|
2341 | * @param options - Configures screencast behavior.
|
2342 | *
|
2343 | * @experimental
|
2344 | *
|
2345 | * @remarks
|
2346 | *
|
2347 | * All recordings will be {@link https://www.webmproject.org/ | WebM} format using
|
2348 | * the {@link https://www.webmproject.org/vp9/ | VP9} video codec. The FPS is 30.
|
2349 | *
|
2350 | * You must have {@link https://ffmpeg.org/ | ffmpeg} installed on your system.
|
2351 | */
|
2352 | async screencast(
|
2353 | options: Readonly<ScreencastOptions> = {},
|
2354 | ): Promise<ScreenRecorder> {
|
2355 | const ScreenRecorder = environment.value.ScreenRecorder;
|
2356 | const [width, height, devicePixelRatio] =
|
2357 | await this.#getNativePixelDimensions();
|
2358 | let crop: BoundingBox | undefined;
|
2359 | if (options.crop) {
|
2360 | const {
|
2361 | x,
|
2362 | y,
|
2363 | width: cropWidth,
|
2364 | height: cropHeight,
|
2365 | } = roundRectangle(normalizeRectangle(options.crop));
|
2366 | if (x < 0 || y < 0) {
|
2367 | throw new Error(
|
2368 | `\`crop.x\` and \`crop.y\` must be greater than or equal to 0.`,
|
2369 | );
|
2370 | }
|
2371 | if (cropWidth <= 0 || cropHeight <= 0) {
|
2372 | throw new Error(
|
2373 | `\`crop.height\` and \`crop.width\` must be greater than or equal to 0.`,
|
2374 | );
|
2375 | }
|
2376 |
|
2377 | const viewportWidth = width / devicePixelRatio;
|
2378 | const viewportHeight = height / devicePixelRatio;
|
2379 | if (x + cropWidth > viewportWidth) {
|
2380 | throw new Error(
|
2381 | `\`crop.width\` cannot be larger than the viewport width (${viewportWidth}).`,
|
2382 | );
|
2383 | }
|
2384 | if (y + cropHeight > viewportHeight) {
|
2385 | throw new Error(
|
2386 | `\`crop.height\` cannot be larger than the viewport height (${viewportHeight}).`,
|
2387 | );
|
2388 | }
|
2389 |
|
2390 | crop = {
|
2391 | x: x * devicePixelRatio,
|
2392 | y: y * devicePixelRatio,
|
2393 | width: cropWidth * devicePixelRatio,
|
2394 | height: cropHeight * devicePixelRatio,
|
2395 | };
|
2396 | }
|
2397 | if (options.speed !== undefined && options.speed <= 0) {
|
2398 | throw new Error(`\`speed\` must be greater than 0.`);
|
2399 | }
|
2400 | if (options.scale !== undefined && options.scale <= 0) {
|
2401 | throw new Error(`\`scale\` must be greater than 0.`);
|
2402 | }
|
2403 |
|
2404 | const recorder = new ScreenRecorder(this, width, height, {
|
2405 | ...options,
|
2406 | path: options.ffmpegPath,
|
2407 | crop,
|
2408 | });
|
2409 | try {
|
2410 | await this._startScreencast();
|
2411 | } catch (error) {
|
2412 | void recorder.stop();
|
2413 | throw error;
|
2414 | }
|
2415 | if (options.path) {
|
2416 | const {createWriteStream} = environment.value.fs;
|
2417 | const stream = createWriteStream(options.path, 'binary');
|
2418 | recorder.pipe(stream);
|
2419 | }
|
2420 | return recorder;
|
2421 | }
|
2422 |
|
2423 | #screencastSessionCount = 0;
|
2424 | #startScreencastPromise: Promise<void> | undefined;
|
2425 |
|
2426 | /**
|
2427 | * @internal
|
2428 | */
|
2429 | async _startScreencast(): Promise<void> {
|
2430 | ++this.#screencastSessionCount;
|
2431 | if (!this.#startScreencastPromise) {
|
2432 | this.#startScreencastPromise = this.mainFrame()
|
2433 | .client.send('Page.startScreencast', {format: 'png'})
|
2434 | .then(() => {
|
2435 | // Wait for the first frame.
|
2436 | return new Promise(resolve => {
|
2437 | return this.mainFrame().client.once('Page.screencastFrame', () => {
|
2438 | return resolve();
|
2439 | });
|
2440 | });
|
2441 | });
|
2442 | }
|
2443 | await this.#startScreencastPromise;
|
2444 | }
|
2445 |
|
2446 | /**
|
2447 | * @internal
|
2448 | */
|
2449 | async _stopScreencast(): Promise<void> {
|
2450 | --this.#screencastSessionCount;
|
2451 | if (!this.#startScreencastPromise) {
|
2452 | return;
|
2453 | }
|
2454 | this.#startScreencastPromise = undefined;
|
2455 | if (this.#screencastSessionCount === 0) {
|
2456 | await this.mainFrame().client.send('Page.stopScreencast');
|
2457 | }
|
2458 | }
|
2459 |
|
2460 | /**
|
2461 | * Gets the native, non-emulated dimensions of the viewport.
|
2462 | */
|
2463 | async #getNativePixelDimensions(): Promise<
|
2464 | readonly [width: number, height: number, devicePixelRatio: number]
|
2465 | > {
|
2466 | const viewport = this.viewport();
|
2467 | using stack = new DisposableStack();
|
2468 | if (viewport && viewport.deviceScaleFactor !== 0) {
|
2469 | await this.setViewport({...viewport, deviceScaleFactor: 0});
|
2470 | stack.defer(() => {
|
2471 | void this.setViewport(viewport).catch(debugError);
|
2472 | });
|
2473 | }
|
2474 | return await this.mainFrame()
|
2475 | .isolatedRealm()
|
2476 | .evaluate(() => {
|
2477 | return [
|
2478 | window.visualViewport!.width * window.devicePixelRatio,
|
2479 | window.visualViewport!.height * window.devicePixelRatio,
|
2480 | window.devicePixelRatio,
|
2481 | ] as const;
|
2482 | });
|
2483 | }
|
2484 |
|
2485 | /**
|
2486 | * Captures a screenshot of this {@link Page | page}.
|
2487 | *
|
2488 | * @param options - Configures screenshot behavior.
|
2489 | *
|
2490 | * @remarks
|
2491 | *
|
2492 | * While a screenshot is being taken in a {@link BrowserContext}, the
|
2493 | * following methods will automatically wait for the screenshot to
|
2494 | * finish to prevent interference with the screenshot process:
|
2495 | * {@link BrowserContext.newPage}, {@link Browser.newPage},
|
2496 | * {@link Page.close}.
|
2497 | *
|
2498 | * Calling {@link Page.bringToFront} will not wait for existing
|
2499 | * screenshot operations.
|
2500 | *
|
2501 | */
|
2502 | async screenshot(
|
2503 | options: Readonly<ScreenshotOptions> & {encoding: 'base64'},
|
2504 | ): Promise<string>;
|
2505 | async screenshot(options?: Readonly<ScreenshotOptions>): Promise<Uint8Array>;
|
2506 | function () {
( |
2507 | return this.browser();
|
2508 | })
|
2509 | async screenshot(
|
2510 | userOptions: Readonly<ScreenshotOptions> = {},
|
2511 | ): Promise<Uint8Array | string> {
|
2512 | using _guard = await this.browserContext().startScreenshot();
|
2513 |
|
2514 | const options = {
|
2515 | ...userOptions,
|
2516 | clip: userOptions.clip
|
2517 | ? {
|
2518 | ...userOptions.clip,
|
2519 | }
|
2520 | : undefined,
|
2521 | };
|
2522 | if (options.type === undefined && options.path !== undefined) {
|
2523 | const filePath = options.path;
|
2524 | // Note we cannot use Node.js here due to browser compatibility.
|
2525 | const extension = filePath
|
2526 | .slice(filePath.lastIndexOf('.') + 1)
|
2527 | .toLowerCase();
|
2528 | switch (extension) {
|
2529 | case 'png':
|
2530 | options.type = 'png';
|
2531 | break;
|
2532 | case 'jpeg':
|
2533 | case 'jpg':
|
2534 | options.type = 'jpeg';
|
2535 | break;
|
2536 | case 'webp':
|
2537 | options.type = 'webp';
|
2538 | break;
|
2539 | }
|
2540 | }
|
2541 | if (options.quality !== undefined) {
|
2542 | if (options.quality < 0 || options.quality > 100) {
|
2543 | throw new Error(
|
2544 | `Expected 'quality' (${options.quality}) to be between 0 and 100, inclusive.`,
|
2545 | );
|
2546 | }
|
2547 | if (
|
2548 | options.type === undefined ||
|
2549 | !['jpeg', 'webp'].includes(options.type)
|
2550 | ) {
|
2551 | throw new Error(
|
2552 | `${options.type ?? 'png'} screenshots do not support 'quality'.`,
|
2553 | );
|
2554 | }
|
2555 | }
|
2556 | if (options.clip) {
|
2557 | if (options.clip.width <= 0) {
|
2558 | throw new Error("'width' in 'clip' must be positive.");
|
2559 | }
|
2560 | if (options.clip.height <= 0) {
|
2561 | throw new Error("'height' in 'clip' must be positive.");
|
2562 | }
|
2563 | }
|
2564 |
|
2565 | setDefaultScreenshotOptions(options);
|
2566 |
|
2567 | await using stack = new AsyncDisposableStack();
|
2568 | if (options.clip) {
|
2569 | if (options.fullPage) {
|
2570 | throw new Error("'clip' and 'fullPage' are mutually exclusive");
|
2571 | }
|
2572 |
|
2573 | options.clip = roundRectangle(normalizeRectangle(options.clip));
|
2574 | } else {
|
2575 | if (options.fullPage) {
|
2576 | // If `captureBeyondViewport` is `false`, then we set the viewport to
|
2577 | // capture the full page. Note this may be affected by on-page CSS and
|
2578 | // JavaScript.
|
2579 | if (!options.captureBeyondViewport) {
|
2580 | const scrollDimensions = await this.mainFrame()
|
2581 | .isolatedRealm()
|
2582 | .evaluate(() => {
|
2583 | const element = document.documentElement;
|
2584 | return {
|
2585 | width: element.scrollWidth,
|
2586 | height: element.scrollHeight,
|
2587 | };
|
2588 | });
|
2589 | const viewport = this.viewport();
|
2590 | await this.setViewport({
|
2591 | ...viewport,
|
2592 | ...scrollDimensions,
|
2593 | });
|
2594 | stack.defer(async () => {
|
2595 | await this.setViewport(viewport).catch(debugError);
|
2596 | });
|
2597 | }
|
2598 | } else {
|
2599 | options.captureBeyondViewport = false;
|
2600 | }
|
2601 | }
|
2602 |
|
2603 | const data = await this._screenshot(options);
|
2604 | if (options.encoding === 'base64') {
|
2605 | return data;
|
2606 | }
|
2607 |
|
2608 | const typedArray = stringToTypedArray(data, true);
|
2609 | await this._maybeWriteTypedArrayToFile(options.path, typedArray);
|
2610 | return typedArray;
|
2611 | }
|
2612 |
|
2613 | /**
|
2614 | * @internal
|
2615 | */
|
2616 | abstract _screenshot(options: Readonly<ScreenshotOptions>): Promise<string>;
|
2617 |
|
2618 | /**
|
2619 | * Generates a PDF of the page with the `print` CSS media type.
|
2620 | *
|
2621 | * @param options - options for generating the PDF.
|
2622 | *
|
2623 | * @remarks
|
2624 | *
|
2625 | * To generate a PDF with the `screen` media type, call
|
2626 | * {@link Page.emulateMediaType | `page.emulateMediaType('screen')`} before
|
2627 | * calling `page.pdf()`.
|
2628 | *
|
2629 | * By default, `page.pdf()` generates a pdf with modified colors for printing.
|
2630 | * Use the
|
2631 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-print-color-adjust | `-webkit-print-color-adjust`}
|
2632 | * property to force rendering of exact colors.
|
2633 | */
|
2634 | abstract createPDFStream(
|
2635 | options?: PDFOptions,
|
2636 | ): Promise<ReadableStream<Uint8Array>>;
|
2637 |
|
2638 | /**
|
2639 | * {@inheritDoc Page.createPDFStream}
|
2640 | */
|
2641 | abstract pdf(options?: PDFOptions): Promise<Uint8Array>;
|
2642 |
|
2643 | /**
|
2644 | * The page's title
|
2645 | *
|
2646 | * @remarks
|
2647 | *
|
2648 | * Shortcut for {@link Frame.title | page.mainFrame().title()}.
|
2649 | */
|
2650 | async title(): Promise<string> {
|
2651 | return await this.mainFrame().title();
|
2652 | }
|
2653 |
|
2654 | abstract close(options?: {runBeforeUnload?: boolean}): Promise<void>;
|
2655 |
|
2656 | /**
|
2657 | * Indicates that the page has been closed.
|
2658 | * @returns
|
2659 | */
|
2660 | abstract isClosed(): boolean;
|
2661 |
|
2662 | /**
|
2663 | * {@inheritDoc Mouse}
|
2664 | */
|
2665 | abstract get mouse(): Mouse;
|
2666 |
|
2667 | /**
|
2668 | * This method fetches an element with `selector`, scrolls it into view if
|
2669 | * needed, and then uses {@link Page.mouse} to click in the center of the
|
2670 | * element. If there's no element matching `selector`, the method throws an
|
2671 | * error.
|
2672 | *
|
2673 | * @remarks
|
2674 | *
|
2675 | * Bear in mind that if `click()` triggers a navigation event and
|
2676 | * there's a separate `page.waitForNavigation()` promise to be resolved, you
|
2677 | * may end up with a race condition that yields unexpected results. The
|
2678 | * correct pattern for click and wait for navigation is the following:
|
2679 | *
|
2680 | * ```ts
|
2681 | * const [response] = await Promise.all([
|
2682 | * page.waitForNavigation(waitOptions),
|
2683 | * page.click(selector, clickOptions),
|
2684 | * ]);
|
2685 | * ```
|
2686 | *
|
2687 | * Shortcut for {@link Frame.click | page.mainFrame().click(selector[, options]) }.
|
2688 | * @param selector -
|
2689 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2690 | * to query the page for.
|
2691 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2692 | * can be passed as-is and a
|
2693 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2694 | * allows quering by
|
2695 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2696 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2697 | * and
|
2698 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2699 | * and
|
2700 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2701 | * Alternatively, you can specify the selector type using a
|
2702 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. If there are
|
2703 | * multiple elements satisfying the `selector`, the first will be clicked
|
2704 | * @param options - `Object`
|
2705 | * @returns Promise which resolves when the element matching `selector` is
|
2706 | * successfully clicked. The Promise will be rejected if there is no element
|
2707 | * matching `selector`.
|
2708 | */
|
2709 | click(selector: string, options?: Readonly<ClickOptions>): Promise<void> {
|
2710 | return this.mainFrame().click(selector, options);
|
2711 | }
|
2712 |
|
2713 | /**
|
2714 | * This method fetches an element with `selector` and focuses it. If
|
2715 | * there's no element matching `selector`, the method throws an error.
|
2716 | * @param selector -
|
2717 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2718 | * to query the page for.
|
2719 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2720 | * can be passed as-is and a
|
2721 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2722 | * allows quering by
|
2723 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2724 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2725 | * and
|
2726 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2727 | * and
|
2728 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2729 | * Alternatively, you can specify the selector type using a
|
2730 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
2731 | * If there are multiple elements satisfying the selector, the first
|
2732 | * will be focused.
|
2733 | * @returns Promise which resolves when the element matching selector
|
2734 | * is successfully focused. The promise will be rejected if there is
|
2735 | * no element matching selector.
|
2736 | *
|
2737 | * @remarks
|
2738 | *
|
2739 | * Shortcut for
|
2740 | * {@link Frame.focus | page.mainFrame().focus(selector)}.
|
2741 | */
|
2742 | focus(selector: string): Promise<void> {
|
2743 | return this.mainFrame().focus(selector);
|
2744 | }
|
2745 |
|
2746 | /**
|
2747 | * This method fetches an element with `selector`, scrolls it into view if
|
2748 | * needed, and then uses {@link Page.mouse}
|
2749 | * to hover over the center of the element.
|
2750 | * If there's no element matching `selector`, the method throws an error.
|
2751 | * @param selector -
|
2752 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2753 | * to query the page for.
|
2754 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2755 | * can be passed as-is and a
|
2756 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2757 | * allows quering by
|
2758 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2759 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2760 | * and
|
2761 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2762 | * and
|
2763 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2764 | * Alternatively, you can specify the selector type using a
|
2765 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. If there are
|
2766 | * multiple elements satisfying the `selector`, the first will be hovered.
|
2767 | * @returns Promise which resolves when the element matching `selector` is
|
2768 | * successfully hovered. Promise gets rejected if there's no element matching
|
2769 | * `selector`.
|
2770 | *
|
2771 | * @remarks
|
2772 | *
|
2773 | * Shortcut for {@link Page.hover | page.mainFrame().hover(selector)}.
|
2774 | */
|
2775 | hover(selector: string): Promise<void> {
|
2776 | return this.mainFrame().hover(selector);
|
2777 | }
|
2778 |
|
2779 | /**
|
2780 | * Triggers a `change` and `input` event once all the provided options have been
|
2781 | * selected. If there's no `<select>` element matching `selector`, the method
|
2782 | * throws an error.
|
2783 | *
|
2784 | * @example
|
2785 | *
|
2786 | * ```ts
|
2787 | * page.select('select#colors', 'blue'); // single selection
|
2788 | * page.select('select#colors', 'red', 'green', 'blue'); // multiple selections
|
2789 | * ```
|
2790 | *
|
2791 | * @param selector -
|
2792 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2793 | * to query the page for.
|
2794 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2795 | * can be passed as-is and a
|
2796 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2797 | * allows quering by
|
2798 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2799 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2800 | * and
|
2801 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2802 | * and
|
2803 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2804 | * Alternatively, you can specify the selector type using a
|
2805 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
2806 | * @param values - Values of options to select. If the `<select>` has the
|
2807 | * `multiple` attribute, all values are considered, otherwise only the first one
|
2808 | * is taken into account.
|
2809 | * @returns
|
2810 | *
|
2811 | * @remarks
|
2812 | *
|
2813 | * Shortcut for {@link Frame.select | page.mainFrame().select()}
|
2814 | */
|
2815 | select(selector: string, ...values: string[]): Promise<string[]> {
|
2816 | return this.mainFrame().select(selector, ...values);
|
2817 | }
|
2818 |
|
2819 | /**
|
2820 | * This method fetches an element with `selector`, scrolls it into view if
|
2821 | * needed, and then uses {@link Page.touchscreen}
|
2822 | * to tap in the center of the element.
|
2823 | * If there's no element matching `selector`, the method throws an error.
|
2824 | * @param selector -
|
2825 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2826 | * to query the page for.
|
2827 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2828 | * can be passed as-is and a
|
2829 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2830 | * allows quering by
|
2831 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2832 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2833 | * and
|
2834 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2835 | * and
|
2836 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2837 | * Alternatively, you can specify the selector type using a
|
2838 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}. If there are multiple elements satisfying the
|
2839 | * selector, the first will be tapped.
|
2840 | *
|
2841 | * @remarks
|
2842 | *
|
2843 | * Shortcut for {@link Frame.tap | page.mainFrame().tap(selector)}.
|
2844 | */
|
2845 | tap(selector: string): Promise<void> {
|
2846 | return this.mainFrame().tap(selector);
|
2847 | }
|
2848 |
|
2849 | /**
|
2850 | * Sends a `keydown`, `keypress/input`, and `keyup` event for each character
|
2851 | * in the text.
|
2852 | *
|
2853 | * To press a special key, like `Control` or `ArrowDown`, use {@link Keyboard.press}.
|
2854 | * @example
|
2855 | *
|
2856 | * ```ts
|
2857 | * await page.type('#mytextarea', 'Hello');
|
2858 | * // Types instantly
|
2859 | * await page.type('#mytextarea', 'World', {delay: 100});
|
2860 | * // Types slower, like a user
|
2861 | * ```
|
2862 | *
|
2863 | * @param selector -
|
2864 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2865 | * to query the page for.
|
2866 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2867 | * can be passed as-is and a
|
2868 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2869 | * allows quering by
|
2870 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2871 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2872 | * and
|
2873 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2874 | * and
|
2875 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2876 | * Alternatively, you can specify the selector type using a
|
2877 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
2878 | * @param text - A text to type into a focused element.
|
2879 | * @param options - have property `delay` which is the Time to wait between
|
2880 | * key presses in milliseconds. Defaults to `0`.
|
2881 | * @returns
|
2882 | */
|
2883 | type(
|
2884 | selector: string,
|
2885 | text: string,
|
2886 | options?: Readonly<KeyboardTypeOptions>,
|
2887 | ): Promise<void> {
|
2888 | return this.mainFrame().type(selector, text, options);
|
2889 | }
|
2890 |
|
2891 | /**
|
2892 | * Wait for the `selector` to appear in page. If at the moment of calling the
|
2893 | * method the `selector` already exists, the method will return immediately. If
|
2894 | * the `selector` doesn't appear after the `timeout` milliseconds of waiting, the
|
2895 | * function will throw.
|
2896 | *
|
2897 | * @example
|
2898 | * This method works across navigations:
|
2899 | *
|
2900 | * ```ts
|
2901 | * import puppeteer from 'puppeteer';
|
2902 | * (async () => {
|
2903 | * const browser = await puppeteer.launch();
|
2904 | * const page = await browser.newPage();
|
2905 | * let currentURL;
|
2906 | * page
|
2907 | * .waitForSelector('img')
|
2908 | * .then(() => console.log('First URL with image: ' + currentURL));
|
2909 | * for (currentURL of [
|
2910 | * 'https://example.com',
|
2911 | * 'https://google.com',
|
2912 | * 'https://bbc.com',
|
2913 | * ]) {
|
2914 | * await page.goto(currentURL);
|
2915 | * }
|
2916 | * await browser.close();
|
2917 | * })();
|
2918 | * ```
|
2919 | *
|
2920 | * @param selector -
|
2921 | * {@link https://pptr.dev/guides/page-interactions#selectors | selector}
|
2922 | * to query the page for.
|
2923 | * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | CSS selectors}
|
2924 | * can be passed as-is and a
|
2925 | * {@link https://pptr.dev/guides/page-interactions#non-css-selectors | Puppeteer-specific selector syntax}
|
2926 | * allows quering by
|
2927 | * {@link https://pptr.dev/guides/page-interactions#text-selectors--p-text | text},
|
2928 | * {@link https://pptr.dev/guides/page-interactions#aria-selectors--p-aria | a11y role and name},
|
2929 | * and
|
2930 | * {@link https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath | xpath}
|
2931 | * and
|
2932 | * {@link https://pptr.dev/guides/page-interactions#querying-elements-in-shadow-dom | combining these queries across shadow roots}.
|
2933 | * Alternatively, you can specify the selector type using a
|
2934 | * {@link https://pptr.dev/guides/page-interactions#prefixed-selector-syntax | prefix}.
|
2935 | * @param options - Optional waiting parameters
|
2936 | * @returns Promise which resolves when element specified by selector string
|
2937 | * is added to DOM. Resolves to `null` if waiting for hidden: `true` and
|
2938 | * selector is not found in DOM.
|
2939 | *
|
2940 | * @remarks
|
2941 | * The optional Parameter in Arguments `options` are:
|
2942 | *
|
2943 | * - `visible`: A boolean wait for element to be present in DOM and to be
|
2944 | * visible, i.e. to not have `display: none` or `visibility: hidden` CSS
|
2945 | * properties. Defaults to `false`.
|
2946 | *
|
2947 | * - `hidden`: Wait for element to not be found in the DOM or to be hidden,
|
2948 | * i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to
|
2949 | * `false`.
|
2950 | *
|
2951 | * - `timeout`: maximum time to wait for in milliseconds. Defaults to `30000`
|
2952 | * (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
2953 | * by using the {@link Page.setDefaultTimeout} method.
|
2954 | */
|
2955 | async waitForSelector<Selector extends string>(
|
2956 | selector: Selector,
|
2957 | options: WaitForSelectorOptions = {},
|
2958 | ): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
2959 | return await this.mainFrame().waitForSelector(selector, options);
|
2960 | }
|
2961 |
|
2962 | /**
|
2963 | * Waits for the provided function, `pageFunction`, to return a truthy value when
|
2964 | * evaluated in the page's context.
|
2965 | *
|
2966 | * @example
|
2967 | * {@link Page.waitForFunction} can be used to observe a viewport size change:
|
2968 | *
|
2969 | * ```ts
|
2970 | * import puppeteer from 'puppeteer';
|
2971 | * (async () => {
|
2972 | * const browser = await puppeteer.launch();
|
2973 | * const page = await browser.newPage();
|
2974 | * const watchDog = page.waitForFunction('window.innerWidth < 100');
|
2975 | * await page.setViewport({width: 50, height: 50});
|
2976 | * await watchDog;
|
2977 | * await browser.close();
|
2978 | * })();
|
2979 | * ```
|
2980 | *
|
2981 | * @example
|
2982 | * Arguments can be passed from Node.js to `pageFunction`:
|
2983 | *
|
2984 | * ```ts
|
2985 | * const selector = '.foo';
|
2986 | * await page.waitForFunction(
|
2987 | * selector => !!document.querySelector(selector),
|
2988 | * {},
|
2989 | * selector,
|
2990 | * );
|
2991 | * ```
|
2992 | *
|
2993 | * @example
|
2994 | * The provided `pageFunction` can be asynchronous:
|
2995 | *
|
2996 | * ```ts
|
2997 | * const username = 'github-username';
|
2998 | * await page.waitForFunction(
|
2999 | * async username => {
|
3000 | * const githubResponse = await fetch(
|
3001 | * `https://api.github.com/users/${username}`,
|
3002 | * );
|
3003 | * const githubUser = await githubResponse.json();
|
3004 | * // show the avatar
|
3005 | * const img = document.createElement('img');
|
3006 | * img.src = githubUser.avatar_url;
|
3007 | * // wait 3 seconds
|
3008 | * await new Promise((resolve, reject) => setTimeout(resolve, 3000));
|
3009 | * img.remove();
|
3010 | * },
|
3011 | * {},
|
3012 | * username,
|
3013 | * );
|
3014 | * ```
|
3015 | *
|
3016 | * @param pageFunction - Function to be evaluated in browser context until it returns a
|
3017 | * truthy value.
|
3018 | * @param options - Options for configuring waiting behavior.
|
3019 | */
|
3020 | waitForFunction<
|
3021 | Params extends unknown[],
|
3022 | Func extends EvaluateFunc<Params> = EvaluateFunc<Params>,
|
3023 | >(
|
3024 | pageFunction: Func | string,
|
3025 | options?: FrameWaitForFunctionOptions,
|
3026 | ...args: Params
|
3027 | ): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
3028 | return this.mainFrame().waitForFunction(pageFunction, options, ...args);
|
3029 | }
|
3030 |
|
3031 | /**
|
3032 | * This method is typically coupled with an action that triggers a device
|
3033 | * request from an api such as WebBluetooth.
|
3034 | *
|
3035 | * :::caution
|
3036 | *
|
3037 | * This must be called before the device request is made. It will not return a
|
3038 | * currently active device prompt.
|
3039 | *
|
3040 | * :::
|
3041 | *
|
3042 | * @example
|
3043 | *
|
3044 | * ```ts
|
3045 | * const [devicePrompt] = Promise.all([
|
3046 | * page.waitForDevicePrompt(),
|
3047 | * page.click('#connect-bluetooth'),
|
3048 | * ]);
|
3049 | * await devicePrompt.select(
|
3050 | * await devicePrompt.waitForDevice(({name}) => name.includes('My Device')),
|
3051 | * );
|
3052 | * ```
|
3053 | */
|
3054 | abstract waitForDevicePrompt(
|
3055 | options?: WaitTimeoutOptions,
|
3056 | ): Promise<DeviceRequestPrompt>;
|
3057 |
|
3058 | /** @internal */
|
3059 | override [disposeSymbol](): void {
|
3060 | return void this.close().catch(debugError);
|
3061 | }
|
3062 |
|
3063 | /** @internal */
|
3064 | [asyncDisposeSymbol](): Promise<void> {
|
3065 | return this.close();
|
3066 | }
|
3067 | }
|
3068 |
|
3069 | /**
|
3070 | * @internal
|
3071 | */
|
3072 | export const supportedMetrics = new Set<string>([
|
3073 | 'Timestamp',
|
3074 | 'Documents',
|
3075 | 'Frames',
|
3076 | 'JSEventListeners',
|
3077 | 'Nodes',
|
3078 | 'LayoutCount',
|
3079 | 'RecalcStyleCount',
|
3080 | 'LayoutDuration',
|
3081 | 'RecalcStyleDuration',
|
3082 | 'ScriptDuration',
|
3083 | 'TaskDuration',
|
3084 | 'JSHeapUsedSize',
|
3085 | 'JSHeapTotalSize',
|
3086 | ]);
|
3087 |
|
3088 | /** @see https://w3c.github.io/webdriver-bidi/#normalize-rect */
|
3089 | function normalizeRectangle<BoundingBoxType extends BoundingBox>(
|
3090 | clip: Readonly<BoundingBoxType>,
|
3091 | ): BoundingBoxType {
|
3092 | return {
|
3093 | ...clip,
|
3094 | ...(clip.width < 0
|
3095 | ? {
|
3096 | x: clip.x + clip.width,
|
3097 | width: -clip.width,
|
3098 | }
|
3099 | : {
|
3100 | x: clip.x,
|
3101 | width: clip.width,
|
3102 | }),
|
3103 | ...(clip.height < 0
|
3104 | ? {
|
3105 | y: clip.y + clip.height,
|
3106 | height: -clip.height,
|
3107 | }
|
3108 | : {
|
3109 | y: clip.y,
|
3110 | height: clip.height,
|
3111 | }),
|
3112 | };
|
3113 | }
|
3114 |
|
3115 | function roundRectangle<BoundingBoxType extends BoundingBox>(
|
3116 | clip: Readonly<BoundingBoxType>,
|
3117 | ): BoundingBoxType {
|
3118 | const x = Math.round(clip.x);
|
3119 | const y = Math.round(clip.y);
|
3120 | const width = Math.round(clip.width + clip.x - x);
|
3121 | const height = Math.round(clip.height + clip.y - y);
|
3122 | return {...clip, x, y, width, height};
|
3123 | }
|