The main purpose of the waitForResponse
callback is to validate if a specific response matches what you are expecting. This can be achieved by returning either a boolean value or a promise that resolves to a truthy value. While typically checking the URL, method, and status suffices, you also have the option to use the response body as a predicate (refer to the end of this post for details). It's worth noting that while res.body().then(b => {
technically resolves to true, it doesn't help in extracting the desired property from the response when handling it in your code flow post-response. Consider utilizing:
const [response] = await Promise.all([
page.waitForResponse(res =>
res.status() === 200 &&
res.url() === 'https://apples.com/members/msg/get_inbox?filter=all&unread_only=0&last_msg_id=0'
),
page.getByRole('button', { name: 'Continue' }).click()
]);
const data = await response.json();
console.log(data.threads[0].my_id);
// alternatively, to print all my_ids:
for (const e of data.threads) {
console.log(e.my_id);
}
I've removed the await
before page.getByRole
. Typically, promises should be passed to Promise.all()
, not their resolved values. Using await
on the click operation means it will resolve before waitForResponse
registers, leading to the failure of capturing the response.
If you're unsure about locating the key within the JSON structure, you can use this tool (ensure closing square brackets & remove trailing commas for valid JSON).
Here's a working proof of concept:
const playwright = require("playwright"); // ^1.39.0
const sampleHTML = `<!DOCTYPE html><html><body>
<button>click me</button>
<script>
document.querySelector("button").addEventListener("click", e => {
fetch("https://jsonplaceholder.typicode.com/posts");
});
</script></body></html>`;
let browser;
(async () => {
browser = await playwright.firefox.launch();
const page = await browser.newPage();
await page.setContent(sampleHTML);
const [response] = await Promise.all([
page.waitForResponse(res =>
res.status() === 200 &&
res.url() === "https://jsonplaceholder.typicode.com/posts"
),
page.click("button"),
]);
const data = await response.json();
console.log(data[0].title);
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
Furthermore, if awaiting a response with specific body data, waitForResponse
can handle any promises returned. For instance, you could do something like:
page.waitForResponse(res =>
// Return false if a synchronous condition fails
res.status() === 200 &&
res.url() === "https://jsonplaceholder.typicode.com/posts" &&
// Otherwise, return a promise resolving to a boolean after sync checks pass
// Note that only one promise can be returned using '&&' and it must be the last operand.
res.json().then(data => data[0].userId === 1 && data.length === 100)
)
This approach offers more precision compared to using substring matching in the body content. Remember, ensure that the promise is the final part of the boolean condition chain for implicit return and Playwright awaits it.