As it is stated in the documentation of Puppeteer, the basic usage of "dialog" event is the following:
page.on('dialog', async (dialog) => {
await dialog.dismiss()
// or await dialog.accept()
})
I want to loop through a list of URLs each of them firing a confirm dialog. But I want to accept or dismiss the dialog depending of the page content.
I wonder if it is possible?
When I use it in a loop I get an error: "Cannot dismiss dialog which is already handled!"
for (let url in urls) {
if (condition) {
page.on("dialog", async (dialog) => {
await dialog.accept();
});
} else {
page.on("dialog", async (dialog) => {
await dialog.dismiss();
});
}
}
I'm adding a listener on every loop, so I'm getting an error.
But when I move the "dialog" listener out of the loop, I get a "dialog is undefined" error.
page.on("dialog", async (dialog) => {
for (let url in urls) {
if (condition) {
await dialog.accept();
} else {
await dialog.dismiss();
}
}
});
I tried to make a custom event listener.
await page.exposeFunction("test", async (e) => {
// But I don't know how to dismiss or accept the confirm dialog here.
});
await page.evaluate(() => {
window.addEventListener("confirm", window.test());
});
The problem with this approach is that I don't have access to handleJavaScriptDialog
which is responsible for handling the confirm dialog returns:
https://pub.dev/documentation/puppeteer/latest/puppeteer/Dialog/dismiss.html
So far I think the only solution I have is to emulate Enter key press to accept the confirm dialog, or to just go to the next page when I want to dismiss the confirm dialog.
Are there any solutions to using dialog events in a loop like this with Puppeteer?
======
Update
======
//Example for @ggorlen
for (let url in urls) {
await page.goto(url);
const dialogDismissed = new Promise((resolve, reject) => {
const handler = async (dialog) => {
await dialog.dismiss();
resolve(dialog.message());
};
page.on("dialog", handler);
});
const dialogAccepted = new Promise((resolve, reject) => {
const handler = async (dialog) => {
await dialog.accept();
resolve(dialog.message());
};
page.on("dialog", handler);
});
await page.evaluate(() => window.confirm("Yes or No?"));
if (condition) {
//want to accept
//how to handle the dialog promise here?
} else {
//want to dismiss
//how to handle the dialog promise here?
}
}
======
Update 2
======
//Based on @ggorlen answer but without promisifing the handler
const puppeteer = require("puppeteer");
let browser;
(async () => {
const html = `<html><body><script>
document.write(confirm("yes or no?") ? "confirmed" : "rejected");
</script></body></html>`;
browser = await puppeteer.launch({
headless: true,
});
const [page] = await browser.pages();
const urls = ["just", "a", "demo", "replace", "this"];
for (const url of urls) {
const someCondition = Math.random() < 0.5; // for example
//This bloc is in question.
//Is there a need to promisify?
page.once("dialog", async (dialog) => {
console.log(dialog.message());
await (someCondition ? dialog.accept() : dialog.dismiss());
});
//await page.goto(url, {waitUntil: "networkidle0"});
await page.setContent(html);
console.log(await page.$eval("body", (el) => el.innerText));
}
})()
.catch((err) => console.error(err))
.finally(() => browser?.close());