javascriptgoogle-chromepuppeteerintegration-testing

What is the proper way to capture a HTTP response with Puppeteer?


I am trying to capture the http response status from a user sign-up.

My code looks like this:

  it.only('returns a 400 response if email is taken', async () => {
    await page.goto(`${process.env.DOMAIN}/sign-up`)
    await page.waitFor('input[id="Full Name"]')

    await page.type('input[id="Full Name"]', 'Luke Skywalker')
    await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com')
    await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})
    await page.click('input[type="submit"]', {delay: 1000})

    const response = await page.on('response', response => response)

    console.log('request status', response.status)
    // expect(response).toEqual(400)
  })

The docs give an example of intercepting the request and doing things with it:

await page.setRequestInterception(true);
page.on('request', request => {
  request.respond({
    status: 404,
    contentType: 'text/plain',
    body: 'Not Found!'
  });
});

And I have tried a similar pattern to no avail, along with many other patterns. Everything I do returns the page, a huge object with no status on it that I can see. Any help is much appreciated.

WHAT WORKED:

thank you to @tomahaug for steering me in the correct direction. My first problem was placement, the listener needs to go be set up before the request is made, I had it just after the request. Makes sense. My biggest issue was assigning the listener to a variable, so that I could call the expect as my last line. Assigning it to a variable caused the page to be returned. What I needed to do was just run the test inside the listener. While using done() throws and error for me I closed off my test as follows below, the working version of my code:

it.only('returns a 400 response if email is taken', async () => {
    await page.goto(`${process.env.DOMAIN}/sign-up`)
    await page.waitFor('input[id="Full Name"]')

    await page.type('input[id="Full Name"]', 'Luke Skywalker')
    await page.type('input[id="Email"]', 'LukeSkywalker@voyage1.com')
    await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})

    await page.on('response', response => {
      if (response.request().method === 'POST' && response.url === `${process.env.USERS_API_DOMAIN}/sessions`) {
        expect(response.status).toEqual(400)
      }
    })

    await page.click('input[type="submit"]', {delay: 1000})
  })

  after(async function () {
    await browser.close()
  })

Hope this helps someone else!


Solution

  • I believe you should do something along those lines. Note the callback function done.

    What the code does, is that it attaches a listener for responses, then clicks the submit button. When a response is received it checks the status code, asserts it, and terminates the test by calling done.

    You might want to have an if-statement that checks that it is the actual response from your form that you are checking in the callback, as the response handler might emit events for other concurrent requests.

    it.only('returns a 400 response if email is taken', () => {
      await page.goto(`${process.env.DOMAIN}/sign-up`)
      await page.waitFor('input[id="Full Name"]')
    
      await page.type('input[id="Full Name"]', 'Luke Skywalker')
      await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com')
      await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})
    
      page.on('response', (response) => {
        if (
          response.request().method === 'POST' && 
          response.url === `${process.env.USERS_API_DOMAIN}/sessions`) 
        {
          expect(response.status).toEqual(400)
        }
      })
    
      await page.click('input[type="submit"]', {delay: 1000})
    })
    

    I have not tested the code, but it should give you the right idea.

    Edit: Adjusted to reflect what worked out in the end.