admin 管理员组

文章数量: 1086019

I am testing some forms on a website using Puppeteer. When I run the tests, I noticed the execution gets stuck when trying to navigate between pages. I did a dry run myself manually and realised that the page is issuing a dialog as some of the form inputs have changed without the form being submitted.

It's the message that says:

Leave site?
Changes you made may not be saved.

There are some obvious workarounds to this like I could ensure that the form is submitted each time before navigating to the next page. However, I'd ideally like to just be able to ignore this dialog altogether as I'm just running tests and I don't care that the changes won't be saved.

Is there a way to disable these messages? If not, is there a way to check if there's a dialog open and then dismiss it?

I am testing some forms on a website using Puppeteer. When I run the tests, I noticed the execution gets stuck when trying to navigate between pages. I did a dry run myself manually and realised that the page is issuing a dialog as some of the form inputs have changed without the form being submitted.

It's the message that says:

Leave site?
Changes you made may not be saved.

There are some obvious workarounds to this like I could ensure that the form is submitted each time before navigating to the next page. However, I'd ideally like to just be able to ignore this dialog altogether as I'm just running tests and I don't care that the changes won't be saved.

Is there a way to disable these messages? If not, is there a way to check if there's a dialog open and then dismiss it?

Share Improve this question asked Oct 28, 2020 at 8:57 dashingdovedashingdove 4451 gold badge8 silver badges18 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 6

Try to set window.onbeforeunload = null by puppeteer

await page.evaluate(() => {
  window.onbeforeunload = null;
});

To elaborate on the existing answer, the beforeunload event you're trying to deal with causes a dialog box prompt to appear. This dialog can be handled in the same manner as prompts and alerts, with page.on("dialog", dialog => ...).

The dialog object passed to the handler can be checked for the "beforeunload" type, then you can call dismiss() or accept() depending on the behavior you want. dismiss() stays on the current page, aborting the navigation action, while accept() agrees to leave the site.

Your likely solution is to agree to leave the site with accept():

import puppeteer from "puppeteer"; // ^23.6.0

let browser;
(async () => {
  const html = `
    <h1>a page that prompts before unload</h1>
    <script>
    window.addEventListener("beforeunload", function (e) {
      e.preventDefault();
      (e || window.event).returnValue = "";
      return "";
    });
    </script>
  `;
  browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages();

  const acceptBeforeUnload = dialog => 
    dialog.type() === "beforeunload" && dialog.accept();
  page.on("dialog", acceptBeforeUnload); 

  await page.setContent(html);

  await page.goto("https://www.example.");
  console.log(page.url()); // => => https://www.example.
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

Note that using the existing answer's method of simply setting window.beforeunload = null in an evaluate callback doesn't work for this page. You can test that by removing the page.on and adding the evaluate call above page.goto.

However, for more plex behaviors, like enabling and disabling the unload dynamically, the evaluate approach seems to help avoid abort throws in conjunction with removing a dismiss handler, as this example illustrates:

import puppeteer from "puppeteer";

let browser;
(async () => {
  const html = `
    <h1>a page that prompts before unload</h1>
    <script>
    window.addEventListener("beforeunload", function (e) {
      e.preventDefault();
      (e || window.event).returnValue = "";
      return "";
    });
    </script>
  `;
  browser = await puppeteer.launch({headless: false});
  const [page] = await browser.pages();

  const dismissBeforeUnload = dialog =>
    dialog.type() === "beforeunload" && dialog.dismiss();
  page.on("dialog", dismissBeforeUnload);

  await page.setContent(html);

  // throws Error: net::ERR_ABORTED at https://www.example.
  await page.goto("https://www.example.").catch(() => {});
  console.log(page.url()); // => about:blank

  // add to avoid Error: net::ERR_ABORTED when adding a new handler
  await page.evaluate(() => {
    window.onbeforeunload = null;
  });
  page.off("dialog", dismissBeforeUnload);

  // next unload, we'll accept the dialog
  page.on("dialog", dialog =>
    dialog.type() === "beforeunload" && dialog.accept()
  );

  // this navigation will succeed
  await page.goto("https://www.example.");
  console.log(page.url()); // => https://www.example.
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close());

本文标签: javascriptCan I ignore the quotLeave Sitequot dialog when browsing headless using PuppeteerStack Overflow