reactjs - FETCH_ERROR "TypeError [ERR_INVALID_URL]: Ugyldig URL" for forespørsler gjort i tester


Dette er første gang jeg setter opp testmiljø med RTK Query og mock-server (msw). Vennligst fortell meg hva jeg gjør feil.

Miljø

NavnVersjon
msw1.2.1
reagere18
redux verktøykasse1.9.3
vitest0.29.3
cross-fetch3.1.5
typescript5
NodeJS18 (prøvde også 19,3)
OSWindows

Mock-server og forespørselshåndterere

import { rest } from 'msw';
import { setupServer } from 'msw/node';

export const handlers = [
  rest.get('http://localhost:5173/api/search/photos', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(resList));
  }),
  rest.get('http://localhost:5173/api/photos/ZJIwrGrQb2Y', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(resPhoto));
  }),
];

export const server = setupServer(...handlers);

Deretter starter denne mock-serveren i setupTests.ts-filen før vi kjører tester

import { beforeAll, afterEach, afterAll } from 'vitest';
import { setupStore } from '../redux/store';
import { apiSlice } from '../redux/api';

const store = setupStore({});

beforeAll(() =>
  server.listen({
    onUnhandledRequest: 'error',
  })
);

afterEach(() => {
  server.resetHandlers();
  store.dispatch(apiSlice.util.resetApiState());
});

afterAll(() => server.close());

RTK Query-forespørsler

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({
    baseUrl: BASE_URL,
    prepareHeaders(headers) {
      headers.set('Accept', 'application/json');
      headers.set('Accept-Version', 'v1');
      headers.set('Authorization', `Client-ID ${API_ACCESS_TOKEN}`);
      return headers;
    },
  }),
  endpoints: (builder) => {
    return {
      getPhotoList: builder.query<TransformedApiResponse, RequestParams>({
        query: (params: RequestParams) => {
          const { query } = params;
          return `/search/photos?query=${query}`;
        },
        transformResponse: (response: ApiResponse): TransformedApiResponse => ({
          total: response.total,
          totalPages: response.total_pages,
          cards: response.results.map((item: PhotoDTO): CardData => {
            return {
              id: item.id,
              imgSrc: item.urls.small,
              alt: item.alt_description,
              likes: item.likes,
            };
          }),
        }),
      }),
      getOnePhoto: builder.query<FullPhotoDTO, string>({
        query: (id: string) => {
          return `/photos/${id}`;
        },
      }),
    };
  },
});

export const { useGetPhotoListQuery, useGetOnePhotoQuery } = apiSlice;

Vite-konfigurasjon

/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import eslint from 'vite-plugin-eslint';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), eslint()],
  test: {
    globals: true,
    environment: 'happy-dom',
    coverage: {
      provider: 'istanbul',
      all: true,
    },
    setupFiles: ['./src/test/setupTests.ts'],
  },
  server: {
    proxy: {
      '/api': {
        target: 'https://api.unsplash.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
});

Testkode

describe('App test', () => {
  test('Should render home page', async () => {
    renderWithProviders(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>
    );

    expect(screen.getByText(/Home Page/i)).toBeDefined();

    expect(screen.getByRole('loader')).toBeDefined();

    await waitForElementToBeRemoved(() => screen.getByRole('loader'));

    expect(screen.getByText('1113')).toBeDefined();
  });

Nåværende atferd

Den ovennevnte testen mislykkes:

Jeg viser nettverksfeil på siden, slik at jeg kan se dette i HTML:

Forespørsler som gjøres i hovedapplikasjonen fungerer fint (testet på localhost). Så jeg er sikker på at RTK query-koden er bra. Forespørsler som gjøres fra tester feiler alle med FETCH_ERROR "TypeError [ERR_INVALID_URL]: Invalid URL". Før jeg migrerte til redux og RTK query, brukte jeg en enkel fetch og jeg husker også at det var en veldig lignende feil (Invalid URL null eller noe lignende). Den gangen løste jeg det ved å importere fetch fra cross-fetch. Denne gangen har jeg prøvd å gjøre det samme, men feilen vedvarer.

Hva prøvde jeg?

  1. importer fetch fra cross-fetch (som anbefalt på GitHub og StackOverflow)

    import { fetch, Headers, Request, Response } from 'cross-fetch'
    
    global.fetch = fetch
    global.Headers = Headers
    global.Request = Request
    global.Response = Response
    
  2. Prøvde alternative biblioteker: whatwg-fetch, isomorphic-fetch.

  3. Jeg prøvde også å fjerne vite-proxyserveren, bare for å forenkle ting og lokalisere problemet, men det hjalp uansett ikke.

  4. Jeg prøvde å flytte server.listen()-kallet fra setupTests.ts til hver enkelt testfil.

  5. Jeg la til logging i Home-komponenten min. Slik fikk jeg i det minste en mer detaljert feilmelding.

    const { query } = useAppSelector((state) => state.home);
    
    const { data, isFetching, error } = useGetPhotoListQuery({ query: query });
    console.table({ data, isFetching, error });
    

Jeg vet ikke hvordan jeg skal sjekke at min mock-server (msw) er oppe og klar til å motta forespørsler.

Svar #1

Forklaring

Jeg hadde det samme problemet i dag og kunne løse det ved bare å erstatte den globale location-egenskapen. MSW konstruerer internt et URL-objekt med URL-en du ba om, og som andre parameter den nåværende opprinnelsen. happy-dom etterligner location-API-et, men setter origin-egenskapen til null. Derfor gir følgende kall Invalid URL-feilen (laget av MSW internt):

const requestUrl = new URL(
       url,
       typeof location !== 'undefined' ? location.origin : undefined
     )

Dette er også grunnen til at du fikk feilen som nevnte en eller annen null-verdi da du brukte fetch. For eksempel bruker jeg bare fetch API for forespørsler, og derfor får jeg den enkle feilen produsert av node. Din feil er antagelig utvidet på grunn av rammeverket du bruker for forespørslene.

Løsning

Bare bruk lokasjons-API-metodene som location.replace(url) eller location.assign(url) for å få origin satt korrekt:

beforeEach(() => {
    location.replace(`http://localhost`);    
})

Merk at jeg bruker vitests beforeEach hook som ikke ville vært nødvendig.

Svar #2

Denne feilen er blitt fikset i MSW v2.1.5. Sørg for å alltid oppdatere til den nyeste versjonen (npm i msw@latest).

kilde: utgivelsesnotater.

Relaterte artikler