skip to Main Content

I am writing a JavaScript application and using Playwright to test my application.

I found a bug in my code such that on selecting an item in a list, my app got stuck in an infinite loop calling a server API.

How can you write a playwright tests to ensure an action only causes a single server call.

Sample Code

I wait for the item to be visible using something like the following

await page.locator('list1','file1').waitFor();

Then I select and wait for the server request with code similar to beloe

const responsePromise = page.waitForResponse(resp => 
resp.url().includes('/myServerURL') && resp.status() === 200);
await page.locator('list1','file1').click();
const response = await responsePromise;

But i want to make sure that I only get one request to ‘/myServerURL’ on selecting the item, and not more than one.

Is this possible in Playwright

2

Answers


  1. This is certainly possible:

    import {setTimeout} from "node:timers/promises";
    import {expect, test} from "@playwright/test"; // ^1.46.1
    
    const html = `<!DOCTYPE html><html><body>
    <button>click</button>
    <script>
    document.querySelector("button").addEventListener("click", () => {
      fetch("https://jsonplaceholder.typicode.com/users");
    
      // remove this extra request to see the test pass
      fetch("https://jsonplaceholder.typicode.com/users");
    });
    </script>
    </body>`;
    
    test("Only sends one request on button press", async ({page}) => {
      let requests = 0;
      const handleRequest = req => {
        if (req.method() === "GET" && req.url().includes("/users")) {
          requests++;
        }
      };
    
      await page.setContent(html);
      page.on("request", handleRequest);
      await page.locator("button").click();
      await setTimeout(100); // adjust to taste if there's async behavior
      await expect(() => expect(requests).toBe(1)).toPass({
        timeout: 1000,
      });
      page.off("request", handleRequest);
    });
    

    But I don’t think this is a good idea. It’s cumbersome to code (although you can write a helper function), and strikes me as overly paranoid. If other requests on the page to the same endpoint happen to trigger at the same time, you may see a false positive.

    I’d focus more on the user experience than on network activity. There’s probably a better way to verify this given more context. For example, to verify the app isn’t frozen after the button click, try clicking something else and see if it takes effect.

    That said, I suppose if you have an isolated case, you could use this once-off to help avoid regressions.

    Note that since you haven’t provided a reproducible case of this "button firing request multiple times" bug, I’ve made a best guess, and you’ll likely need to adapt my code to match your actual application.

    Login or Signup to reply.
  2. My feeling is the the logic of the test is to capture the failure when waiting on the 2nd fetch.

    To this end expect(promise).rejects.toThrow() looks like it takes the right direction, but I’m working to improve the timing.

    Here’s the test

    test("Check for multiple fetch on click", async ({page}) => {
      await page.goto('http://127.0.0.1:5500/html/one-fetch.html')
    
      const responsePromise1 = page.waitForResponse(resp => 
        resp.url().includes('jsonplaceholder') && resp.status() === 200
      )
      
      await page.locator("button").click()
    
      // consume the expected and valid fetch
      await responsePromise1;
    
      // look for a second fetch and expect it's promise to crash
      const responsePromise2 = page.waitForResponse(resp => 
        resp.url().includes('jsonplaceholder') && resp.status() === 200
      )
      expect(responsePromise2).rejects.toThrow()
    
    })
    

    enter image description here

    My sample web page is

    <!DOCTYPE html>
    <html>
      <body>
        <button>click</button>
        <script>
          document.querySelector('button').addEventListener('click', () => {
            console.log('click')
            fetch('https://jsonplaceholder.typicode.com/users')
    
            setTimeout(() => {
              fetch('https://jsonplaceholder.typicode.com/users')
            }, 500)
          })
        </script>
      </body>
    </html>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search