MSW handler correctly intercepting request, but still resulting Axios network error

587 views Asked by At

I'm kinda stumped - I've read through the many postings on here about Axios + MSW [1] [2] [3] and I can't quite figure out where i'm going wrong.

What I'm doing - I'm trying to use MSW to intercept network requests that are made by Axios to inject responses for testing.

Code:

Test file (this will fail, I'm just trying to get it to work properly)-

/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { jobsDashboardResponse } from "../../../tests/mocks/msw/handlers"
import { server as mswServer } from "../../../tests/mocks/msw/server"
import { bacalhauAPI } from "../bacalhau"

// Enable request interception.
beforeAll(() => mswServer.listen())

// Reset handlers so that each test could alter them
// without affecting other, unrelated tests.
afterEach(() => mswServer.resetHandlers())

// Don't forget to clean up afterwards.
afterAll(() => mswServer.close())

describe("Basic fetch of mocked API", () => {
    it("should GET /orchestrator/jobs", async () => {
        const jobsResponse = { a: 2 }

        mswServer.use(jobsDashboardResponse)
        mswServer.listHandlers()

        const jobs = await bacalhauAPI.listJobs()

        expect(jobs).toEqual(jobsResponse)
    })
})

BacalhauAPI server ts:

import axios, { AxiosInstance } from "axios"
import {
  JobListRequest,
} from "../helpers/jobInterfaces"

class BacalhauAPI {
  apiClient: AxiosInstance

  constructor(baseURL: string) {
    this.apiClient = axios.create({
      baseURL,
      headers: {
        "Content-Type": "application/json",
      },
    })
  }

  async listJobs(labels?: string[], nextToken?: string): Promise<JobsResponse> {
    try {
      const params: JobListRequest = {
        order_by: "created_at",
        reverse: true,
        limit: 10,
        labels: labels ? `env in (${labels.join(",")})` : undefined,
        next_token: nextToken,
      }
      const response = await this.apiClient.get("/orchestrator/jobs", {
        params,
      })
      return response.data as JobsResponse
    } catch (error) {
      console.error("An error occurred while listing jobs:", error)
      throw error
    }
  }

}

function getAPIConfig(
  property: "host" | "port" | "base",
  defaultValue: string
): string {
  const declared = document
    .querySelector(`link[rel=api-${property}]`)
    ?.getAttribute("href")
  const useDefault =
    declared === undefined ||
    declared?.match(/\{{2}/) ||
    declared === "" ||
    declared === null
  return useDefault ? defaultValue : declared || ""
}

const host = getAPIConfig("host", document.location.hostname)
const port = getAPIConfig("port", "1234")
const base = getAPIConfig("base", "api/v1")
export const bacalhauAPI = new BacalhauAPI(
  `${document.location.protocol}//${host}:${port}/${base}`
)

msw handlers:

import { http, HttpResponse, RequestHandler, RequestHandlerOptions } from "msw";
import { TestData } from "../../basic/msw.tests";

export const jobsDashboardResponse = http.get('http://localhost:1234/api/v1/orchestrator/jobs', ({ cookies }) => {
  // Placeholders for messing around with cookies
  const { v } = cookies

  return HttpResponse.json(v === 'a' ? { foo: 'a' } : { bar: 'b' })
})

export const handlers: RequestHandler<any, any, any, RequestHandlerOptions>[] = [jobsDashboardResponse]

msw server:

import { setupServer } from "msw/node"
import { handlers } from "./handlers"

export const server = setupServer(...handlers)

The error:

  console.error
    An error occurred while listing jobs: AxiosError {
      message: 'Network Error',
      name: 'AxiosError',
      code: 'ERR_NETWORK',
      config: {
        transitional: {
          silentJSONParsing: true,
          forcedJSONParsing: true,
          clarifyTimeoutError: false
        },
        adapter: [ 'xhr', 'http' ],
        transformRequest: [ [Function: transformRequest] ],
        transformResponse: [ [Function: transformResponse] ],
        timeout: 0,
        xsrfCookieName: 'XSRF-TOKEN',
        xsrfHeaderName: 'X-XSRF-TOKEN',
        maxContentLength: -1,
        maxBodyLength: -1,
        env: { FormData: [Function], Blob: [class Blob] },
        validateStatus: [Function: validateStatus],
        headers: Object [AxiosHeaders] {
          Accept: 'application/json, text/plain, */*',
          'Content-Type': null
        },
        baseURL: 'http://localhost:1234/api/v1',
        params: {
          order_by: 'created_at',
          reverse: true,
          limit: 10,
          labels: undefined,
          next_token: undefined
        },
        method: 'get',
        url: '/orchestrator/jobs',
        data: undefined
      },
      request: XMLHttpRequest {
        open: [Function: open],
        setRequestHeader: [Function: setRequestHeader],
        send: [Function: send],
        abort: [Function: abort],
        getResponseHeader: [Function: getResponseHeader],
        getAllResponseHeaders: [Function: getAllResponseHeaders],
        overrideMimeType: [Function: overrideMimeType],
        onreadystatechange: [Getter/Setter],
        readyState: 4,
        timeout: [Getter/Setter],
        withCredentials: [Getter/Setter],
        upload: [Getter],
        responseURL: [Getter],
        status: [Getter],
        statusText: [Getter],
        responseType: [Getter/Setter],
        response: [Getter],
        responseText: [Getter],
        responseXML: [Getter],
        UNSENT: 0,
        OPENED: 1,
        HEADERS_RECEIVED: 2,
        LOADING: 3,
        DONE: 4
      }
    }

When I console.log or debug, it's clearly getting grabbed by the MSW handler. So what's up?

[1] Reactjs: MSW mock handlers failing with Error "Network request failed"

[2] FETCH_ERROR "TypeError [ERR_INVALID_URL]: Invalid URL" for requests made in tests

[3] Jest returns "Network Error" when doing a mock request with axios and msw

1

There are 1 answers

1
aronchick On

ARG - just found it. Turns out that it was an issue with undici listed here:

https://github.com/mswjs/msw/discussions/1915

tldr downgrade undici to usev5.26.2.