DEV Community

marinsky roma
marinsky roma

Posted on

Playwright Quirks — waitForResponse

Playwright has a convenient feature for waiting on responses from requests - waitForResponse.

waitForResponse — playwright.dev

This is helpful when there are no visual changes on the web UI, but you need to verify that a request was actually sent and an entity was successfully created. Instead of:

  • Opening the page with that entity and writing checks to verify all data is correct
  • Or directly "poking" a specific endpoint to check its fields

You can implement response waiting. Here's an example from the documentation:

Option 1: Declare the expectation first

Start waiting, perform the action, then await the response:

const responsePromise = page.waitForResponse('https://example.com/resource');
await page.getByText('trigger response').click();
const response = await responsePromise;
Enter fullscreen mode Exit fullscreen mode

Option 2: Use a predicate

Declare the predicate with expectations, perform the action, and get the result:

const responsePromise = page.waitForResponse(response =>
    response.url() === 'https://example.com' && response.status() === 200
    && response.request().method() === 'GET'
);
await page.getByText('trigger response').click();
const response = await responsePromise;
Enter fullscreen mode Exit fullscreen mode

The Promise Advantage

Promises supposedly give us an advantage over other non-async languages.
Legally, the logic described is correct and logically sound:

  1. First, we declare that we need to wait for a response from the backend, but we don't block the execution of subsequent actions
  2. Then we perform the action itself, and wait for it to complete - the browser directly tells Playwright that the action is done, the event is complete, roughly speaking
  3. Only then do we "receive" the result of the async wait that we declared to the browser

But Here's the Thing...

Your backend doesn't respond to the frontend in 1 millisecond. At best, it'll respond in 100 milliseconds (unless we're talking about gRPC or WebSocket).
The question arises: How much time passes between one promise completing and the next promise starting?
Answer: Microseconds - it's just the time for the JS event loop to process the microtask queue.

So You Can Actually Write It Like This

await page.getByTestId('submit-button').click(); 
await page.waitForResponse('**/api/users');
Enter fullscreen mode Exit fullscreen mode

It's more compact and simpler!
You're very unlikely to encounter a race condition where a click or fill takes so long that waiting for the response becomes pointless.
So feel free to try writing "synchronous" code like this in your project - it works for me, and it'll work for you.

Proof of Concept

For skeptics, I've prepared an example repository that emulates this behavior on localhost, where everything runs locally and passes successfully:

https://github.com/rmarinsky/trivial_service_for_experiments

Of course, edge cases can occur depending on your frontend implementation.

Top comments (0)