|
@@ -0,0 +1,329 @@
|
|
|
+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", "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" },
|
|
|
+ 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", "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" },
|
|
|
+ // 拦截到元数据
|
|
|
+ meta: { foo: "bar" },
|
|
|
+ method: "POST",
|
|
|
+ name: "/test",
|
|
|
+ searchParams: {},
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(fetch).toBeCalledWith("https://example.com/test", {
|
|
|
+ body: "{}",
|
|
|
+ headers: { "content-type": "application/json" },
|
|
|
+ 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" },
|
|
|
+ method: "POST",
|
|
|
+ mode: "cors",
|
|
|
+ });
|
|
|
+ expect(fetch).toBeCalledWith("https://example.com/test", {
|
|
|
+ body: '{"time":2}',
|
|
|
+ headers: { "content-type": "application/json" },
|
|
|
+ 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",
|
|
|
+ "x-power-by": "test",
|
|
|
+ "my-id": "mock",
|
|
|
+ },
|
|
|
+ method: "PUT",
|
|
|
+ mode: "cors",
|
|
|
+ });
|
|
|
+});
|