import { describe, test, expect, vi } from 'vitest'; import { asyncTimeout, eachMethods, mockAdapter, mockAdapterError, mockAdapterFail, testEachMethods, } from 'scripts/test.utils'; import { request } from '@/request/request'; import axios from '@/axios'; describe('src/request/request.ts', () => { testEachMethods('%s 方法应该返回正确的响应体结构', (k) => { const c = { adapter: mockAdapter(), url: '/test', method: k, }; request(c).then((response) => { expect(response.status).toBeTypeOf('number'); expect(response.statusText).toBeTypeOf('string'); expect(response.headers).toBeTypeOf('object'); expect(response.data).toBeTypeOf('object'); expect(response.config).toBeTypeOf('object'); expect(response.request).toBeTypeOf('object'); }); }); testEachMethods('%s 方法应该支持补全响应体结构', (k) => { const c = { // @ts-ignore adapter: ({ success }) => { success({ data: {}, }); }, url: '/test', method: k, }; request(c).then((response) => { expect(response.status).toBeTypeOf('number'); expect(response.statusText).toBeTypeOf('string'); expect(response.headers).toBeTypeOf('object'); expect(response.data).toBeTypeOf('object'); expect(response.isFail).toBeTypeOf('undefined'); expect(response.config).toBeTypeOf('object'); expect(response.request).toBeTypeOf('undefined'); }); }); testEachMethods('%s 方法应该返回正确的响应错误结构', (k) => { const c = { adapter: mockAdapterError(), validateStatus: () => false, url: '/test', method: k, }; request(c).catch((error) => { expect(error.message).toBeTypeOf('string'); expect(error.config).toBeTypeOf('object'); expect(error.request).toBeTypeOf('object'); expect(error.response.status).toBeTypeOf('number'); expect(error.response.statusText).toBeTypeOf('string'); expect(error.response.headers).toBeTypeOf('object'); expect(error.response.data).toBeTypeOf('object'); expect(error.response.isFail).toBeTypeOf('undefined'); expect(error.response.config).toBeTypeOf('object'); expect(error.response.request).toBeTypeOf('object'); }); }); testEachMethods('%s 方法应该返回正确的平台错误结构', (k) => { const c = { adapter: mockAdapterFail(), validateStatus: () => false, url: '/test', method: k, }; request(c).catch((error) => { expect(error.message).toBeTypeOf('string'); expect(error.config).toBeTypeOf('object'); expect(error.request).toBeTypeOf('object'); expect(error.response.status).toBeTypeOf('number'); expect(error.response.statusText).toBeTypeOf('string'); expect(error.response.headers).toBeTypeOf('object'); expect(error.response.data).toBeTypeOf('object'); expect(error.response.isFail).toBeTruthy(); expect(error.response.config).toBeTypeOf('object'); expect(error.response.request).toBeTypeOf('object'); }); }); testEachMethods('%s 方法应该支持补全错误体结构', (k) => { const c = { // @ts-ignore adapter: ({ fail }) => { fail({ data: {}, }); }, url: '/test', method: k, }; request(c).catch((error) => { expect(error.response.status).toBeTypeOf('number'); expect(error.response.statusText).toBeTypeOf('string'); expect(error.response.headers).toBeTypeOf('object'); expect(error.response.data).toBeTypeOf('object'); expect(error.response.isFail).toBeTruthy(); expect(error.response.config).toBeTypeOf('object'); expect(error.response.request).toBeTypeOf('undefined'); }); }); test('应该支持请求发送前取消请求', async () => { const cb = vi.fn(); const task = { abort: vi.fn(), }; const { cancel, token } = axios.CancelToken.source(); cancel(); await request({ adapter: () => task, url: '/test', method: 'get' as const, cancelToken: token, }).catch(cb); expect(task.abort).toBeCalled(); expect(cb).toBeCalled(); }); test('应该支持请求发送后取消请求', async () => { const cb = vi.fn(); const task = { abort: vi.fn(), }; const { cancel, token } = axios.CancelToken.source(); const p = request({ adapter: () => task, url: '/test', method: 'get' as const, cancelToken: token, }).catch(cb); cancel(); await p; expect(task.abort).toBeCalled(); expect(cb).toBeCalled(); }); test('无 task 也应该可以取消请求', async () => { const cb = vi.fn(); const { cancel, token } = axios.CancelToken.source(); cancel(); await request({ adapter: () => undefined, url: '/test', method: 'get' as const, cancelToken: token, }).catch(cb); expect(cb).toBeCalled(); expect(axios.isCancel(cb.mock.calls[0][0])).toBeTruthy(); }); test('应该发送不同类型的请求', () => { request({ adapter: ({ type }) => { expect(type).toBe('upload'); }, url: 'test', method: 'post', upload: true, }); request({ adapter: ({ type }) => { expect(type).toBe('download'); }, url: 'test', method: 'get', download: true, }); eachMethods((a) => { request({ adapter: ({ type }) => { expect(type).toBe('request'); }, url: 'test', method: a, }); }); }); 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(); }); await asyncTimeout(); expect(on).toBeCalled(); expect(on.mock.calls[0][0]).toBe(cb); }); test('应该可以监听下载进度', async () => { const on = vi.fn(); const cb = vi.fn(); request({ adapter: () => ({ onProgressUpdate: on, }), url: 'test', method: 'get', download: true, onDownloadProgress: cb, }); await asyncTimeout(); 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); }); test('上传不应该监听下载进度/下载不应该监听上传进度', async () => { const on = vi.fn(); const cb = vi.fn(); request({ adapter: () => ({ onProgressUpdate: on, }), url: 'test', method: 'post', upload: true, onDownloadProgress: cb, }); request({ adapter: () => ({ onProgressUpdate: on, }), url: 'test', method: 'get', download: true, onUploadProgress: cb, }); await asyncTimeout(); expect(on).not.toBeCalled(); expect(cb).not.toBeCalled(); }); });