Saltar al contenido

¿Cuál es la forma correcta de capturar una respuesta HTTP con Puppeteer?

Te sugerimos que pruebes esta resolución en un ambiente controlado antes de pasarlo a producción, un saludo.

Solución:

Creo que deberías hacer algo en ese sentido. Tenga en cuenta la función de devolución de llamada done.

Lo que hace el código es que adjunta un oyente para las respuestas y luego hace clic en el botón enviar. Cuando se recibe una respuesta, verifica el código de estado, lo afirma y finaliza la prueba llamando done.

Es posible que desee tener un if-declaración que verifica que es la respuesta real de su formulario lo que está verificando en la devolución de llamada, ya que el controlador de respuesta podría emitir eventos para otras solicitudes concurrentes.

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"]', '[email protected]')
  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)
)

No he probado el código, pero debería darte la idea correcta.

Editar: ajustado para reflejar lo que funcionó al final.

Si necesita manipular la solicitud / respuesta, use page.setRequestInterception(true) y page.on/page.once (como se documenta).

Sin embargo, si todo lo que necesita es afirmar algo sobre la respuesta, la forma más simple e idiomática de hacerlo es con page.waitForResponse:

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

Esto permite que el flujo de prueba permanezca lineal y evita la ambigüedad en torno al cierre de una prueba antes de una page.on el manejador recibe un response evento.

La respuesta aceptada (que también se editó en la pregunta) es incorrecta. Introduce una condición de carrera debido a un retraso de 1 segundo agregado a la llamada de clic. En el mejor de los casos, esto ralentiza innecesariamente la suite de pruebas y, en el peor, genera false fallas en caso de que la solicitud tarde más de un segundo en resolverse (poco probable si se burla, pero no cambia el hecho de que el código no es seguro).

Siempre que haya una devolución de llamada en un caso de prueba de Jest, la forma correcta de asegurarse de que se haya ejecutado y de que todas las afirmaciones que dependen de su activación se hayan realizado sin agregar demoras artificiales es llamar done() desde la devolución de llamada. Si hay un lanzamiento en la devolución de llamada que hace done inalcanzable, llamar done(error) en el controlador de errores para informar el fallo del caso de prueba a Jest.

Para hacer esto, necesitará agregar done como el parámetro de la devolución de llamada pasado al it, test o only función para que esté disponible en el bloque. Esto permite que el ejecutor de pruebas de Jest trate la prueba como asincrónica y no la resuelva hasta done se llama. Sin done, la suite de pruebas ignora las afirmaciones de la devolución de llamada. async/await no ayuda porque es una cadena asincrónica separada de la devolución de llamada.

Solo necesitas especificar done como parámetro o devolver una promesa (async devuelve implícitamente una promesa), nunca ambos. Sin embargo, es probable que aún desee utilizar await para llamadas a la biblioteca Titiritero en lugar de then. Puedes usar un async IIFE que eventualmente dispara el done() llame cuando todas las afirmaciones se hayan disparado para obtener lo mejor de ambos mundos.

Por ejemplo,

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"]', '[email protected]');
    await page.type('input[id="Password"]', 'LukeSkywalker123', delay: 100);
    await page.click('input[type="submit"]');
  )();
);

Teniendo esto en cuenta, esta respuesta muestra un enfoque probablemente mejor utilizando waitForResponse que le permite omitir la devolución de llamada y done enteramente. La devolución de llamada a waitForResponse es un string URL o predicado de función que debería devolver true para la respuesta de destino que se está esperando:

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"]', '[email protected]');
  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);
);

También debo mencionar waitFor está en desuso a favor de waitForSelector en los fragmentos anteriores y que .url y .method son funciones. No he verificado el código anterior; está ahí para relacionarse con la publicación original y mostrar los patrones de alto nivel.


Ejemplo mínimo

index.html

Esta es la página web que estamos probando.



  
    
  
  
    
    
  

index.test.js (async/await versión):

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.js (then versión):

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.js (done versión):

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");
    )();
  );
);

Recuerda que puedes comunicar este tutorial si te valió la pena.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *