I am trying to code a program in Node.js that uses puppeteer to click a button on a webpage that cause the browser to visit a new webpage and puppeteer has to click a button on the new page. Every time I run the code I get the error Exception has occurred: ProtocolError: Protocol error (Runtime.addBinding): Cannot find execution context with given executionContextId. When I set headless to false I see the code has no problem clicking the first button on the webpage but it encounters an error when trying to clicking the second one.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
const buttonSelector = 'input.btn.ispSubHeader[value="Download All';
try {
// Step 1: Go to the initial page
await page.goto(''https://isp.illinois.gov/Sor/disclaimer'', { waitUntil: 'networkidle2', timeout: 60000 });
// Step 2: Trigger navigation to the new page
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle2', timeout: 60000 }),
page.click('button.btn.ispSubHeader[value="Agree')
]);
// Step 3: Wait for the new page to load
await page.waitForSelector('body', { timeout: 60000 });
// Step 4: Interact with the new page (e.g., click the button)
await page.waitForSelector(buttonSelector, { timeout: 60000 });
// Verify if the button is visible
const buttonVisible = await page.evaluate((selector) => {
const button = document.querySelector(selector);
return button && button.offsetParent !== null;
}, buttonSelector);
if (buttonVisible) {
// Click the button
await page.click(buttonSelector);
// take a screenshot after clicking the button
await page.screenshot({ path: 'after_click.png' });
console.log('Button clicked successfully');
} else {
console.error('Button not visible or not found.');
}
} catch (error) {
console.error('Error occurred:', error);
// take a screenshot if an error occurs
await page.screenshot({ path: 'error_screenshot.png' });
} finally {
await browser.close();
}
})();
Also, in visual code studio when I run the code, another window opens, called Defferred.ts and that’s where the error comes from.
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {TimeoutError} from '../common/Errors.js';
/**
* @internal
*/
export interface DeferredOptions {
message: string;
timeout: number;
}
/**
* Creates and returns a deferred object along with the resolve/reject functions.
*
* If the deferred has not been resolved/rejected within the `timeout` period,
* the deferred gets resolves with a timeout error. `timeout` has to be greater than 0 or
* it is ignored.
*
* @internal
*/
export class Deferred<T, V extends Error = Error> {
static create<R, X extends Error = Error>(
opts?: DeferredOptions
): Deferred<R, X> {
return new Deferred<R, X>(opts);
}
static async race<R>(
awaitables: Array<Promise<R> | Deferred<R>>
): Promise<R> {
const deferredWithTimeout = new Set<Deferred<R>>();
try {
const promises = awaitables.map(value => {
if (value instanceof Deferred) {
if (value.#timeoutId) {
deferredWithTimeout.add(value);
}
return value.valueOrThrow();
}
return value;
});
// eslint-disable-next-line no-restricted-syntax
return await Promise.race(promises);
} finally {
for (const deferred of deferredWithTimeout) {
// We need to stop the timeout else
// Node.JS will keep running the event loop till the
// timer executes
deferred.reject(new Error('Timeout cleared'));
}
}
}
#isResolved = false;
#isRejected = false;
#value: T | V | TimeoutError | undefined;
// SAFETY: This is ensured by #taskPromise.
#resolve!: (value: void) => void;
#taskPromise = new Promise<void>(resolve => {
this.#resolve = resolve;
});
#timeoutId: ReturnType<typeof setTimeout> | undefined;
#timeoutError: TimeoutError | undefined;
constructor(opts?: DeferredOptions) {
if (opts && opts.timeout > 0) {
this.#timeoutError = new TimeoutError(opts.message);
this.#timeoutId = setTimeout(() => {
this.reject(this.#timeoutError!);
}, opts.timeout);
}
}
#finish(value: T | V | TimeoutError) {
clearTimeout(this.#timeoutId);
this.#value = value;
this.#resolve();
}
resolve(value: T): void {
if (this.#isRejected || this.#isResolved) {
return;
}
this.#isResolved = true;
this.#finish(value);
}
reject(error: V | TimeoutError): void {
if (this.#isRejected || this.#isResolved) {
return;
}
this.#isRejected = true;
this.#finish(error);
}
resolved(): boolean {
if (this.#isRejected) console.log('resolved anyway')
return this.#isResolved;
}
finished(): boolean {
return this.#isResolved || this.#isRejected;
}
value(): T | V | TimeoutError | undefined {
return this.#value;
}
#promise: Promise<T> | undefined;
valueOrThrow(): Promise<T> {
if (!this.#promise) {
this.#promise = (async () => {
await this.#taskPromise;
if (this.#isRejected) { //this is where the error shows up
throw this.#value;
}
return this.#value as T;
})();
}
return this.#promise;
}
}