From 4ba12491467d148e340247d453c1440b7e64c721 Mon Sep 17 00:00:00 2001 From: zjx0905 <954270063@qq.com> Date: Mon, 10 Apr 2023 18:53:21 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/request.ts | 1 - test/adapter.test.ts | 141 ++++++++++++++++++++- test/axios.api.test.ts | 11 +- test/axios.instance.test.ts | 2 + test/core/Axios.test.ts | 203 ++++++++++++++++++++++++++++-- test/core/cancel.test.ts | 12 ++ test/core/dispatchRequest.test.ts | 9 +- test/core/request.test.ts | 86 +++++++++++++ test/helpers/buildURL.test.ts | 10 ++ 9 files changed, 455 insertions(+), 20 deletions(-) diff --git a/src/core/request.ts b/src/core/request.ts index 5ca93ed..58bd1ae 100644 --- a/src/core/request.ts +++ b/src/core/request.ts @@ -32,7 +32,6 @@ function tryToggleProgressUpdate( progressUpdate(onDownloadProgress); } break; - default: } } } diff --git a/test/adapter.test.ts b/test/adapter.test.ts index 609e6e0..37879cb 100644 --- a/test/adapter.test.ts +++ b/test/adapter.test.ts @@ -38,6 +38,9 @@ describe('src/adapter.ts', () => { const a = createAdapter(p); const r = { type: 'request' as const, + headers: { + Accept: 'application/json, text/plain, */*', + }, url: 'test', method: 'GET' as const, success: noop, @@ -45,31 +48,159 @@ describe('src/adapter.ts', () => { }; const u = { type: 'upload' as const, + headers: { + Accept: 'application/json, text/plain, */*', + }, url: 'test', method: 'POST' as const, - data: {}, + data: { + name: 'file', + filePath: '/path/file', + user: 'test', + id: 1, + }, success: noop, fail: noop, }; const d = { type: 'download' as const, + headers: { + Accept: 'application/json, text/plain, */*', + }, url: 'test', method: 'GET' as const, - params: { filePath: '' }, + params: { + filePath: '/path/file', + }, success: noop, fail: noop, }; expect(p.request).not.toBeCalled(); a(r); - expect(p.request).toBeCalled(); + expect(p.request.mock.calls[0][0]).toMatchInlineSnapshot(` + { + "fail": [Function], + "header": { + "Accept": "application/json, text/plain, */*", + }, + "headers": { + "Accept": "application/json, text/plain, */*", + }, + "method": "GET", + "success": [Function], + "type": "request", + "url": "test", + } + `); expect(p.upload).not.toBeCalled(); a(u); - expect(p.upload).toBeCalled(); + expect(p.upload.mock.calls[0][0]).toMatchInlineSnapshot(` + { + "data": { + "filePath": "/path/file", + "id": 1, + "name": "file", + "user": "test", + }, + "fail": [Function], + "fileName": "file", + "filePath": "/path/file", + "fileType": undefined, + "formData": { + "id": 1, + "user": "test", + }, + "header": { + "Accept": "application/json, text/plain, */*", + }, + "headers": { + "Accept": "application/json, text/plain, */*", + }, + "method": "POST", + "name": "file", + "success": [Function], + "type": "upload", + "url": "test", + } + `); expect(p.download).not.toBeCalled(); a(d); - expect(p.download).toBeCalled(); + expect(p.download.mock.calls[0][0]).toMatchInlineSnapshot(` + { + "fail": [Function], + "filePath": "/path/file", + "header": { + "Accept": "application/json, text/plain, */*", + }, + "headers": { + "Accept": "application/json, text/plain, */*", + }, + "method": "GET", + "params": { + "filePath": "/path/file", + }, + "success": [Function], + "type": "download", + "url": "test", + } + `); + }); + + test('应该支持转换下载数据', () => { + const p1 = { + request: vi.fn(), + upload: vi.fn(), + download: vi.fn((config) => { + config.success({ + filePath: config.filePath, + tempFilePath: '/path/temp/file', + }); + }), + }; + const p2 = { + request: vi.fn(), + upload: vi.fn(), + download: vi.fn((config) => { + config.success({ + filePath: config.filePath, + apFilePath: '/path/temp/file', + }); + }), + }; + + createAdapter(p1)({ + type: 'download' as const, + url: 'test', + method: 'GET' as const, + params: { filePath: '/test/file' }, + success: (response: any) => { + expect(response.data).toMatchInlineSnapshot(` + { + "filePath": "/test/file", + "tempFilePath": "/path/temp/file", + } + `); + }, + fail: noop, + }); + + createAdapter(p2)({ + type: 'download' as const, + url: 'test', + method: 'GET' as const, + params: { filePath: '/test/file' }, + success: (response: any) => { + expect(response.data).toMatchInlineSnapshot(` + { + "filePath": "/test/file", + "tempFilePath": "/path/temp/file", + } + `); + }, + fail: noop, + }); }); }); diff --git a/test/axios.api.test.ts b/test/axios.api.test.ts index 64bf5ab..9776be3 100644 --- a/test/axios.api.test.ts +++ b/test/axios.api.test.ts @@ -17,12 +17,19 @@ describe('src/axios.ts', () => { }); test('应该可以创建实例', () => { - const i1 = axios.create(); const i2 = axios.create({ baseURL: 'http://api.com', }); - expect(i1.defaults).toEqual(defaults); + expect(i2.defaults).toEqual({ ...defaults, baseURL: 'http://api.com' }); + expect(i2.interceptors).toBeTypeOf('object'); + expect(i2.getUri).toBeTypeOf('function'); + expect(i2.fork).toBeTypeOf('function'); + expect(i2.request).toBeTypeOf('function'); + + [...Axios.as, ...Axios.asp, ...Axios.asd].forEach((k) => { + expect(i2[k]).toBeTypeOf('function'); + }); }); test('创建的实例应该有这些实例属性及方法', () => { diff --git a/test/axios.instance.test.ts b/test/axios.instance.test.ts index 8cc3cfb..e8b8669 100644 --- a/test/axios.instance.test.ts +++ b/test/axios.instance.test.ts @@ -1,6 +1,7 @@ import { describe, test, expect, beforeAll, afterAll } from 'vitest'; import { mockAdapter } from 'scripts/test.utils'; import Axios from '@/core/Axios'; +import AxiosDomain from '@/core/AxiosDomain'; import defaults from '@/defaults'; import axios from '@/axios'; @@ -121,5 +122,6 @@ describe('src/axios.ts', () => { baseURL: 'test', }); expect(a.defaults.baseURL).toBe('http://api.com/test'); + expect(a instanceof AxiosDomain).toBeTruthy(); }); }); diff --git a/test/core/Axios.test.ts b/test/core/Axios.test.ts index 28bd2c9..5faded2 100644 --- a/test/core/Axios.test.ts +++ b/test/core/Axios.test.ts @@ -1,4 +1,4 @@ -import { describe, test, expect } from 'vitest'; +import { describe, test, expect, vi } from 'vitest'; import { mockAdapter } from 'scripts/test.utils'; import Axios from '@/core/Axios'; import AxiosDomain from '@/core/AxiosDomain'; @@ -116,18 +116,201 @@ describe('src/core/Axios.ts', () => { }); }); + test('应该支持添加/移除请求拦截器', async () => { + const cb = vi.fn((v) => v); + + const id = axios.interceptors.request.use(cb); + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(1); + + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(2); + + axios.interceptors.request.eject(id); + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(2); + }); + + test('添加多个请求拦截器时应该按添加顺序从后往前依次执行', () => { + const axios = new Axios(); + + const cb1 = vi.fn((v) => { + expect(v.data.index).toBe(2); + v.data.index = 3; + return v; + }); + const cb2 = vi.fn((v) => { + expect(v.data.index).toBe(1); + v.data.index = 2; + return v; + }); + const cb3 = vi.fn((v) => { + expect(v.data.index).toBe(0); + v.data.index = 1; + return v; + }); + + axios.interceptors.request.use(cb1); + axios.interceptors.request.use(cb2); + axios.interceptors.request.use(cb3); + + axios.request('/test', { + adapter: (config) => { + expect(config.data!.index).toBe(3); + }, + data: { + index: 0, + }, + }); + }); + + test('请求拦截器应该支持抛出异常', async () => { + const axios = new Axios(); + const c = { adapter: vi.fn(), url: 'test' }; + const body = (v: any) => { + throw { ...v, throw: true }; + }; + const res1 = vi.fn(body); + const rej1 = vi.fn(body); + const res2 = vi.fn(body); + const rej2 = vi.fn(body); + + axios.interceptors.request.use(res1, rej1); + axios.interceptors.request.use(res2, rej2); + + try { + await axios.request(c); + } catch (err) { + expect(err).toEqual({ ...c, throw: true }); + } + + expect(c.adapter).not.toBeCalled(); + expect(res1).not.toBeCalled(); + expect(rej1).toBeCalled(); + expect(rej1.mock.calls[0][0]).toEqual({ ...c, throw: true }); + expect(res2).toBeCalled(); + expect(res2.mock.calls[0][0]).toEqual(c); + expect(rej2).not.toBeCalled(); + }); + + test('应该支持添加/移除响应拦截器', async () => { + const cb = vi.fn((v) => v); + + const id = axios.interceptors.response.use(cb); + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(1); + + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(2); + + axios.interceptors.response.eject(id); + await axios.request('/test', { + adapter: mockAdapter(), + }); + + expect(cb.mock.calls.length).toBe(2); + }); + + test('添加多个响应拦截器时应该按添加顺序从前往后依次执行', async () => { + const axios = new Axios(); + + const cb1 = vi.fn((v) => { + expect(v.data.index).toBe(0); + v.data.index = 1; + return v; + }); + const cb2 = vi.fn((v) => { + expect(v.data.index).toBe(1); + v.data.index = 2; + return v; + }); + const cb3 = vi.fn((v) => { + expect(v.data.index).toBe(2); + v.data.index = 3; + return v; + }); + + axios.interceptors.response.use(cb1); + axios.interceptors.response.use(cb2); + axios.interceptors.response.use(cb3); + + const response = await axios.request<{ index: 0 }>('/test', { + adapter: mockAdapter({ data: { index: 0 } }), + }); + + expect(response.data.index).toBe(3); + }); + + test('响应拦截器应该支持抛出异常', async () => { + const axios = new Axios(); + const c = { adapter: vi.fn(mockAdapter()), url: 'test' }; + const body = () => { + throw { throw: true }; + }; + const res1 = vi.fn(body); + const rej1 = vi.fn(body); + const res2 = vi.fn(body); + const rej2 = vi.fn(body); + + axios.interceptors.response.use(res1, rej1); + axios.interceptors.response.use(res2, rej2); + + try { + await axios.request(c); + } catch (err) { + expect(err).toEqual({ throw: true }); + } + expect(c.adapter).toBeCalled(); + expect(res1).toBeCalled(); + expect(rej1).not.toBeCalled(); + expect(res2).not.toBeCalled(); + expect(rej2).toBeCalled(); + }); + test('应该可以获取 URI', () => { - expect( - axios.getUri({ - url: 'test', - }), - ).toBe('test'); + expect(axios.getUri({ url: 'test' })).toBe('test'); + expect(axios.getUri({ url: 'test', params: { id: 1 } })).toBe('test?id=1'); + expect(axios.getUri({ url: 'test', paramsSerializer: () => 'id=1' })).toBe( + 'test?id=1', + ); }); test('应该可以派生领域', () => { - const a = axios.fork({ - baseURL: 'test', - }); - expect(a.defaults.baseURL).toBe('http://api.com/test'); + const a1 = axios.fork({ baseURL: 'test' }); + const a2 = new Axios().fork({ baseURL: 'test' }); + + expect(a1.defaults.baseURL).toBe('http://api.com/test'); + expect(a1 instanceof AxiosDomain).toBeTruthy(); + expect(a2.defaults.baseURL).toBe('/test'); + }); + + test('基于当前实例派生的领域应该可以复用当前实例上的中间件', async () => { + const axios = new Axios(); + const req = vi.fn((v) => v); + const res = vi.fn((v) => v); + + axios.interceptors.request.use(req); + axios.interceptors.response.use(res); + + const a = axios.fork({ baseURL: 'test' }); + await a.request('test', { adapter: mockAdapter() }); + + expect(req).toBeCalled(); + expect(res).toBeCalled(); }); }); diff --git a/test/core/cancel.test.ts b/test/core/cancel.test.ts index ed2d105..e74817f 100644 --- a/test/core/cancel.test.ts +++ b/test/core/cancel.test.ts @@ -97,6 +97,18 @@ describe('src/helpers/cancel.ts', () => { expect(() => s.token.throwIfRequested()).toThrowError(); }); + test('多次取消时应该只有第一次会生效', () => { + const s = CancelToken.source(); + + expect(s.token.throwIfRequested()).toBeUndefined(); + + s.cancel('1'); + s.cancel('2'); + s.cancel('3'); + + expect(() => s.token.throwIfRequested()).toThrowError('1'); + }); + test('应该可以在请求发出之前取消', async () => { const cb = vi.fn(); const s = CancelToken.source(); diff --git a/test/core/dispatchRequest.test.ts b/test/core/dispatchRequest.test.ts index 75bbe1e..aec977c 100644 --- a/test/core/dispatchRequest.test.ts +++ b/test/core/dispatchRequest.test.ts @@ -1,6 +1,7 @@ import { describe, test, expect, vi } from 'vitest'; import { asyncNext, + asyncTimeout, mockAdapter, mockAdapterError, mockAdapterFail, @@ -119,13 +120,17 @@ describe('src/core/dispatchRequest.ts', () => { ...defaults, adapter: mockAdapterError(), url: 'test', - errorHandler: e1, + errorHandler: async (err: unknown) => { + e1(err); + }, }; const c2 = { ...defaults, adapter: mockAdapterFail(), url: 'test', - errorHandler: e2, + errorHandler: async (err: unknown) => { + e2(err); + }, }; try { diff --git a/test/core/request.test.ts b/test/core/request.test.ts index 0406121..7f39288 100644 --- a/test/core/request.test.ts +++ b/test/core/request.test.ts @@ -1,5 +1,7 @@ import { describe, test, expect, vi } from 'vitest'; import { + asyncNext, + asyncTimeout, mockAdapter, mockAdapterError, mockAdapterFail, @@ -184,4 +186,88 @@ describe('src/core/request.ts', () => { }); }); }); + + test('应该可以监听上传进度', () => { + const on = vi.fn(); + const cb = vi.fn(); + + request({ + adapter: () => ({ + onProgressUpdate: on, + }), + url: 'test', + method: 'post', + upload: true, + onUploadProgress: cb, + }); + + expect(on).toBeCalled(); + expect(on.mock.calls[0][0]).toBe(cb); + }); + + test('取消请求时应该可以取消监听上传进度', async () => { + const on = vi.fn(); + const cb = vi.fn(); + const { cancel, token } = axios.CancelToken.source(); + + cancel(); + + await request({ + adapter: () => ({ + offProgressUpdate: on, + }), + url: 'test', + method: 'post', + upload: true, + onUploadProgress: cb, + cancelToken: token, + }).catch((err) => { + expect(axios.isCancel(err)).toBeTruthy(); + }); + + expect(on).toBeCalled(); + expect(on.mock.calls[0][0]).toBe(cb); + }); + + test('应该可以监听下载进度', () => { + const on = vi.fn(); + const cb = vi.fn(); + + request({ + adapter: () => ({ + onProgressUpdate: on, + }), + url: 'test', + method: 'get', + download: true, + onDownloadProgress: cb, + }); + + expect(on).toBeCalled(); + expect(on.mock.calls[0][0]).toBe(cb); + }); + + test('取消请求时应该可以取消监听下载进度', async () => { + const on = vi.fn(); + const cb = vi.fn(); + const { cancel, token } = axios.CancelToken.source(); + + cancel(); + + await request({ + adapter: () => ({ + offProgressUpdate: on, + }), + url: 'test', + method: 'get', + download: true, + onDownloadProgress: cb, + cancelToken: token, + }).catch((err) => { + expect(axios.isCancel(err)).toBeTruthy(); + }); + + expect(on).toBeCalled(); + expect(on.mock.calls[0][0]).toBe(cb); + }); }); diff --git a/test/helpers/buildURL.test.ts b/test/helpers/buildURL.test.ts index 1b241a7..c7c8801 100644 --- a/test/helpers/buildURL.test.ts +++ b/test/helpers/buildURL.test.ts @@ -25,6 +25,11 @@ describe('src/helpers/buildURL.ts', () => { }); test('应该对数组进行系列化', () => { + expect( + buildURL('/test', { + arr: [], + }), + ).toBe('/test'); expect( buildURL('/test', { arr: [1, 2], @@ -33,6 +38,11 @@ describe('src/helpers/buildURL.ts', () => { }); test('应该对对象进行系列化', () => { + expect( + buildURL('/test', { + obj: {}, + }), + ).toBe('/test'); expect( buildURL('/test', { obj: {