import { initial, isInitialized, request, requestPagination } from "./request"; const fetchSuccess = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ data: "test", success: true }), headers: new Map(), }) ); // 原样返回 const fetchEcho = jest.fn((url: string, options: RequestInit) => { return Promise.resolve({ ok: true, json: () => Promise.resolve({ data: JSON.parse(options.body as string), success: true, }), headers: new Map(), }); }); afterEach(() => { fetchSuccess.mockClear(); fetchEcho.mockClear(); }); test("initial", async () => { expect(request("test")).rejects.toThrow("请先调用 initial 初始化"); initial({ baseURL: "https://example.com", fetch: (() => {}) as any, }); expect(isInitialized).toBeTruthy(); }); test("request", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, }); expect( await request( "/test", { foo: "bar" }, { method: "PUT", searchParams: { one: "two" }, headers: { "X-POWER-BY": "test", }, } ) ).toEqual("test"); expect(fetch).toBeCalledWith("https://example.com/test?one=two", { body: '{"foo":"bar"}', headers: { "content-type": "application/json;charset=UTF-8", "x-power-by": "test", }, method: "PUT", mode: "cors", }); }); test("name 参数变量替换", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, }); request("/test/{id}/{foo.bar}", { id: "123", foo: { bar: "baz" } }); expect(fetch).toBeCalledWith("https://example.com/test/123/baz", { body: '{"id":"123","foo":{"bar":"baz"}}', headers: { "content-type": "application/json;charset=UTF-8" }, method: "POST", mode: "cors", }); }); test("支持识别 application/x-www-form-urlencoded", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, }); expect( await request( "/test", { foo: "bar", baz: 1 }, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, } ) ).toEqual("test"); expect(fetch).toBeCalledWith("https://example.com/test", { body: "baz=1&foo=bar", headers: { "content-type": "application/x-www-form-urlencoded" }, method: "POST", mode: "cors", }); }); test("拦截器", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, interceptor: async (req, next) => { req.headers["X-POWER-BY"] = "test"; req.searchParams.one = "two"; req.method = "GET"; req.body.foo = "foo"; const response = await next(); response.data = "boom"; return response; }, }); expect(await request("/test", { bar: "bar" })).toEqual("boom"); expect(fetch).toBeCalledWith( "https://example.com/test?bar=bar&foo=foo&one=two", { body: undefined, headers: { "content-type": "application/json;charset=UTF-8", "x-power-by": "test", }, method: "GET", mode: "cors", } ); }); test("拦截器元数据", async () => { const fetch = fetchSuccess; const interceptor = jest.fn((req, next) => { return next(); }); initial({ baseURL: "https://example.com", fetch: fetch as any, interceptor, }); // @ts-expect-error request("/test", {}, { meta: { foo: "bar" } }); expect(interceptor.mock.calls[0][0]).toEqual({ body: {}, headers: { "content-type": "application/json;charset=UTF-8" }, // 拦截到元数据 meta: { foo: "bar" }, method: "POST", name: "/test", searchParams: {}, }); expect(fetch).toBeCalledWith("https://example.com/test", { body: "{}", headers: { "content-type": "application/json;charset=UTF-8" }, method: "POST", mode: "cors", }); }); test("拦截重试, 用于重新登录等复杂场景", async () => { const fetch = fetchEcho; initial({ baseURL: "https://example.com", fetch: fetch as any, interceptor: async (req, next) => { req.body.time = 1; await next(); // 二次重试请求 req.body.time = 2; return await next(); }, }); const result = await request("/test"); // 返回最后一次 next 的请求 expect(result).toEqual({ time: 2 }); // fetch 被调用两次 expect(fetch).toBeCalledWith("https://example.com/test", { body: '{"time":1}', headers: { "content-type": "application/json;charset=UTF-8" }, method: "POST", mode: "cors", }); expect(fetch).toBeCalledWith("https://example.com/test", { body: '{"time":2}', headers: { "content-type": "application/json;charset=UTF-8" }, method: "POST", mode: "cors", }); }); test("拦截重试,调用 next 多次应该抛出异常", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, interceptor: async (req, next) => { while (true) { await next(); } }, }); await expect(request("/test")).rejects.toThrow( "拦截器调用 next 次数过多, 请检查代码,可能存在无限循环" ); expect(fetch).toBeCalledTimes(3); }); test("响应内容规范化 - error", async () => { const fetch = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ data: "test", success: false, errorCode: 400, errorMessage: "mock error", }), headers: new Map(), }) ); initial({ baseURL: "https://example.com", fetch: fetch as any, }); try { await request("/test"); // never expect(true).toBe(false); } catch (err) { expect(err).toMatchObject({ code: 400, message: "mock error" }); } }); test("响应内容规范化 - success", async () => { const fetch = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ data: "test", success: true, code: 400, msg: "mock error", other: "foo", }), headers: new Map(), }) ); initial({ baseURL: "https://example.com", fetch: fetch as any, }); expect(await requestPagination("/test")).toEqual({ __raw__: { data: { code: 400, data: "test", msg: "mock error", other: "foo", success: true, }, header: {}, statusCode: undefined, }, data: "test", code: 400, msg: "mock error", other: "foo", success: true, }); }); test("变量替换", async () => { const fetch = fetchSuccess; initial({ baseURL: "https://example.com", fetch: fetch as any, globalVariables: { appId: "mock" }, }); expect( await request( "/test", { foo: "bar", myId: "{appId}" }, { method: "PUT", searchParams: { one: "two", myId: "{appId}" }, headers: { "X-POWER-BY": "test", "MY-ID": "{appId}", }, } ) ).toEqual("test"); expect(fetch).toBeCalledWith("https://example.com/test?myId=mock&one=two", { body: '{"foo":"bar","myId":"mock"}', headers: { "content-type": "application/json;charset=UTF-8", "x-power-by": "test", "my-id": "mock", }, method: "PUT", mode: "cors", }); });