test: 测试 generateType

pull/41/head
zjx0905 2023-04-02 23:27:45 +08:00
parent 0cfb3e1ff0
commit ce12a83873
8 changed files with 215 additions and 74 deletions

View File

@ -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);
}

View File

@ -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<AxiosResponse>(),
};
public options!: <TData = unknown>(
url: string,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public get!: <TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public head!: <TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public post!: <TData = unknown>(
url: string,
data?: AnyObject | AxiosRequestFormData,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public put!: <TData = unknown>(
url: string,
data?: AnyObject | AxiosRequestFormData,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public delete!: <TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public trace!: <TData = unknown>(
url: string,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
public connect!: <TData = unknown>(
url: string,
config?: AxiosRequestConfig,
) => Promise<AxiosResponse<TData>>;
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<AxiosResponse<TData>>;
}
public options<TData = unknown>(
url: string,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('options', url, undefined, config);
}
public get<TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('get', url, params, config);
}
public head<TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('head', url, params, config);
}
public post<TData = unknown>(
url: string,
data?: AnyObject | AxiosRequestFormData,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._reqWithData<TData>('post', url, data, config);
}
public put<TData = unknown>(
url: string,
data?: AnyObject | AxiosRequestFormData,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._reqWithData<TData>('put', url, data, config);
}
public delete<TData = unknown>(
url: string,
params?: AnyObject,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('delete', url, params, config);
}
public trace<TData = unknown>(
url: string,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('trace', url, undefined, config);
}
public connect<TData = unknown>(
url: string,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<TData>> {
return this._req<TData>('connect', url, undefined, config);
}
private _req<TData = unknown>(
method: AxiosRequestMethod,
url: string,

View File

@ -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();
}
}

View File

@ -52,7 +52,7 @@ export function request<TData = unknown>(
fail,
};
const adapterTask = config.adapter?.(adapterConfig) as
const adapterTask = config.adapter!(adapterConfig) as
| AxiosAdapterTask
| undefined;
@ -83,8 +83,9 @@ export function request<TData = unknown>(
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);

View File

@ -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, '');
}

42
test/axios.test.ts Normal file
View File

@ -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 });
});
});
});

View File

@ -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();

View File

@ -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,
});
});
});