From ce12a8387335ce52c6c721f1580801394b016634 Mon Sep 17 00:00:00 2001 From: zjx0905 <954270063@qq.com> Date: Sun, 2 Apr 2023 23:27:45 +0800 Subject: [PATCH] =?UTF-8?q?test:=20=E6=B5=8B=E8=AF=95=20generateType?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/test.utils.ts | 37 ++++++++++ src/core/Axios.ts | 128 +++++++++++++++++---------------- src/core/dispatchRequest.ts | 7 +- src/core/request.ts | 7 +- src/helpers/error.ts | 5 +- test/axios.test.ts | 42 +++++++++++ test/core/cancel.test.ts | 13 ++-- test/core/generateType.test.ts | 50 +++++++++++++ 8 files changed, 215 insertions(+), 74 deletions(-) create mode 100644 test/axios.test.ts create mode 100644 test/core/generateType.test.ts diff --git a/scripts/test.utils.ts b/scripts/test.utils.ts index 23b8667..ad44740 100644 --- a/scripts/test.utils.ts +++ b/scripts/test.utils.ts @@ -1,3 +1,5 @@ +import { AxiosAdapterRequestConfig } from 'src'; + export function asyncNext() { return Promise.resolve().then; } @@ -47,3 +49,38 @@ export function mockSuccess(headers: AnyObject = {}, data: AnyObject = {}) { export function mockFail(headers: AnyObject = {}, data: AnyObject = {}) { return mockResponse(400, 'FAIL', headers, data); } + +export interface MockAdapterOptions { + headers?: AnyObject; + data?: AnyObject; + delay?: number; + before?: (config: AxiosAdapterRequestConfig) => void; + after?: () => void; +} + +export function mockAdapter( + type: 'success' | 'fail', + options: MockAdapterOptions = {}, +) { + const { headers = {}, data = {}, delay = 0, before, after } = options; + + return (config: AxiosAdapterRequestConfig) => { + before?.(config); + setTimeout(() => { + if (type === 'success') { + config.success(mockSuccess(headers, data)); + } else { + config.fail(mockFail(headers, data)); + } + after?.(); + }, delay); + }; +} + +export function mockAdapterSuccess(options: MockAdapterOptions = {}) { + return mockAdapter('success', options); +} + +export function mockAdapterFail(options: MockAdapterOptions = {}) { + return mockAdapter('fail', options); +} diff --git a/src/core/Axios.ts b/src/core/Axios.ts index b8ec38c..4271736 100644 --- a/src/core/Axios.ts +++ b/src/core/Axios.ts @@ -138,6 +138,10 @@ export interface AxiosConstructor { } export default class Axios { + public static as = ['options', 'trace', 'connect'] as const; + public static pas = ['head', 'get', 'delete'] as const; + public static das = ['post', 'put'] as const; + public defaults: AxiosRequestConfig; public interceptors = { @@ -145,8 +149,71 @@ export default class Axios { response: new InterceptorManager(), }; + public options!: ( + url: string, + config?: AxiosRequestConfig, + ) => Promise>; + + public get!: ( + url: string, + params?: AnyObject, + config?: AxiosRequestConfig, + ) => Promise>; + + public head!: ( + url: string, + params?: AnyObject, + config?: AxiosRequestConfig, + ) => Promise>; + + public post!: ( + url: string, + data?: AnyObject | AxiosRequestFormData, + config?: AxiosRequestConfig, + ) => Promise>; + + public put!: ( + url: string, + data?: AnyObject | AxiosRequestFormData, + config?: AxiosRequestConfig, + ) => Promise>; + + public delete!: ( + url: string, + params?: AnyObject, + config?: AxiosRequestConfig, + ) => Promise>; + + public trace!: ( + url: string, + config?: AxiosRequestConfig, + ) => Promise>; + + public connect!: ( + url: string, + config?: AxiosRequestConfig, + ) => Promise>; + public constructor(defaults: AxiosRequestConfig = {}) { this.defaults = defaults; + + for (const alias of Axios.as) { + this[alias] = (url, config) => { + return this._req(alias, url, undefined, config); + }; + } + + for (const alias of Axios.pas) { + this[alias] = (url, params, config) => { + return this._req(alias, url, params, config); + }; + } + + for (const alias of Axios.das) { + this[alias] = (url, data, config) => { + return this._reqWithData(alias, url, data, config); + }; + } } public getUri(config: AxiosRequestConfig): string { @@ -183,67 +250,6 @@ export default class Axios { return promiseResponse as Promise>; } - public options( - url: string, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('options', url, undefined, config); - } - - public get( - url: string, - params?: AnyObject, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('get', url, params, config); - } - - public head( - url: string, - params?: AnyObject, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('head', url, params, config); - } - - public post( - url: string, - data?: AnyObject | AxiosRequestFormData, - config?: AxiosRequestConfig, - ): Promise> { - return this._reqWithData('post', url, data, config); - } - - public put( - url: string, - data?: AnyObject | AxiosRequestFormData, - config?: AxiosRequestConfig, - ): Promise> { - return this._reqWithData('put', url, data, config); - } - - public delete( - url: string, - params?: AnyObject, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('delete', url, params, config); - } - - public trace( - url: string, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('trace', url, undefined, config); - } - - public connect( - url: string, - config?: AxiosRequestConfig, - ): Promise> { - return this._req('connect', url, undefined, config); - } - private _req( method: AxiosRequestMethod, url: string, diff --git a/src/core/dispatchRequest.ts b/src/core/dispatchRequest.ts index 20ac484..2a1f5fe 100644 --- a/src/core/dispatchRequest.ts +++ b/src/core/dispatchRequest.ts @@ -1,5 +1,5 @@ import { isPlainObject } from '../helpers/isTypes'; -import { isCancel } from './cancel'; +import { isCancel, isCancelToken } from './cancel'; import { flattenHeaders } from './flattenHeaders'; import { transformData } from './transformData'; import { request } from './request'; @@ -7,8 +7,9 @@ import { AxiosRequestConfig, AxiosResponse } from './Axios'; import { transformURL } from './transformURL'; function throwIfCancellationRequested(config: AxiosRequestConfig) { - if (config.cancelToken) { - config.cancelToken.throwIfRequested(); + const { cancelToken } = config; + if (isCancelToken(cancelToken)) { + cancelToken.throwIfRequested(); } } diff --git a/src/core/request.ts b/src/core/request.ts index 5a60456..784a14f 100644 --- a/src/core/request.ts +++ b/src/core/request.ts @@ -52,7 +52,7 @@ export function request( fail, }; - const adapterTask = config.adapter?.(adapterConfig) as + const adapterTask = config.adapter!(adapterConfig) as | AxiosAdapterTask | undefined; @@ -83,8 +83,9 @@ export function request( tryToggleProgressUpdate(adapterConfig, adapterTask.onProgressUpdate); } - if (isCancelToken(config.cancelToken)) { - config.cancelToken.onCancel((reason: unknown) => { + const { cancelToken } = config; + if (isCancelToken(cancelToken)) { + cancelToken.onCancel((reason) => { if (isPlainObject(adapterTask)) { tryToggleProgressUpdate(adapterConfig, adapterTask.offProgressUpdate); diff --git a/src/helpers/error.ts b/src/helpers/error.ts index 957a3a6..03fca6f 100644 --- a/src/helpers/error.ts +++ b/src/helpers/error.ts @@ -13,9 +13,8 @@ export function throwError(msg: string): void { export function cleanStack(error: Error) { if (error.stack) { const start = error.stack.indexOf('at'); - const end = error.stack.indexOf('at /'); - - if (start !== end) { + const end = error.stack.search(/at ([\w-_.]+:)?\//i); + if (start < end) { const removed = error.stack.slice(start, end); error.stack = error.stack.replace(removed, ''); } diff --git a/test/axios.test.ts b/test/axios.test.ts new file mode 100644 index 0000000..01ef256 --- /dev/null +++ b/test/axios.test.ts @@ -0,0 +1,42 @@ +import { describe, test, expect } from 'vitest'; +import axios from 'src/axios'; +import { mockAdapterFail, mockAdapterSuccess } from 'scripts/test.utils'; + +describe('src/axios.ts', () => { + test('应该处理成功和失败', () => { + axios({ + adapter: mockAdapterSuccess({ + headers: { type: 'json' }, + data: { v1: 1 }, + before: (config) => { + expect(config.url).toBe('http://api.com/user/1?id=1'); + }, + }), + baseURL: 'http://api.com', + url: 'user/:id', + params: { + id: 1, + }, + }).then((response) => { + expect(response.headers).toEqual({ type: 'json' }); + expect(response.data).toEqual({ v1: 1 }); + }); + + axios('user/:id', { + adapter: mockAdapterFail({ + headers: { type: 'json' }, + data: { v1: 1 }, + before: (config) => { + expect(config.url).toBe('http://api.com/user/1'); + }, + }), + baseURL: 'http://api.com', + data: { + id: 1, + }, + }).catch((error) => { + expect(error.response.headers).toEqual({ type: 'json' }); + expect(error.response.data).toEqual({ v1: 1 }); + }); + }); +}); diff --git a/test/core/cancel.test.ts b/test/core/cancel.test.ts index 3ee286a..ca7b25c 100644 --- a/test/core/cancel.test.ts +++ b/test/core/cancel.test.ts @@ -2,12 +2,17 @@ import { describe, test, expect, vi } from 'vitest'; import { asyncNext, captureError, - mockSuccess, + mockAdapterSuccess, noop, asyncTimeout, } from 'scripts/test.utils'; import axios from 'src/axios'; -import { Cancel, isCancel, CancelToken, isCancelToken } from 'src/core/cancel'; +import { + Cancel, + isCancel, + CancelToken, + isCancelToken, +} from '../../src/core/cancel'; describe('src/helpers/cancel.ts', () => { test('应该支持空参数', () => { @@ -100,7 +105,7 @@ describe('src/helpers/cancel.ts', () => { source.cancel(); axios({ - adapter: ({ success }) => success(mockSuccess()), + adapter: mockAdapterSuccess(), cancelToken: source.token, }).catch(canceled); @@ -114,7 +119,7 @@ describe('src/helpers/cancel.ts', () => { const source = CancelToken.source(); axios({ - adapter: ({ success }) => success(mockSuccess()), + adapter: mockAdapterSuccess(), cancelToken: source.token, }).catch(canceled); source.cancel(); diff --git a/test/core/generateType.test.ts b/test/core/generateType.test.ts new file mode 100644 index 0000000..354d689 --- /dev/null +++ b/test/core/generateType.test.ts @@ -0,0 +1,50 @@ +import { describe, test, expect } from 'vitest'; +import { mockAdapterSuccess } from 'scripts/test.utils'; +import { generateType } from 'src/core/generateType'; +import Axios from 'src/core/Axios'; +import axios from 'src/axios'; + +describe('src/core/generateType.ts', () => { + test('应该是一个 reuqest', () => { + for (const alias of [...Axios.as, ...Axios.pas, ...Axios.das]) { + expect(generateType({ method: alias })).toBe('request'); + + axios({ + adapter: mockAdapterSuccess({ + before: (config) => { + expect(config.type).toBe('request'); + }, + }), + method: alias, + }); + } + }); + + test('应该是一个 upload', () => { + expect(generateType({ method: 'post', upload: true })).toBe('upload'); + + axios({ + adapter: mockAdapterSuccess({ + before: (config) => { + expect(config.type).toBe('upload'); + }, + }), + method: 'post', + upload: true, + }); + }); + + test('应该是一个 download', () => { + expect(generateType({ method: 'get', download: true })).toBe('download'); + + axios({ + adapter: mockAdapterSuccess({ + before: (config) => { + expect(config.type).toBe('download'); + }, + }), + method: 'get', + download: true, + }); + }); +});