skip to Main Content

It is easy to wait for a request if they have unique endpoints.
But how do we wait for a particular request if all the endpoints are same, but the request payload varies, eg GraqhQL middle layer.
In this case all the backend calls are done using https://myapplication.com/graphql-v2, but the request payload differs.
I want to intercept this request where payload contains the string getUserDetails

something on similar lines, but better :

const response = await page.waitForResponse(
      (request) => request.url().includes('/graphql-v2') && request.body().includes('getUserDetails')
)

enter image description here

2

Answers


  1. Your post mentions "request", but you’re using waitForResponse. If you want to wait for a request, you can use something like this example:

    const playwright = require("playwright"); // ^1.41.2
    
    const html = `<!DOCTYPE html><html><body><script>
    fetch("https://www.example.com/graphql-v2", {
      method: "POST",
      body: JSON.stringify({query: "getUserDetails"})
    })
      .then(res => {
        if (!res.ok) throw Error(res.statusText);
        return res.json();
      })
      .then(data => console.log(data))
      .catch(err => console.error(err));
    </script></body></html>`;
    
    let browser;
    (async () => {
      browser = await playwright.firefox.launch();
      const page = await browser.newPage();
      const requestFound = page.waitForRequest(req =>
        req.url().includes("/graphql-v2") &&
        /bgetUserDetailsb/.test(JSON.parse(req.postData()).query)
      );
      await page.setContent(html);
      const request = await requestFound;
      console.log(JSON.parse(await request.postData()));
        // => { query: 'getUserDetails' }
    })()
      .catch(err => console.error(err))
      .finally(() => browser?.close());
    

    To intercept a response based on its body, those involve a promise to parse the body. See the bottom of this answer for an explanation. In short, you can return a promise instead of a boolean from the callback to waitForResponse, which Puppeteer will await and use its resolved value as a boolean.

    Here’s a minimal, complete example:

    const playwright = require("playwright");
    
    const html = `<!DOCTYPE html><html><body><script>
    fetch("https://httpbin.org/post", {
      method: "POST",
      body: JSON.stringify({query: "getUserDetails"})
    })
      .then(res => {
        if (!res.ok) throw Error(res.statusText);
        return res.json();
      })
      .then(data => console.log(data))
      .catch(err => console.error(err));
    </script></body></html>`;
    
    let browser;
    (async () => {
      browser = await playwright.firefox.launch();
      const page = await browser.newPage();
      const responseFound = page.waitForResponse(
        res =>
          res.status() === 200 &&
          res.url().includes("httpbin.org/post") &&
          res
            .json()
            .then(data => /bgetUserDetailsb/.test(data.json.query))
      );
      await page.setContent(html);
      const response = await responseFound;
      console.log((await response.json()).json);
        // => { query: 'getUserDetails' }
    })()
      .catch(err => console.error(err))
      .finally(() => browser?.close());
    

    Finally, if you want to intercept a response based on the request body, which I think is your actual use case, you could do something like:

    const response = await page.waitForResponse(
      res =>
        res.status() === 200 &&
        res.url().includes("/graphql-v2") &&
        /bgetUserDetailsb/.test(
          JSON.parse(res.request().postData()).query
        )
    );
    

    The difference from your attempt is that I’ve renamed the parameter res (response) to clarify what the object really is, then used .request().postData() to pull the request payload. The regex is added for a bit of precision, but you can use includes if you want.

    If this doesn’t work, a reproducible example of the site (or a mock site like I’ve shown) would be useful so I can provide a precise answer.

    Login or Signup to reply.
  2. another way to wait for request payload which I am using it in my test cases as following

    
        test('Test case to wait for request payload', async ({ page }) => {
    
            let payloadData = '';
            const keysAndValuesToCheck = {
                properties: {
                    'Value 1': 'true',
                    'Value 2': 'false',
                    'Value 3': 'username',
                    'Value 4': 'email'
                }
            };
    
            // Act
            // Perform all the actions untill the point where you expect the request 
            //Specify the request to intercept
            const requestPromise = page.waitForRequest("https://yourreqesthere.com");
    
            const request = await requestPromise;
    
            // Once the request is received, capture its payload data
            if (request) {
                try {
                    payloadData = await request.postData();
                } catch (error) {
                    console.error('Error reading request payload:', error);
                }
            } else {
                console.error('Request not received within the timeout period');
            }
            //You might get encoded payload data which you would have to decode and if you want,
            // you can convert it to json
            if (typeof payloadData === 'string') {
                // Once you have the payloadData, decode and parse it
                const encodedJsonString = payloadData.split('=')[1];
                const decodedJsonString = decodeURIComponent(encodedJsonString);
                const jsonData = JSON.parse(decodedJsonString);
                console.log("Payload after jsonoiding : ", jsonData);
            }
    
        }); 
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search