javascriptdialogplaywright

Issue while testing dialogs in playwright


I have a page which on a button click shows a dialog with the question if it is OK to redirect to another page. If the user clicks OK, then the page is redirected. In the HTML it looks like this:

  <div class='row'>
       <form onSubmit="myFunction() ">
          <p>show dialog</p><input id='submitter' type='submit' value='OK'>
       </form>
  </div>
        
<!--and further on:-->

<script>
function myFunction() {
    let result = confirm("to fruits page?");
 
    if(result) {
        console.log( 'redirect to fruits');
        document.location.href = "http://localhost:7080/fruits";
}}
</script>

If I test this manually, the browser stays on the same page if 'cancel' is clicked. If OK is click, the page is replaced by the 'fruits' page. Exactly what I want.

I want to test this behaviour with playwright (javascript), but somehow the test fails. I can see from the console output that the dialog is accepted and that the Dialog code is fired, but the assertion fails, testing on text that should appear on the (redirected to) fruits page. The ui shows that playwright still has the original page, not the result of the redirect.

My question is: what can I do to test if the page is properly redirected?

Currently I have this:

test ("test dialog2", async ({page}) => {
   page.on('dialog', async dialog => {
        console.log(dialog.message());
           await dialog.accept();
   });
   await cp.goto();
   const inp = await cp.formsubmit.click();
   const slowExpect = expect.configure({ timeout: 5000 });
   await slowExpect(page.locator('*')).toContainText([/fruit/i]); 
});

When the test is run, I can see in the console both the console.log "to fruits page?" and the console.log 'redirect to fruits', indicating that the page.on 'dialog' fires. But the redirect following te console.log does not seem to take place. In the network activity, I can see the fruits page is loaded and immediately thereafter the original page again...

What am I missing?

thanks in advance!


Solution

  • Your test should work. The problem appears to be that your sample page is broken, even just visiting it in a normal website as a human. If you don't call event.preventDefault(), the form submission overrides the redirect, and the default form action when not provided is to submit the form to the current URL.

    Here's a fixed version that actually redirects when the dialog is accepted and doesn't redirect when the dialog is rejected:

    function maybeRedirect(event) {
      event.preventDefault();
     
      if (confirm("to example page?")) {
        window.location = "https://www.example.com";
      }
    }
    <form onsubmit="maybeRedirect(event)">
      <input type="submit" value="show dialog">
    </form>

    Now your test can be:

    import {expect, test} from "@playwright/test"; // ^1.42.1
    
    const html = `<!DOCTYPE html><html>
    <form onsubmit="maybeRedirect(event)">
      <input type="submit" value="show dialog">
    </form>
    <script>
    function maybeRedirect(event) {
      event.preventDefault();
    
      if (confirm("to example page?")) {
        window.location = "https://www.example.com";
      }
    }
    </script>`;
    
    test("Redirects on accept", async ({page}) => {
      await page.setContent(html);
      page.once("dialog", dialog => dialog.accept());
      const btn = page.getByRole("button", {name: "show dialog"});
      await btn.click();
      await expect(btn).not.toBeVisible();
      await expect(page).toHaveURL("https://www.example.com");
    });
    
    test("Doesn't redirect on reject", async ({page}) => {
      await page.setContent(html);
      page.once("dialog", dialog => dialog.dismiss());
      const btn = page.getByRole("button", {name: "show dialog"});
      const originalUrl = await page.url();
      await btn.click();
      await expect(btn).toBeVisible();
      await expect(page).toHaveURL(originalUrl);
    });
    
    Running 2 tests using 1 worker
    
      ✓  1 pw1.test.js:17:1 › Redirects on accept (265ms)
      ✓  2 pw1.test.js:24:1 › Doesn't redirect on accept (81ms)
    
      2 passed (940ms)
    

    As always, temporarily break your code to ensure the tests fail and catch that regression. For example, comment out the redirect or invert the condition. If the tests still pass on a broken app, they're not testing what they should be and are giving you false positives.

    You can also save the before-navigation URL and use await expect(page).not.toHaveURL(originalUrl); in the positive test, if you don't care where the destination redirect goes, as long as it goes somewhere. Similar to waitForNavigation.

    If you need more complex dialog assertions, see these answers.