playwright-msw icon indicating copy to clipboard operation
playwright-msw copied to clipboard

Playwright-MSW Fails when a request that has been cloned is passed to fetch

Open jcannon98188 opened this issue 1 year ago • 4 comments

Ran into this issue where playwright-msw will fail when a Request object that has been cloned is passed to fetch.

I ran into this issue when using playwright-msw alongside the rtk-query library, which creates a Request object, then clones it, and then passes the original request object to fetch. Example of that happening in their source code here

Both in my actual application, as well as the minimum reproduction that I have linked below, these requests are handled just fine by msw when ran on its own. The issue only appears when using playwright-msw to run the msw.

From my investigation into this issue, I suspect the issue is occuring as part of the handler.ts file in playwright-msw source link. When I was investigating this as part of my actual application I am working on, I found that request.postData was returning null.

Minimum reproduction of this issue can be found at a repo here

Relevant files are:

src/App.tsx src/mocks/handlers.ts tests/example.spec.ts

jcannon98188 avatar Mar 29 '23 19:03 jcannon98188

Not directly related to this issue, but in case anyone comes across this issue before it is fixed, and uses RTK-Query here is the workaround that allowed me to get the libraries to play nice with eachother

const playwrightMSWCompatibleFetch = async (input: RequestInfo, _init: RequestInit) => {
    if (typeof input === 'string') return await fetch(input);
    const request = input as Request;
    let body: string;
    if (request.body !== null) {
        body = await request.text();
    }
    let requestOptions: RequestInit = {
        method: request.method,
        headers: request.headers,
        credentials: request.credentials,
    };
    if (body !== undefined) {
        requestOptions.body = body;
    }
    return await fetch(request.url, requestOptions);
};

export const emptySplitApi = createApi({
    baseQuery: fetchBaseQuery({
            baseUrl: '/',
            fetchFn: playwrightMSWCompatibleFetch,
        }),
    endpoints: () => ({}),
});

jcannon98188 avatar Mar 29 '23 19:03 jcannon98188

I am experiencing this issue as well. playwright-msw is not intercepting requests if they have already been handled by the base handlers. I am trying to overwrite mocked requests in specific tests to throw errors, and those requests are not being handled by playwright-msw.

samanthaandrews avatar Jun 20 '23 18:06 samanthaandrews

I'm not sure if that is the same issue @samanthaandrews however I have experienced similar things as well. Usually it's caused by your playwright test being finished before the network call actually finishing. If you post your test that's failing up I can see if I can figure it out for you, but no guarantees.

jcannon98188 avatar Jun 20 '23 20:06 jcannon98188

Hi @jcannon98188 , @samanthaandrews and I are both working on the same bug, I can help with providing the failed test that showcases the problem.

This is the test that's failing.

  test.only("Returning book fails", async ({ page, worker }) => {
    await worker.use(
      rest.get(ALMOST_MIDNIGHT_REVOKE_URL, async (_req, res, _ctx) => {
        return res(
          problemDocument(500, "Something went wrong", "Error message")
        );
      })
    );

    await page.goto(ALMOST_MIDNIGHT_DETTAIL_PAGE_URL);

    const downloadButton = await page.getByRole("button", { name: "Download" });
    await expect(downloadButton).toBeVisible();

    await downloadButton.click();

    // shows the progress bar
    await expect(page.getByText("Downloading")).toBeVisible();

    const returnButton = await page.getByRole("button", { name: "Return" });
    const readButton = await page.getByRole("button", {
      name: "Read",
      exact: true
    });

    await expect(returnButton).toBeVisible();
    await expect(readButton).toBeVisible();

    await returnButton.click();

    const confirmBtn = await page.getByRole("button", { name: "Return" });
    await expect(confirmBtn).toBeVisible();
// After this action, the request should fail, but looking at the console,
// it's returning the good response set in the base handler file
    await confirmBtn.click();

    const alertBox = await page.locator(".alert-wrapper");
    await expect(alertBox).toHaveCount(1);
  });

This is the base handler.

  rest.get(ALMOST_MIDNIGHT_REVOKE_URL, (_req, res, ctx) => {
    return res(ctx.xml(bookAlmostMidnightInvoked));
  }),

await confirmBtn.click(); should execute the request and fail, but it returned a good response from the base.

Please let me know if you need more information. Thank you!

Toxiapo avatar Jun 20 '23 21:06 Toxiapo