diff --git a/src/adpater/createAdapter.ts b/src/adpater/createAdapter.ts index 192343b..9bddaec 100644 --- a/src/adpater/createAdapter.ts +++ b/src/adpater/createAdapter.ts @@ -140,11 +140,11 @@ export interface AxiosAdapterBaseOptions extends AxiosAdapterRequestConfig { /** * 成功的回调 */ - success(response: AxiosAdapterResponse): void; + success(response: AnyObject): void; /** * 失败的回调 */ - fail(error: AxiosAdapterResponseError): void; + fail(error: AnyObject): void; } /** @@ -271,11 +271,11 @@ export function createAdapter(platform: AxiosAdapterPlatform) { return { ...config, header: config.headers, - success(response) { + success(response: AxiosAdapterResponse) { transformResponse(response); config.success(response); }, - fail(responseError) { + fail(responseError: AxiosAdapterResponseError) { responseError.data = { errMsg: responseError.errMsg, errno: responseError.errno, @@ -284,14 +284,14 @@ export function createAdapter(platform: AxiosAdapterPlatform) { config.fail(responseError); }, }; + } - function transformResponse( - response: AxiosAdapterResponse | AxiosAdapterResponseError, - ) { - response.status = response.status ?? response.statusCode; - response.headers = response.headers ?? response.header; - clean(response, ['statusCode', 'errMsg', 'errno', 'header']); - } + function transformResponse( + response: AxiosAdapterResponse | AxiosAdapterResponseError, + ) { + response.status = response.status ?? response.statusCode; + response.headers = response.headers ?? response.header; + clean(response, ['statusCode', 'errMsg', 'errno', 'header']); } function processRequest( diff --git a/src/axios.ts b/src/axios.ts index 4d55c38..5811d04 100644 --- a/src/axios.ts +++ b/src/axios.ts @@ -4,37 +4,13 @@ import { isCancel, } from './request/cancel'; import { isAxiosError } from './request/createError'; +import Axios, { AxiosConstructor, AxiosRequestConfig } from './core/Axios'; +import { AxiosInstance, createInstance } from './core/createInstance'; import { mergeConfig } from './core/mergeConfig'; -import { AxiosDomainRequest } from './core/AxiosDomain'; -import Axios, { - AxiosConstructor, - AxiosRequestConfig, - AxiosRequestHeaders, -} from './core/Axios'; import { createAdapter } from './adpater/createAdapter'; import defaults from './defaults'; import { version } from './version'; -/** - * axios 实例默认配置 - */ -export interface AxiosInstanceDefaults extends AxiosRequestConfig { - /** - * 请求头 - */ - headers: Required; -} - -/** - * axios 实例 - */ -export interface AxiosInstance extends AxiosDomainRequest, Axios { - /** - * 默认请求配置 - */ - defaults: AxiosInstanceDefaults; -} - /** * axios 静态对象 */ @@ -51,12 +27,6 @@ export interface AxiosStatic extends AxiosInstance { * 取消令牌 */ CancelToken: CancelTokenConstructor; - /** - * 创建 axios 实例 - * - * @param config 默认配置 - */ - create(config?: AxiosRequestConfig): AxiosInstance; /** * 创建适配器 */ @@ -69,26 +39,18 @@ export interface AxiosStatic extends AxiosInstance { * 传入响应错误返回 true */ isAxiosError: typeof isAxiosError; -} - -function createInstance(config: AxiosRequestConfig) { - const context = new Axios(config); - const instance = context.request as AxiosInstance; - - Object.assign(instance, context); - Object.setPrototypeOf(instance, Axios.prototype); - - return instance; + /** + * 创建 axios 实例 + * + * @param config 默认配置 + */ + create(config?: AxiosRequestConfig): AxiosInstance; } const axios = createInstance(defaults) as AxiosStatic; - axios.create = function create(config) { - const instance = createInstance(mergeConfig(axios.defaults, config)); - instance.flush = axios.middleware.wrap(instance.flush); - return instance; + return createInstance(mergeConfig(axios.defaults, config)); }; - axios.version = version; axios.Axios = Axios; axios.CancelToken = CancelToken; diff --git a/src/core/Axios.ts b/src/core/Axios.ts index dfbdf32..415ccd8 100644 --- a/src/core/Axios.ts +++ b/src/core/Axios.ts @@ -1,7 +1,9 @@ -import { buildURL } from '../helpers/buildURL'; import { combineURL } from '../helpers/combineURL'; +import { isString } from '../helpers/isTypes'; +import { dispatchRequest } from '../request/dispatchRequest'; import { CancelToken } from '../request/cancel'; import { AxiosTransformer } from '../request/transformData'; +import { deepMerge } from '../helpers/deepMerge'; import { AxiosAdapter, AxiosAdapterRequestMethod, @@ -9,9 +11,17 @@ import { AxiosAdapterRequestConfig, AxiosAdapterResponseData, } from '../adpater/createAdapter'; -import InterceptorManager, { Interceptor } from './InterceptorManager'; +import InterceptorManager, { + Interceptor, + InterceptorExecutor, +} from './InterceptorManager'; import { mergeConfig } from './mergeConfig'; -import AxiosDomain, { AxiosDomainRequestHandler } from './AxiosDomain'; +import { + PLAIN_METHODS, + WITH_DATA_METHODS, + WITH_PARAMS_METHODS, +} from '../constants/methods'; +import MiddlewareManager from './MiddlewareManager'; /** * 请求方法 @@ -293,6 +303,51 @@ export interface AxiosResponseError extends AnyObject { request?: AxiosAdapterPlatformTask; } +export interface AxiosContext { + req: AxiosRequestConfig; + res: null | AxiosResponse; +} + +export interface AxiosRequest { + (config: AxiosRequestConfig): Promise< + AxiosResponse + >; + ( + url: string, + config?: AxiosRequestConfig, + ): Promise>; +} + +/** + * 普通的请求方法 + */ +export type AxiosRequestMethodFn = ( + url: string, + config?: AxiosRequestConfig, +) => Promise>; + +/** + * 带参数的请求方法 + */ +export type AxiosRequestMethodFnWithParams = ( + url: string, + params?: AnyObject, + config?: AxiosRequestConfig, +) => Promise>; + +/** + * 带数据的请求方法 + */ +export type AxiosRequestMethodFnWithData = ( + url: string, + data?: AxiosRequestData, + config?: AxiosRequestConfig, +) => Promise>; + +export interface AxiosDomainRequestHandler { + (config: AxiosRequestConfig): Promise; +} + /** * Axios 构造函数 */ @@ -300,7 +355,13 @@ export interface AxiosConstructor { new (config: AxiosRequestConfig): Axios; } -export default class Axios extends AxiosDomain { +export default class Axios { + #parent?: Axios; + /** + * 默认请求配置 + */ + defaults: AxiosRequestConfig; + /** * 拦截器 */ @@ -315,37 +376,102 @@ export default class Axios extends AxiosDomain { response: new InterceptorManager(), }; - constructor(defaults: AxiosRequestConfig = {}) { - super(defaults, (...args) => this.#processRequest(...args)); - } + /** + * 中间件 + */ + #middleware = new MiddlewareManager(async (ctx) => { + ctx.res = await dispatchRequest(ctx.req); + }); - getUri(config: AxiosRequestConfig): string { - const { url, params, paramsSerializer } = mergeConfig( - this.defaults, - config, - ); - return buildURL(url, params, paramsSerializer).replace(/^\?/, ''); + /** + * 发送 options 请求 + */ + options!: AxiosRequestMethodFn; + + /** + * 发送 get 请求 + */ + get!: AxiosRequestMethodFnWithParams; + + /** + * 发送 head 请求 + */ + head!: AxiosRequestMethodFnWithParams; + + /** + * 发送 post 请求 + */ + post!: AxiosRequestMethodFnWithData; + + /** + * 发送 put 请求 + */ + put!: AxiosRequestMethodFnWithData; + + /** + * 发送 patch 请求 + */ + patch!: AxiosRequestMethodFnWithData; + + /** + * 发送 delete 请求 + */ + delete!: AxiosRequestMethodFnWithParams; + + /** + * 发送 trace 请求 + */ + trace!: AxiosRequestMethodFn; + + /** + * 发送 connect 请求 + */ + connect!: AxiosRequestMethodFn; + + /** + * 添加中间件 + */ + use: MiddlewareManager['use']; + + constructor(defaults: AxiosRequestConfig = {}, parent?: Axios) { + this.defaults = defaults; + this.#parent = parent; + if (this.#parent) { + this.#middleware.flush = this.#parent.#middleware.wrap( + this.#middleware.flush, + ); + } + this.use = this.#middleware.use.bind(this.#middleware); } /** - * 派生领域 + * 发送请求 */ - fork = (config: AxiosRequestConfig = {}) => { - config.baseURL = combineURL(this.defaults.baseURL, config.baseURL); - const domain = new AxiosDomain( - mergeConfig(this.defaults, config), - (...args) => this.#processRequest(...args), - ); - domain.flush = this.middleware.wrap(domain.flush); - return domain; + request: AxiosRequest = ( + urlOrConfig: string | AxiosRequestConfig, + config: AxiosRequestConfig = {}, + ) => { + if (isString(urlOrConfig)) { + config.url = urlOrConfig; + } else { + config = urlOrConfig; + } + config.method = config.method || 'get'; + + return this.#processRequest(mergeConfig(this.defaults, config)); }; - #processRequest( - config: AxiosRequestConfig, - requestHandlerFn: AxiosDomainRequestHandler, - ) { + #processRequest(config: AxiosRequestConfig) { const requestHandler = { - resolved: requestHandlerFn, + resolved: async (config: AxiosRequestConfig) => { + config.url = combineURL(config.baseURL, config.url); + const ctx: AxiosContext = { + req: config, + res: null, + }; + await this.#middleware.flush(ctx); + return ctx.res as AxiosResponse; + }, }; const errorHandler = { rejected: config.errorHandler, @@ -355,11 +481,11 @@ export default class Axios extends AxiosDomain { | Partial> )[] = []; - this.interceptors.request.forEach((requestInterceptor) => { + this.#eacheRequestInterceptors((requestInterceptor) => { chain.unshift(requestInterceptor); }); chain.push(requestHandler); - this.interceptors.response.forEach((responseInterceptor) => { + this.#eacheResponseInterceptors((responseInterceptor) => { chain.push(responseInterceptor); }); chain.push(errorHandler); @@ -374,4 +500,49 @@ export default class Axios extends AxiosDomain { Promise.resolve(config), ) as Promise; } + + #eacheRequestInterceptors(executor: InterceptorExecutor) { + this.interceptors.request.forEach(executor); + if (this.#parent) { + this.#parent.#eacheRequestInterceptors(executor); + } + } + + #eacheResponseInterceptors(executor: InterceptorExecutor) { + this.interceptors.response.forEach(executor); + if (this.#parent) { + this.#parent.#eacheResponseInterceptors(executor); + } + } +} + +for (const method of PLAIN_METHODS) { + Axios.prototype[method] = function processRequestMethod(url, config = {}) { + config.method = method; + return this.request(url, config); + }; +} + +for (const method of WITH_PARAMS_METHODS) { + Axios.prototype[method] = function processRequestMethodWithParams( + url, + params = {}, + config = {}, + ) { + config.method = method; + config.params = deepMerge(params, config.params ?? {}); + return this.request(url, config); + }; +} + +for (const method of WITH_DATA_METHODS) { + Axios.prototype[method] = function processRequestMethodWithData( + url, + data, + config = {}, + ) { + config.method = method; + config.data = data; + return this.request(url, config); + }; } diff --git a/src/core/AxiosDomain.ts b/src/core/AxiosDomain.ts deleted file mode 100644 index e631226..0000000 --- a/src/core/AxiosDomain.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { - PLAIN_METHODS, - WITH_DATA_METHODS, - WITH_PARAMS_METHODS, -} from '../constants/methods'; -import { isString, isUndefined } from '../helpers/isTypes'; -import { deepMerge } from '../helpers/deepMerge'; -import { combineURL } from '../helpers/combineURL'; -import { dispatchRequest } from '../request/dispatchRequest'; -import { mergeConfig } from './mergeConfig'; -import { - AxiosRequestConfig, - AxiosRequestData, - AxiosResponse, - AxiosResponseData, -} from './Axios'; -import MiddlewareManager, { - MiddlewareContext, - MiddlewareFlush, -} from './MiddlewareManager'; - -/** - * 请求函数 - */ -export interface AxiosDomainRequest { - (config: AxiosRequestConfig): Promise< - AxiosResponse - >; - ( - url: string, - config?: AxiosRequestConfig, - ): Promise>; -} - -/** - * 普通的请求方法 - */ -export type AxiosDomainRequestMethod = ( - url: string, - config?: AxiosRequestConfig, -) => Promise>; - -/** - * 带参数的请求方法 - */ -export type AxiosDomainRequestMethodWithParams = < - TData extends AxiosResponseData, ->( - url: string, - params?: AnyObject, - config?: AxiosRequestConfig, -) => Promise>; - -/** - * 带数据的请求方法 - */ -export type AxiosDomainRequestMethodWithData = < - TData extends AxiosResponseData, ->( - url: string, - data?: AxiosRequestData, - config?: AxiosRequestConfig, -) => Promise>; - -export interface AxiosDomainRequestHandler { - (config: AxiosRequestConfig): Promise; -} - -export default class AxiosDomain { - /** - * 默认请求配置 - */ - defaults: AxiosRequestConfig; - - middleware = new MiddlewareManager(); - - /** - * 发送请求 - */ - request!: AxiosDomainRequest; - - /** - * 发送 options 请求 - */ - options!: AxiosDomainRequestMethod; - - /** - * 发送 get 请求 - */ - get!: AxiosDomainRequestMethodWithParams; - - /** - * 发送 head 请求 - */ - head!: AxiosDomainRequestMethodWithParams; - - /** - * 发送 post 请求 - */ - post!: AxiosDomainRequestMethodWithData; - - /** - * 发送 put 请求 - */ - put!: AxiosDomainRequestMethodWithData; - - /** - * 发送 patch 请求 - */ - patch!: AxiosDomainRequestMethodWithData; - - /** - * 发送 delete 请求 - */ - delete!: AxiosDomainRequestMethodWithParams; - - /** - * 发送 trace 请求 - */ - trace!: AxiosDomainRequestMethod; - - /** - * 发送 connect 请求 - */ - connect!: AxiosDomainRequestMethod; - - flush: MiddlewareFlush; - - constructor( - defaults: AxiosRequestConfig, - processRequest: ( - config: AxiosRequestConfig, - requestHandler: AxiosDomainRequestHandler, - ) => Promise, - ) { - this.defaults = defaults; - - this.request = ( - urlOrConfig: string | AxiosRequestConfig, - config: AxiosRequestConfig = {}, - ) => { - if (isString(urlOrConfig)) { - config.url = urlOrConfig; - } else { - config = urlOrConfig; - } - - if (isUndefined(config.method)) { - config.method = 'get'; - } - - return processRequest( - mergeConfig(this.defaults, config), - this.#requestHandler, - ); - }; - this.flush = this.middleware.wrap(async (ctx) => { - ctx.res = await dispatchRequest(ctx.req); - }); - } - - #requestHandler: AxiosDomainRequestHandler = async (config) => { - config.url = combineURL(config.baseURL, config.url); - const ctx: MiddlewareContext = { - req: config, - res: null, - }; - await this.flush(ctx); - return ctx.res as AxiosResponse; - }; -} - -for (const method of PLAIN_METHODS) { - AxiosDomain.prototype[method] = function processRequestMethod( - url, - config = {}, - ) { - config.method = method; - return this.request(url, config); - }; -} - -for (const method of WITH_PARAMS_METHODS) { - AxiosDomain.prototype[method] = function processRequestMethodWithParams( - url, - params = {}, - config = {}, - ) { - config.method = method; - config.params = deepMerge(params, config.params ?? {}); - return this.request(url, config); - }; -} - -for (const method of WITH_DATA_METHODS) { - AxiosDomain.prototype[method] = function processRequestMethodWithData( - url, - data, - config = {}, - ) { - config.method = method; - config.data = data; - return this.request(url, config); - }; -} diff --git a/src/core/MiddlewareManager.ts b/src/core/MiddlewareManager.ts index c669bf9..527d339 100644 --- a/src/core/MiddlewareManager.ts +++ b/src/core/MiddlewareManager.ts @@ -1,36 +1,44 @@ import { assert } from '../helpers/error'; import { combineURL } from '../helpers/combineURL'; -import { isFunction } from '../helpers/isTypes'; -import { AxiosRequestConfig, AxiosResponse } from './Axios'; - -export interface MiddlewareContext { - req: AxiosRequestConfig; - res: null | AxiosResponse; -} +import { isFunction, isString } from '../helpers/isTypes'; export interface MiddlewareNext { (): Promise; } -export interface MiddlewareCallback { - (ctx: MiddlewareContext, next: MiddlewareNext): Promise; +export interface MiddlewareCallback { + (ctx: Conext, next: MiddlewareNext): Promise; } -export interface MiddlewareFlush { - (ctx: MiddlewareContext): Promise; +export interface MiddlewareFlush { + (ctx: Conext): Promise; } -export default class MiddlewareManager { - #map = new Map(); +export default class MiddlewareManager { + #map = new Map[]>(); - use(callback: MiddlewareCallback): MiddlewareManager; - use(path: string, callback: MiddlewareCallback): MiddlewareManager; - use(path: string | MiddlewareCallback, callback?: MiddlewareCallback) { + flush: MiddlewareFlush; + + constructor(flush: MiddlewareFlush) { + this.flush = this.wrap(flush); + } + + use(callback: MiddlewareCallback): MiddlewareManager; + use( + path: string, + callback: MiddlewareCallback, + ): MiddlewareManager; + use( + path: string | MiddlewareCallback, + callback?: MiddlewareCallback, + ) { if (isFunction(path)) { callback = path; path = '/'; } - assert(!!path, 'path 不是一个非空的 string'); + assert(isString(path), 'path 不是一个 string'); + assert(!!path, 'path 不是一个长度大于零的 string'); + assert(isFunction(callback), 'callback 不是一个 function'); const middlewares = this.#map.get(path) ?? []; middlewares.push(callback!); @@ -39,32 +47,25 @@ export default class MiddlewareManager { return this; } - wrap(flush: MiddlewareFlush): MiddlewareFlush { - return (ctx) => this.#performer(ctx, flush); - } + wrap(flush: MiddlewareFlush): MiddlewareFlush { + return (ctx) => { + const allMiddlewares: MiddlewareCallback[] = []; - #performer(ctx: MiddlewareContext, flush: MiddlewareFlush) { - const middlewares = [...this.#getAllMiddlewares(ctx), flush]; + for (const [path, middlewares] of this.#map.entries()) { + const url = combineURL(ctx.req.baseURL, path); + const checkRE = new RegExp(`^${url}([/?].*)?`); - function next(): Promise { - return middlewares.shift()!(ctx, next); - } - - return next(); - } - - #getAllMiddlewares(ctx: MiddlewareContext) { - const allMiddlewares: MiddlewareCallback[] = []; - - for (const [path, middlewares] of this.#map.entries()) { - const url = combineURL(ctx.req.baseURL, path); - - const checkRE = new RegExp(`^${url}([/?].*)?`); - if (checkRE.test(ctx.req.url!)) { - allMiddlewares.push(...middlewares); + if (path === '/') { + allMiddlewares.push(...middlewares); + } else if (checkRE.test(ctx.req.url!)) { + allMiddlewares.push(...middlewares); + } } - } - return allMiddlewares; + const tasks = [...allMiddlewares, flush]; + return (function next(): Promise { + return tasks.shift()!(ctx, next); + })(); + }; } } diff --git a/src/core/createInstance.ts b/src/core/createInstance.ts new file mode 100644 index 0000000..839f351 --- /dev/null +++ b/src/core/createInstance.ts @@ -0,0 +1,69 @@ +import { combineURL } from '../helpers/combineURL'; +import { buildURL } from '../helpers/buildURL'; +import Axios, { + AxiosRequest, + AxiosRequestConfig, + AxiosRequestHeaders, +} from './Axios'; +import { mergeConfig } from './mergeConfig'; + +/** + * axios 实例默认配置 + */ +export interface AxiosInstanceDefaults extends AxiosRequestConfig { + /** + * 请求头 + */ + headers: Required; +} + +/** + * axios 实例 + */ +export interface AxiosInstance extends AxiosRequest, Axios { + /** + * 默认请求配置 + */ + defaults: AxiosInstanceDefaults; + /** + * 获取 URI + * + * @param config 默认配置 + */ + getUri(config: AxiosRequestConfig): string; + /** + * 派生领域 + * + * @param config 默认配置 + */ + fork(config: AxiosRequestConfig): AxiosInstance; + /** + * 扩展实例 + * + * @param config 默认配置 + */ + extend(config: AxiosRequestConfig): AxiosInstance; +} + +export function createInstance(config: AxiosRequestConfig, parent?: Axios) { + const context = new Axios(config, parent); + const instance = context.request as AxiosInstance; + + instance.getUri = function getUri(config: AxiosRequestConfig) { + const { url, params, paramsSerializer } = mergeConfig( + instance.defaults, + config, + ); + return buildURL(url, params, paramsSerializer).replace(/^\?/, ''); + }; + instance.extend = function extend(config: AxiosRequestConfig = {}) { + config.url = combineURL(instance.defaults.baseURL, config.url); + return createInstance(mergeConfig(instance.defaults, config), context); + }; + instance.fork = instance.extend; + + Object.assign(instance, context); + Object.setPrototypeOf(instance, Axios.prototype); + + return instance; +} diff --git a/src/defaults.ts b/src/defaults.ts index 804de0a..63aa66c 100644 --- a/src/defaults.ts +++ b/src/defaults.ts @@ -1,5 +1,5 @@ import { getDefaultAdapter } from './adpater/getDefaultAdapter'; -import { AxiosInstanceDefaults } from './axios'; +import { AxiosInstanceDefaults } from './core/createInstance'; const defaults: AxiosInstanceDefaults = { // 适配器,在支持的平台中有值。 diff --git a/src/index.ts b/src/index.ts index e58ab62..3331e82 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,10 +15,13 @@ export type { AxiosUploadProgressCallback, } from './core/Axios'; export type { - MiddlewareContext, MiddlewareCallback, MiddlewareNext, } from './core/MiddlewareManager'; +export type { + AxiosInstanceDefaults, + AxiosInstance, +} from './core/createInstance'; export type { AxiosAdapter, AxiosAdapterRequestConfig, @@ -35,11 +38,7 @@ export type { AxiosAdapterPlatform, AxiosAdapterPlatformTask, } from './adpater/createAdapter'; -export type { - AxiosInstanceDefaults, - AxiosInstance, - AxiosStatic, -} from './axios'; +export type { AxiosStatic } from './axios'; export { CancelToken, isCancel } from './request/cancel'; export { isAxiosError } from './request/createError'; diff --git a/test/axios.instance.test.ts b/test/axios.instance.test.ts index 9a1c6c4..0badb6b 100644 --- a/test/axios.instance.test.ts +++ b/test/axios.instance.test.ts @@ -1,11 +1,10 @@ -import { describe, test, expect, vi, afterEach } from 'vitest'; +import { describe, test, expect, vi } from 'vitest'; import { mockAdapter, testEachMethods } from 'scripts/test.utils'; import { PLAIN_METHODS, WITH_DATA_METHODS, WITH_PARAMS_METHODS, } from '@/constants/methods'; -import AxiosDomain from '@/core/AxiosDomain'; import defaults from '@/defaults'; import axios from '@/axios'; @@ -163,8 +162,4 @@ describe('src/axios.ts', () => { 'test?id=1', ); }); - - test('派生的领域应该为 AxiosDomain 的实例', () => { - expect(axios.fork() instanceof AxiosDomain).toBeTruthy(); - }); }); diff --git a/test/core/Axios.test.ts b/test/core/Axios.test.ts index f39bfbd..852870a 100644 --- a/test/core/Axios.test.ts +++ b/test/core/Axios.test.ts @@ -10,7 +10,6 @@ import { WITH_DATA_METHODS, WITH_PARAMS_METHODS, } from '@/constants/methods'; -import AxiosDomain from '@/core/AxiosDomain'; import Axios from '@/core/Axios'; import axios from '@/axios'; @@ -22,10 +21,6 @@ describe('src/core/Axios.ts', () => { baseURL: 'http://api.com', }); - test('应该继承自 AxiosDomain', () => { - expect(new Axios() instanceof AxiosDomain).toBeTruthy(); - }); - test('应该有这些实例属性及方法', () => { const c = { baseURL: 'http://api.com', @@ -34,8 +29,6 @@ describe('src/core/Axios.ts', () => { expect(axiosObj.defaults).toEqual(c); expect(axiosObj.interceptors).toBeTypeOf('object'); expect(axiosObj.request).toBeTypeOf('function'); - expect(axiosObj.getUri).toBeTypeOf('function'); - expect(axiosObj.fork).toBeTypeOf('function'); }); testEachMethods('%s 应该是一个函数', (k) => { @@ -354,45 +347,4 @@ describe('src/core/Axios.ts', () => { expect(res2).not.toBeCalled(); expect(rej2).toBeCalled(); }); - - test('应该可以获取 URI', () => { - expect(axiosObj.getUri({ url: 'test' })).toBe('test'); - expect(axiosObj.getUri({ url: 'test', params: { id: 1 } })).toBe( - 'test?id=1', - ); - expect( - axiosObj.getUri({ url: 'test', paramsSerializer: () => 'id=1' }), - ).toBe('test?id=1'); - }); - - test('派生的领域应该为 AxiosDomain 的实例', () => { - expect(axiosObj.fork() instanceof AxiosDomain).toBeTruthy(); - }); - - test('应该支持 绝对路径/相对路径 派生领域', () => { - const a1 = axiosObj.fork({ baseURL: 'test' }); - const a2 = new Axios().fork({ baseURL: 'test' }); - const a3 = axiosObj.fork({ baseURL: 'https://api.com' }); - const a4 = axiosObj.fork(); - - expect(a1.defaults.baseURL).toBe('http://api.com/test'); - expect(a2.defaults.baseURL).toBe('/test'); - expect(a3.defaults.baseURL).toBe('https://api.com'); - expect(a4.defaults.baseURL).toBe('http://api.com'); - }); - - test('派生自当前实例的领域应该可以复用当前实例的拦截器', async () => { - const axiosObj = new Axios(); - const req = vi.fn((v) => v); - const res = vi.fn((v) => v); - - axiosObj.interceptors.request.use(req); - axiosObj.interceptors.response.use(res); - - const a = axiosObj.fork({ baseURL: 'test' }); - await a.request('test', { adapter: mockAdapter() }); - - expect(req).toBeCalled(); - expect(res).toBeCalled(); - }); }); diff --git a/test/core/AxiosDomain.test.ts b/test/core/AxiosDomain.test.ts deleted file mode 100644 index d37d20d..0000000 --- a/test/core/AxiosDomain.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { describe, test, expect, vi } from 'vitest'; -import { ignore } from '@/helpers/ignore'; -import { - PLAIN_METHODS, - WITH_DATA_METHODS, - WITH_PARAMS_METHODS, -} from '@/constants/methods'; -import AxiosDomain from '@/core/AxiosDomain'; -import { AxiosResponse } from '@/core/Axios'; -import { eachMethods } from 'scripts/test.utils'; - -describe('src/core/AxiosDomain.ts', () => { - test('应该有这些实例属性', () => { - const c = { - baseURL: 'http://api.com', - }; - const a = new AxiosDomain(c, vi.fn()); - - expect(a.defaults).toEqual(c); - expect(a.request).toBeTypeOf('function'); - - eachMethods((k) => { - expect(a[k]).toBeTypeOf('function'); - }); - }); - - test('发送请求时 processRequest 应该被调用', () => { - const p = vi.fn(); - const d = { - baseURL: 'http://api.com', - }; - const c = { - url: 'test', - params: { - id: 1, - }, - data: { - id: 1, - }, - }; - - new AxiosDomain(d, p).request(c); - - expect(p).toBeCalled(); - expect(p.mock.calls[0][0]).toEqual({ - ...d, - ...c, - }); - }); - - test('请求方法应该支持空参数', () => { - const cb = vi.fn(); - const a = new AxiosDomain({}, cb); - - a.request('test'); - - PLAIN_METHODS.forEach((k) => a[k]('test')); - WITH_PARAMS_METHODS.forEach((k) => a[k]('test')); - WITH_DATA_METHODS.forEach((k) => a[k]('test')); - - const l = - PLAIN_METHODS.length + - WITH_PARAMS_METHODS.length + - WITH_DATA_METHODS.length + - 1; - expect(cb.mock.calls.length).toBe(l); - cb.mock.calls.forEach(([config]) => expect(config.url).toBe('test')); - }); - - test('应该可以调用请求方法', () => { - const cb = vi.fn(); - const d = { - baseURL: 'http://api.com', - }; - const u = 'test'; - const c = { - params: { - id: 1, - }, - data: { - id: 1, - }, - }; - const a = new AxiosDomain(d, async (config) => { - cb(); - - expect(config.baseURL).toBe(d.baseURL); - expect(config.url).toBe(u); - expect(config.params).toEqual(c.params); - expect(config.data).toEqual(c.data); - - return {} as AxiosResponse; - }); - - a.request(u, c); - - PLAIN_METHODS.forEach((k) => a[k](u, c)); - WITH_PARAMS_METHODS.forEach((k) => a[k](u, c.params, ignore(c, 'params'))); - WITH_DATA_METHODS.forEach((k) => a[k](u, c.data, ignore(c, 'data'))); - - const l = - PLAIN_METHODS.length + - WITH_PARAMS_METHODS.length + - WITH_DATA_METHODS.length + - 1; - expect(cb.mock.calls.length).toBe(l); - }); - - test('应该可以直接传递 config 调用请求方法', () => { - const cb = vi.fn(); - const d = { - baseURL: 'http://api.com', - }; - const c = { - url: 'test', - params: { - id: 1, - }, - data: { - id: 1, - }, - }; - const a = new AxiosDomain(d, async (config) => { - cb(); - - expect(config.baseURL).toBe(d.baseURL); - expect(config.url).toBe(c.url); - expect(config.params).toEqual(c.params); - expect(config.data).toEqual(c.data); - - return {} as AxiosResponse; - }); - - a.request(c); - - PLAIN_METHODS.forEach((k) => a[k](c.url, ignore(c, 'url'))); - WITH_PARAMS_METHODS.forEach((k) => - a[k](c.url, c.params, ignore(c, 'url', 'params')), - ); - WITH_DATA_METHODS.forEach((k) => - a[k](c.url, c.data, ignore(c, 'url', 'data')), - ); - - const l = - PLAIN_METHODS.length + - WITH_PARAMS_METHODS.length + - WITH_DATA_METHODS.length + - 1; - expect(cb.mock.calls.length).toBe(l); - }); - - test('应该支持深度合并 params', () => { - const d = { - baseURL: 'http://api.com', - }; - const p = { - v1: 1, - v2: { - v1: 1, - }, - }; - const c = { - params: { - v2: { - v2: 2, - }, - v3: 3, - }, - }; - - const a = new AxiosDomain(d, async (config) => { - expect(config.params).toEqual({ - v1: 1, - v2: { - v1: 1, - v2: 2, - }, - v3: 3, - }); - - return {} as AxiosResponse; - }); - - WITH_PARAMS_METHODS.forEach((k) => a[k]('test', p, c)); - }); - - test('应该只取传入的 data', () => { - const ds = { - baseURL: 'http://api.com', - }; - const d = { - v: 1, - }; - const c = { - v: 2, - }; - - const a = new AxiosDomain(ds, async (config) => { - expect(config.data).toEqual({ - v: 1, - }); - - return {} as AxiosResponse; - }); - - WITH_DATA_METHODS.forEach((k) => a[k]('test', d, c)); - }); - - test('应该支持多种类型 data', () => { - const ds = { - baseURL: 'http://api.com', - }; - - const str = '11'; - const obj = {}; - const buff = new ArrayBuffer(0); - - const testStr = new AxiosDomain(ds, async (config) => { - expect(config.data).toBe(str); - - return {} as AxiosResponse; - }); - const testObj = new AxiosDomain(ds, async (config) => { - expect(config.data).toBe(obj); - - return {} as AxiosResponse; - }); - const testBuff = new AxiosDomain(ds, async (config) => { - expect(config.data).toBe(buff); - - return {} as AxiosResponse; - }); - - WITH_DATA_METHODS.forEach((k) => { - testStr[k]('test', str); - testObj[k]('test', obj); - testBuff[k]('test', buff); - }); - }); -}); diff --git a/test/core/MiddlewareManager.test.ts b/test/core/MiddlewareManager.test.ts index 3d80831..f615531 100644 --- a/test/core/MiddlewareManager.test.ts +++ b/test/core/MiddlewareManager.test.ts @@ -3,14 +3,18 @@ import MiddlewareManager from '@/core/MiddlewareManager'; describe('src/core/MiddlewareManager.ts', () => { test('应该有这些实例属性', () => { - const m = new MiddlewareManager(); + const m = new MiddlewareManager(vi.fn()); expect(m.use).toBeTypeOf('function'); expect(m.wrap).toBeTypeOf('function'); }); test('应该可以添加中间件回调', async () => { - const m = new MiddlewareManager(); + const flush = vi.fn(async (ctx) => { + expect(ctx.req.url).toBe('test'); + ctx.res = res; + }); + const m = new MiddlewareManager(flush); const ctx = { req: { url: 'https://api.com' }, res: null, @@ -24,20 +28,20 @@ describe('src/core/MiddlewareManager.ts', () => { await next(); expect(ctx.res).toBe(res); }); - const flush = vi.fn(async (ctx) => { - expect(ctx.req.url).toBe('test'); - ctx.res = res; - }); m.use(midde); - await m.wrap(flush)(ctx); + await m.flush(ctx); expect(ctx.res).toBe(res); expect(midde).toBeCalled(); }); test('应该可以给路径添加中间件回调', async () => { - const m = new MiddlewareManager(); + const flush = vi.fn(async (ctx) => { + ctx.res = res; + }); + + const m = new MiddlewareManager(flush); const ctx1 = { req: { baseURL: 'https://api.com', @@ -60,18 +64,15 @@ describe('src/core/MiddlewareManager.ts', () => { await next(); expect(ctx.res).toBe(res); }); - const flush = vi.fn(async (ctx) => { - ctx.res = res; - }); m.use('/test', midde); - await m.wrap(flush)(ctx1); + await m.flush(ctx1); expect(ctx1.res).toBe(res); expect(midde).not.toBeCalled(); m.use('/test', midde); - await m.wrap(flush)(ctx2); + await m.flush(ctx2); expect(midde).toBeCalled(); });