uki00a / fresh-testing-library

Utilities for testing fresh apps
https://deno.land/x/fresh_testing_library
MIT License
22 stars 1 forks source link

Question: how to test island component that fetches from the backend #61

Closed sigmaSd closed 11 months ago

sigmaSd commented 11 months ago

example:

describe("islands/Counter.tsx", () => { beforeAll(setup); afterEach(cleanup);

it("should work", async () => { const count = signal(9); const user = userEvent.setup(); const screen = render(); const plusOne = screen.getByRole("button", { name: "+1" }); const minusOne = screen.getByRole("button", { name: "-1" }); expect(screen.getByText("9")).toBeInTheDocument();

await user.click(plusOne);
expect(screen.queryByText("9")).not.toBeInTheDocument();
expect(screen.getByText("10")).toBeInTheDocument();

await user.click(minusOne);
expect(screen.getByText("9")).toBeInTheDocument();
expect(screen.queryByText("10")).not.toBeInTheDocument();

}); });



The test fails with `error: (in promise) TypeError: Invalid URL: '/api/joke'`
uki00a commented 11 months ago

Hi @sigmaSd, thanks for opening the issue! In that case, it seems necessary to stub the fetch API. fetch-mock npm package seems to work with Deno:

import fetchMock from "https://esm.sh/fetch-mock@9.11.0/esm/client.js?pin=v133";

fetchMock.get("/api/joke", "Hello World!");
const res = await fetch("/api/joke");
console.info(await res.text()); // => "Hello World!"
fetchMock.reset();

I also tried MSW (msw/node), but at this point it didn't seem to work with Deno yet.

sigmaSd commented 11 months ago

Hello, thanks that works indeed

But what if I want to use the actual responses ?, can I spawn the server somehow so the requests get handled ? or can I import the hanlder from the api and use it directly ?

sigmaSd commented 11 months ago

for now I have this workaround

import { handler } from "@/routes/api/db/bycountry/[country].ts";

fetchMock.get(
      "/api/db/bycountry/Tunis", // need to read the docs to figure out how to handle any country
      handler.GET!(
        new Request("https://fixme_figure_out_what_to_put_here.com"),
        { params: { country: "Tunis" } } as unknown as HandlerContext,
      ),
    )
uki00a commented 11 months ago

But what if I want to use the actual responses ?, can I spawn the server somehow so the requests get handled ? or can I import the hanlder from the api and use it directly ?

In that case, it would be better to use createHandler(). The following code is an example of using createHandler():

import fetchMock from "https://esm.sh/fetch-mock@9.11.0/esm/client.js?pin=v133";
import { createHandler } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";
import config from "./fresh.config.ts";

const handler = createHandler(manifest, config);
fetchMock.get("/api/joke", (url, opts) => handler(new Request(url, opts)));

One of the other solutions would be to write e2e tests using deno-puppeteer or similar.

sigmaSd commented 11 months ago

You can also use deno std for stubbing

import { stub } from "$std/testing/mock.ts";
   const _fetchStub = stub(
      globalThis,
      "fetch",
      //@ts-ignore  FIXME, seems the type *is* string at runtime
      (input: string) => {
        if (input.startsWith("/api/db/bycountry/")) {
          return Promise.resolve(handler.GET!(
            new Request(`http://localhost/${input}`),
            { params: { country: "Tunis" } } as unknown as HandlerContext,
          ));
        }
        throw new Error(`unknown url: ${input}`);
      },
    );
sigmaSd commented 11 months ago

with createHandler

const handler = await createHandler(manifest, config);

const _fetchStub = stub(
      globalThis,
      "fetch",
      //@ts-ignore  FIXME, seems the type *is* string at runtime
      (input: string) => {
        if (input.startsWith("/api/db/bycountry/")) {
          return handler(new Request("http://localhost" + input));
        }
        throw new Error(`unknown url: ${input}`);
      },
    );
Thanks again, I think my question is answered