Chrome 使用Puppeteer捕获HTTP响应的正确方法是什么?

qvtsj1bj  于 2023-03-21  发布在  Go
关注(0)|答案(4)|浏览(361)

我正在尝试从用户注册中捕获http响应状态。
我的代码如下所示:

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)
  })

这些文档给予了一个拦截请求并对其执行操作的示例:

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

我已经尝试了一个类似的模式,但没有效果,沿着许多其他模式。我所做的一切都返回page,一个巨大的对象,上面没有我可以看到的状态。任何帮助都非常感谢。

成功案例:

感谢@tomahaug指引我正确的方向。我的第一个问题是放置,侦听器需要在请求发出之前设置好,我在请求之后就有了它。有道理。我最大的问题是将侦听器分配给一个变量,这样我就可以调用expect作为我的最后一行。将它赋给一个变量会导致返回page。我需要做的只是在侦听器中运行测试。虽然使用done()抛出和错误为我关闭了我的测试如下,我的代码的工作版本:

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()
  })

希望这对其他人有帮助!

1tuwyuhd

1tuwyuhd1#

我相信你应该沿着这些思路做一些事情。注意回调函数done
这段代码所做的是附加一个侦听器来接收响应,然后单击提交按钮,当收到响应时,它检查状态代码,Assert它,并通过调用done来终止测试。
您可能需要一个if-语句来检查回调中检查的是来自表单的实际响应,因为响应处理程序可能会为其他并发请求发出事件。

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})
})

我还没有测试代码,但它应该给予你正确的想法。
编辑:调整以反映最终的结果。

bfrts1fy

bfrts1fy2#

如果需要操作请求/响应,请使用page.setRequestInterception(true)page.on/page.once(如文档所述)。
但是,如果您所需要的只是Assert关于响应的某些内容,那么最简单、最惯用的方法是使用page.waitForResponse

const updateDashboardResponse = await page.waitForResponse(response =>
  response.url().includes('updateDashboard')
);
expect(updateDashboardResponse.status()).toBe(200);

这允许测试流保持线性,并避免在page.on处理程序接收到response事件之前关闭测试的模糊性。

xzlaal3s

xzlaal3s3#

接受的答案(也被编辑到问题中)是不正确的。它引入了争用条件,因为单击调用会增加1秒的延迟。最好的情况下,这会不必要地降低测试套件的速度,最坏的情况下,如果请求需要超过1秒才能解决,则会生成假失败(如果请求被嘲笑,则不太可能,但这不会改变代码不安全的事实)。
每当Jest测试用例中有回调时,确保它已被执行并且所有依赖于它的Assert已被触发而不添加人工延迟的正确方法是从回调中调用done()。如果回调中有一个抛出使done不可达,则在错误处理程序中调用done(error)以向Jest报告测试用例失败。
为此,您需要将done作为参数添加到传递给ittestonly函数的回调函数中,以便它在块中可用。这允许Jest的测试运行器将测试视为异步的,并且在调用done之前不解析它。测试套件忽略回调的Assert。async/await没有帮助,因为它是一个与回调不同的异步链。
您只需指定done作为参数或返回一个promise(async隐式返回一个承诺),但决不能两者都有。您可能仍然希望使用await而不是then来调用Puppeteer库。您可以使用async IIFE,当所有Assert都被触发时,它最终触发done()调用,以获得两个方面的最佳效果。
例如,

it.only('returns a 400 response if email is taken', done => {
  (async () => {
    page.on('response', response => {
      if (response.request().method === 'POST' && 
          response.url === `${process.env.USERS_API_DOMAIN}/sessions`) {
        try { /* try-catch pattern shown for illustration */
          expect(response.status).toEqual(400);
          done();
        } 
        catch (err) {
          done(err);
        }
      }
    });    
    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"]');
  })();
});

考虑到这一点,this answer展示了一个可能更好的方法,使用waitForResponse,它允许您完全跳过回调和done。对waitForResponse的回调是一个字符串URL或函数 predicate ,它应该为正在等待的目标响应返回true:

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"]');
  const response = await page.waitForResponse(response =>
    response.request().method === 'POST' && 
    response.url === `${process.env.USERS_API_DOMAIN}/sessions`
  );
  expect(response.status).toEqual(400);
});

我还应该提到,在上面的代码片段中,waitFor被弃用,waitForSelector.url是函数。它的存在是为了与原始帖子相关联并显示高级模式。

最小示例

一米二十四分一秒

这是我们正在测试的网页。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <button>Post</button>
    <script>
      document
        .querySelector("button")
        .addEventListener("click", e =>
          fetch("https://jsonplaceholder.typicode.com/posts", {
              method: "POST",
              body: JSON.stringify({
                title: "foo",
                body: "bar",
                userId: 1,
              }),
              headers: {
                "Content-type": "application/json; charset=UTF-8",
              },
            })
            .then(response => response.json())
            .then(json => console.log(json))
        )
      ;
    </script>
  </body>
</html>

index.test.jsasync/await版本):

describe("index page", () => {
  it("should respond to POST", async () => {
    const url = "https://jsonplaceholder.typicode.com/posts";
    await page.goto("http://localhost:1234", {waitUntil: "load"});
    await page.click("button");
    const response = await page.waitForResponse(response =>
      response.request().method() === "POST" &&
      response.url() === url
    );
    const expectedBody = {
      body: "bar",
      id: 101,
      title: "foo",
      userId: 1,
    };
    expect(await response.json()).toEqual(expectedBody);
  });
});

index.test.jsthen版本):

describe("index page", () => {
  it("should respond to POST", () => {
    const url = "https://jsonplaceholder.typicode.com/posts";
    const expectedBody = {
      body: "bar",
      id: 101,
      title: "foo",
      userId: 1,
    };
    return page.goto("http://localhost:1234", {
        waitUntil: "load"
      })
      .then(() => page.click("button"))
      .then(() => page.waitForResponse(response =>
        response.request().method() === "POST" &&
        response.url() === url
      ))
      .then(response => response.json())
      .then(body => expect(body).toEqual(expectedBody))
    ;
  });
});

index.test.jsdone版本):

describe("index page", () => {
  it("should respond to POST", done => {
    (async () => {
      const url = "https://jsonplaceholder.typicode.com/posts";
      const expectedBody = {
        body: "bar",
        id: 101,
        title: "foo",
        userId: 1,
      };
      await page.setRequestInterception(true);
      page.on("response", async response => {
        if (response.request().method() === "POST" &&
            response.url() === url) {
          try {
            const body = await response.json();
            expect(body).toEqual(expectedBody);
            done();
          }
          catch (err) {
            done(err);
          }
        }
      });
      await page.goto("http://localhost:1234", {
        waitUntil: "load"
      });
      page.click("button");
    })();
  });
});
vaj7vani

vaj7vani4#

response.url是一个函数,你必须调用它:

response.url()

对于response.request().method也是如此:

response.request().method()

相关问题