parent
ce12a83873
commit
4409a5720b
|
@ -13,6 +13,8 @@ axios.defaults.adapter = function adapter(adapterConfig) {
|
|||
url,
|
||||
// 请求方法
|
||||
method,
|
||||
// 请求参数
|
||||
params,
|
||||
// 请求数据
|
||||
data,
|
||||
// 请求头 同 headers
|
||||
|
@ -53,7 +55,7 @@ axios.defaults.adapter = function adapter(adapterConfig) {
|
|||
return wx.downloadFile({
|
||||
url,
|
||||
method,
|
||||
filePath: data.filePath,
|
||||
filePath: params.filePath,
|
||||
header: headers,
|
||||
success,
|
||||
fail,
|
||||
|
|
|
@ -1,12 +1 @@
|
|||
declare const uni: unknown;
|
||||
declare const wx: unknown;
|
||||
declare const my: unknown;
|
||||
declare const swan: unknown;
|
||||
declare const tt: unknown;
|
||||
declare const qq: unknown;
|
||||
declare const qh: unknown;
|
||||
declare const ks: unknown;
|
||||
declare const dd: unknown;
|
||||
declare const jd: unknown;
|
||||
|
||||
type AnyObject<T = any> = Record<string, T>;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
declare const uni: unknown;
|
||||
declare const wx: unknown;
|
||||
declare const my: unknown;
|
||||
declare const swan: unknown;
|
||||
declare const tt: unknown;
|
||||
declare const qq: unknown;
|
||||
declare const qh: unknown;
|
||||
declare const ks: unknown;
|
||||
declare const dd: unknown;
|
||||
declare const jd: unknown;
|
|
@ -23,7 +23,7 @@ function main() {
|
|||
function buildConfig(format) {
|
||||
const isDts = format === 'dts';
|
||||
const output = {
|
||||
file: resolvePath(format, isDts),
|
||||
file: resolveOutput(format, isDts),
|
||||
format: isDts ? 'es' : format,
|
||||
name: pkg.name,
|
||||
exports: 'default',
|
||||
|
@ -51,7 +51,7 @@ function buildConfig(format) {
|
|||
};
|
||||
}
|
||||
|
||||
function resolvePath(format, isDts) {
|
||||
function resolveOutput(format, isDts) {
|
||||
return path.resolve(
|
||||
distPath,
|
||||
`${pkg.name}${isDts ? '.d.ts' : `.${format}.js`}`,
|
||||
|
|
|
@ -8,7 +8,7 @@ export function asyncTimeout(delay = 0) {
|
|||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
export function captureError<T = any>(fn: () => void): T {
|
||||
export function captureError<T = any>(fn: () => void) {
|
||||
try {
|
||||
fn();
|
||||
throw new Error('without Error');
|
||||
|
@ -17,7 +17,7 @@ export function captureError<T = any>(fn: () => void): T {
|
|||
}
|
||||
}
|
||||
|
||||
export function cleanedStack(error: Error) {
|
||||
export function checkStack(error: Error) {
|
||||
if (error.stack) {
|
||||
return error.stack.indexOf('at') === error.stack.indexOf('at /');
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ export function noop() {
|
|||
return;
|
||||
}
|
||||
|
||||
export function mockResponse(
|
||||
export function mockResponseBase(
|
||||
status: number,
|
||||
statusText: string,
|
||||
headers: AnyObject,
|
||||
|
@ -42,12 +42,15 @@ export function mockResponse(
|
|||
};
|
||||
}
|
||||
|
||||
export function mockSuccess(headers: AnyObject = {}, data: AnyObject = {}) {
|
||||
return mockResponse(200, 'OK', headers, data);
|
||||
export function mockResponse(headers: AnyObject = {}, data: AnyObject = {}) {
|
||||
return mockResponseBase(200, 'OK', headers, data);
|
||||
}
|
||||
|
||||
export function mockFail(headers: AnyObject = {}, data: AnyObject = {}) {
|
||||
return mockResponse(400, 'FAIL', headers, data);
|
||||
export function mockResponseError(
|
||||
headers: AnyObject = {},
|
||||
data: AnyObject = {},
|
||||
) {
|
||||
return mockResponseBase(400, 'FAIL', headers, data);
|
||||
}
|
||||
|
||||
export interface MockAdapterOptions {
|
||||
|
@ -58,8 +61,8 @@ export interface MockAdapterOptions {
|
|||
after?: () => void;
|
||||
}
|
||||
|
||||
export function mockAdapter(
|
||||
type: 'success' | 'fail',
|
||||
export function mockAdapterBase(
|
||||
type: 'success' | 'error' | 'fail' = 'success',
|
||||
options: MockAdapterOptions = {},
|
||||
) {
|
||||
const { headers = {}, data = {}, delay = 0, before, after } = options;
|
||||
|
@ -67,20 +70,30 @@ export function mockAdapter(
|
|||
return (config: AxiosAdapterRequestConfig) => {
|
||||
before?.(config);
|
||||
setTimeout(() => {
|
||||
if (type === 'success') {
|
||||
config.success(mockSuccess(headers, data));
|
||||
} else {
|
||||
config.fail(mockFail(headers, data));
|
||||
switch (type) {
|
||||
case 'success':
|
||||
config.success(mockResponse(headers, data));
|
||||
break;
|
||||
case 'error':
|
||||
config.success(mockResponseError(headers, data));
|
||||
break;
|
||||
case 'fail':
|
||||
config.fail(mockResponseError(headers));
|
||||
break;
|
||||
}
|
||||
after?.();
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
|
||||
export function mockAdapterSuccess(options: MockAdapterOptions = {}) {
|
||||
return mockAdapter('success', options);
|
||||
export function mockAdapter(options: MockAdapterOptions = {}) {
|
||||
return mockAdapterBase('success', options);
|
||||
}
|
||||
|
||||
export function mockAdapterError(options: MockAdapterOptions = {}) {
|
||||
return mockAdapterBase('error', options);
|
||||
}
|
||||
|
||||
export function mockAdapterFail(options: MockAdapterOptions = {}) {
|
||||
return mockAdapter('fail', options);
|
||||
return mockAdapterBase('fail', options);
|
||||
}
|
||||
|
|
|
@ -198,20 +198,34 @@ export default class Axios {
|
|||
this.defaults = defaults;
|
||||
|
||||
for (const alias of Axios.as) {
|
||||
this[alias] = (url, config) => {
|
||||
return this._req(alias, url, undefined, config);
|
||||
this[alias] = (url, config = {}) => {
|
||||
return this.request({
|
||||
...config,
|
||||
method: alias,
|
||||
url,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
for (const alias of Axios.pas) {
|
||||
this[alias] = (url, params, config) => {
|
||||
return this._req(alias, url, params, config);
|
||||
this[alias] = (url, params, config = {}) => {
|
||||
return this.request({
|
||||
...config,
|
||||
method: alias,
|
||||
params,
|
||||
url,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
for (const alias of Axios.das) {
|
||||
this[alias] = (url, data, config) => {
|
||||
return this._reqWithData(alias, url, data, config);
|
||||
return this.request({
|
||||
...config,
|
||||
method: alias,
|
||||
data,
|
||||
url,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -249,32 +263,4 @@ export default class Axios {
|
|||
|
||||
return promiseResponse as Promise<AxiosResponse<TData>>;
|
||||
}
|
||||
|
||||
private _req<TData = unknown>(
|
||||
method: AxiosRequestMethod,
|
||||
url: string,
|
||||
params?: AnyObject,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this.request<TData>({
|
||||
...(config ?? {}),
|
||||
method,
|
||||
url,
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
private _reqWithData<TData = unknown>(
|
||||
method: AxiosRequestMethod,
|
||||
url: string,
|
||||
data?: AnyObject | AxiosRequestFormData,
|
||||
config?: AxiosRequestConfig,
|
||||
): Promise<AxiosResponse<TData>> {
|
||||
return this.request<TData>({
|
||||
...(config ?? {}),
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,32 +27,28 @@ export default function dispatchRequest<TData = unknown>(
|
|||
config.transformRequest,
|
||||
);
|
||||
|
||||
return request<TData>(config).then(
|
||||
(response: AxiosResponse<TData>) => {
|
||||
function transformer(response: AxiosResponse<TData>) {
|
||||
response.data = transformData(
|
||||
response.data as AnyObject,
|
||||
response.headers,
|
||||
config.transformResponse,
|
||||
) as TData;
|
||||
}
|
||||
|
||||
return request<TData>(config)
|
||||
.then((response: AxiosResponse<TData>) => {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
response.data = transformData(
|
||||
response.data as AnyObject,
|
||||
response.headers,
|
||||
config.transformResponse,
|
||||
) as TData;
|
||||
|
||||
transformer(response);
|
||||
return response;
|
||||
},
|
||||
(reason: unknown) => {
|
||||
})
|
||||
.catch((reason: unknown) => {
|
||||
if (!isCancel(reason)) {
|
||||
throwIfCancellationRequested(config);
|
||||
|
||||
if (isPlainObject(reason) && isPlainObject(reason.response)) {
|
||||
reason.response.data = transformData(
|
||||
reason.response.data,
|
||||
reason.response.headers,
|
||||
config.transformResponse,
|
||||
);
|
||||
const response = (reason as AnyObject)?.response;
|
||||
if (isPlainObject(response)) {
|
||||
transformer(response as AxiosResponse<TData>);
|
||||
}
|
||||
}
|
||||
|
||||
throw config.errorHandler?.(reason) ?? reason;
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,35 +2,17 @@ import { isUndefined, isPlainObject } from '../helpers/isTypes';
|
|||
import { deepMerge } from '../helpers/deepMerge';
|
||||
import { AxiosRequestConfig } from './Axios';
|
||||
|
||||
type AxiosRequestConfigKey = keyof AxiosRequestConfig;
|
||||
|
||||
const onlyFromConfig2Keys: AxiosRequestConfigKey[] = [
|
||||
'url',
|
||||
'method',
|
||||
'data',
|
||||
'upload',
|
||||
'download',
|
||||
];
|
||||
const priorityFromConfig2Keys: AxiosRequestConfigKey[] = [
|
||||
'adapter',
|
||||
'baseURL',
|
||||
'paramsSerializer',
|
||||
'transformRequest',
|
||||
'transformResponse',
|
||||
'errorHandler',
|
||||
'cancelToken',
|
||||
'dataType',
|
||||
'responseType',
|
||||
'timeout',
|
||||
'enableHttp2',
|
||||
'enableQuic',
|
||||
'enableCache',
|
||||
'sslVerify',
|
||||
'validateStatus',
|
||||
'onUploadProgress',
|
||||
'onDownloadProgress',
|
||||
];
|
||||
const deepMergeConfigKeys: AxiosRequestConfigKey[] = ['headers', 'params'];
|
||||
const fromConfig2Map: Record<string, boolean> = {
|
||||
url: true,
|
||||
method: true,
|
||||
data: true,
|
||||
upload: true,
|
||||
download: true,
|
||||
};
|
||||
const deepMergeConfigMap: Record<string, boolean> = {
|
||||
headers: true,
|
||||
params: true,
|
||||
};
|
||||
|
||||
export function mergeConfig(
|
||||
config1: AxiosRequestConfig = {},
|
||||
|
@ -38,33 +20,36 @@ export function mergeConfig(
|
|||
): AxiosRequestConfig {
|
||||
const config: AxiosRequestConfig = {};
|
||||
|
||||
for (const key of onlyFromConfig2Keys) {
|
||||
const value = config2[key];
|
||||
// 所有已知键名
|
||||
const keysSet = Array.from(
|
||||
new Set([...Object.keys(config1), ...Object.keys(config2)]),
|
||||
);
|
||||
|
||||
if (!isUndefined(value)) {
|
||||
config[key] = value as any;
|
||||
for (const key of keysSet) {
|
||||
const val1 = config1[key] as any;
|
||||
const val2 = config2[key] as any;
|
||||
|
||||
// 只从 config2 中取值
|
||||
if (fromConfig2Map[key]) {
|
||||
if (!isUndefined(val2)) config[key] = val2;
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of priorityFromConfig2Keys) {
|
||||
const value1 = config1[key];
|
||||
const value2 = config2[key];
|
||||
|
||||
if (!isUndefined(value2)) {
|
||||
config[key] = value2 as any;
|
||||
} else if (!isUndefined(value1)) {
|
||||
config[key] = value1 as any;
|
||||
// 深度合并 config1 和 config2 中的对象
|
||||
else if (deepMergeConfigMap[key]) {
|
||||
if (isPlainObject(val1) && isPlainObject(val2)) {
|
||||
config[key] = deepMerge(val1, val2);
|
||||
} else if (isPlainObject(val1)) {
|
||||
config[key] = deepMerge(val1);
|
||||
} else if (isPlainObject(val2)) {
|
||||
config[key] = deepMerge(val2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of deepMergeConfigKeys) {
|
||||
const value1 = config1[key];
|
||||
const value2 = config2[key];
|
||||
|
||||
if (isPlainObject(value2)) {
|
||||
config[key] = deepMerge(value1 ?? {}, value2) as any;
|
||||
} else if (isPlainObject(value1)) {
|
||||
config[key] = deepMerge(value1) as any;
|
||||
// 优先从 config2 中取值,如果没有值就从 config1 中取值
|
||||
else {
|
||||
if (!isUndefined(val2)) {
|
||||
config[key] = val2;
|
||||
} else if (!isUndefined(val1)) {
|
||||
config[key] = val1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isFunction, isPlainObject } from '../helpers/isTypes';
|
||||
import { isFunction, isPlainObject, isString } from '../helpers/isTypes';
|
||||
import { assert } from '../helpers/error';
|
||||
import {
|
||||
AxiosAdapterRequestConfig,
|
||||
|
@ -42,10 +42,12 @@ export function request<TData = unknown>(
|
|||
): Promise<AxiosResponse<TData>> {
|
||||
return new Promise((resolve, reject) => {
|
||||
assert(isFunction(config.adapter), 'adapter 不是一个 function');
|
||||
assert(isString(config.url), 'url 不是一个 string');
|
||||
assert(isString(config.method), 'method 不是一个 string');
|
||||
|
||||
const adapterConfig: AxiosAdapterRequestConfig = {
|
||||
...config,
|
||||
url: config.url ?? '',
|
||||
url: config.url,
|
||||
type: generateType(config),
|
||||
method: config.method!.toUpperCase() as AxiosAdapterRequestMethod,
|
||||
success,
|
||||
|
|
|
@ -11,12 +11,13 @@ export function throwError(msg: string): void {
|
|||
}
|
||||
|
||||
export function cleanStack(error: Error) {
|
||||
if (error.stack) {
|
||||
const start = error.stack.indexOf('at');
|
||||
const end = error.stack.search(/at ([\w-_.]+:)?\//i);
|
||||
const { stack } = error;
|
||||
if (stack) {
|
||||
const start = stack.indexOf('at');
|
||||
const end = stack.search(/at ([\w-_.]+:)?\//i);
|
||||
if (start < end) {
|
||||
const removed = error.stack.slice(start, end);
|
||||
error.stack = error.stack.replace(removed, '');
|
||||
const removed = stack.slice(start, end);
|
||||
error.stack = stack.replace(removed, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,42 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import axios from 'src/axios';
|
||||
import { mockAdapterFail, mockAdapterSuccess } from 'scripts/test.utils';
|
||||
import { mockAdapter, mockAdapterError } from 'scripts/test.utils';
|
||||
|
||||
describe('src/axios.ts', () => {
|
||||
test('应该处理成功和失败', () => {
|
||||
axios({
|
||||
adapter: mockAdapterSuccess({
|
||||
adapter: mockAdapter({
|
||||
headers: { type: 'json' },
|
||||
data: { v1: 1 },
|
||||
before: (config) => {
|
||||
expect(config.url).toBe('http://api.com/user/1?id=1');
|
||||
expect(config.url).toBe('http://api.com/test/1?id=1');
|
||||
},
|
||||
}),
|
||||
baseURL: 'http://api.com',
|
||||
url: 'user/:id',
|
||||
url: 'test/:id',
|
||||
params: {
|
||||
id: 1,
|
||||
},
|
||||
}).then((response) => {
|
||||
expect(response.headers).toEqual({ type: 'json' });
|
||||
expect(response.data).toEqual({ v1: 1 });
|
||||
}).then((res) => {
|
||||
expect(res.headers).toEqual({ type: 'json' });
|
||||
expect(res.data).toEqual({ v1: 1 });
|
||||
});
|
||||
|
||||
axios('user/:id', {
|
||||
adapter: mockAdapterFail({
|
||||
axios('test/:id', {
|
||||
adapter: mockAdapterError({
|
||||
headers: { type: 'json' },
|
||||
data: { v1: 1 },
|
||||
before: (config) => {
|
||||
expect(config.url).toBe('http://api.com/user/1');
|
||||
expect(config.url).toBe('http://api.com/test/1');
|
||||
},
|
||||
}),
|
||||
baseURL: 'http://api.com',
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
}).catch((error) => {
|
||||
expect(error.response.headers).toEqual({ type: 'json' });
|
||||
expect(error.response.data).toEqual({ v1: 1 });
|
||||
}).catch((err) => {
|
||||
expect(err.response.headers).toEqual({ type: 'json' });
|
||||
expect(err.response.data).toEqual({ v1: 1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import { describe, test, expect, vi } from 'vitest';
|
|||
import {
|
||||
asyncNext,
|
||||
captureError,
|
||||
mockAdapterSuccess,
|
||||
mockAdapter,
|
||||
noop,
|
||||
asyncTimeout,
|
||||
} from 'scripts/test.utils';
|
||||
|
@ -16,17 +16,17 @@ import {
|
|||
|
||||
describe('src/helpers/cancel.ts', () => {
|
||||
test('应该支持空参数', () => {
|
||||
const cancel = new Cancel();
|
||||
const c = new Cancel();
|
||||
|
||||
expect(cancel.message).toBeUndefined();
|
||||
expect(cancel.toString()).toBe('Cancel');
|
||||
expect(c.message).toBeUndefined();
|
||||
expect(c.toString()).toBe('Cancel');
|
||||
});
|
||||
|
||||
test('传入参数时应该有正确的返回结果', () => {
|
||||
const cancel = new Cancel('error');
|
||||
const c = new Cancel('error');
|
||||
|
||||
expect(cancel.message).toBe('error');
|
||||
expect(cancel.toString()).toBe('Cancel: error');
|
||||
expect(c.message).toBe('error');
|
||||
expect(c.toString()).toBe('Cancel: error');
|
||||
});
|
||||
|
||||
test('应该正确判断 Cancel', () => {
|
||||
|
@ -36,44 +36,47 @@ describe('src/helpers/cancel.ts', () => {
|
|||
});
|
||||
|
||||
test('应该可以取消', () => {
|
||||
let cancelAction!: () => void;
|
||||
const cancelToken = new CancelToken((action) => {
|
||||
cancelAction = action;
|
||||
});
|
||||
let ca!: () => void;
|
||||
const ct = new CancelToken((a) => (ca = a));
|
||||
|
||||
expect(cancelToken.throwIfRequested()).toBeUndefined();
|
||||
cancelAction();
|
||||
expect(() => cancelToken.throwIfRequested()).toThrowError();
|
||||
expect(ct.throwIfRequested()).toBeUndefined();
|
||||
|
||||
ca();
|
||||
|
||||
expect(() => ct.throwIfRequested()).toThrowError();
|
||||
});
|
||||
|
||||
test('应该抛出正确的异常信息', async () => {
|
||||
let cancelAction!: (msg: string) => void;
|
||||
const cancelToken = new CancelToken((action) => {
|
||||
cancelAction = action;
|
||||
});
|
||||
let ca!: (msg: string) => void;
|
||||
const ct = new CancelToken((a) => (ca = a));
|
||||
|
||||
cancelAction('stop');
|
||||
const error = captureError<Cancel>(() => cancelToken.throwIfRequested());
|
||||
expect(error.message).toBe('stop');
|
||||
expect(error.toString()).toBe('Cancel: stop');
|
||||
const te = () => ct.throwIfRequested();
|
||||
|
||||
ca('stop');
|
||||
|
||||
expect(te).toThrowErrorMatchingInlineSnapshot(`
|
||||
Cancel {
|
||||
"message": "stop",
|
||||
}
|
||||
`);
|
||||
expect(captureError<Cancel>(te).toString()).toBe('Cancel: stop');
|
||||
});
|
||||
|
||||
test('回调函数应该被异步执行', async () => {
|
||||
const canceled = vi.fn();
|
||||
let cancelAction!: () => void;
|
||||
const cancelToken = new CancelToken((action) => {
|
||||
cancelAction = action;
|
||||
});
|
||||
cancelToken.onCancel(canceled);
|
||||
expect(canceled).not.toBeCalled();
|
||||
const cb = vi.fn();
|
||||
let ca!: () => void;
|
||||
const ct = new CancelToken((a) => (ca = a));
|
||||
|
||||
cancelAction();
|
||||
ct.onCancel(cb);
|
||||
expect(cb).not.toBeCalled();
|
||||
|
||||
expect(canceled).not.toBeCalled();
|
||||
ca();
|
||||
|
||||
expect(cb).not.toBeCalled();
|
||||
|
||||
await asyncNext();
|
||||
expect(canceled).toBeCalled();
|
||||
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||
expect(cb).toBeCalled();
|
||||
expect(isCancel(cb.mock.calls[0][0])).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该正确判断 CancelToken', () => {
|
||||
|
@ -83,49 +86,51 @@ describe('src/helpers/cancel.ts', () => {
|
|||
});
|
||||
|
||||
test('应该有正确返回结果', () => {
|
||||
const source = CancelToken.source();
|
||||
const s = CancelToken.source();
|
||||
|
||||
expect(source.cancel).toBeTypeOf('function');
|
||||
expect(isCancelToken(source.token)).toBeTruthy();
|
||||
expect(s.cancel).toBeTypeOf('function');
|
||||
expect(isCancelToken(s.token)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该可以取消', () => {
|
||||
const source = CancelToken.source();
|
||||
const s = CancelToken.source();
|
||||
|
||||
expect(source.token.throwIfRequested()).toBeUndefined();
|
||||
expect(s.token.throwIfRequested()).toBeUndefined();
|
||||
|
||||
source.cancel();
|
||||
s.cancel();
|
||||
|
||||
expect(() => source.token.throwIfRequested()).toThrowError();
|
||||
expect(() => s.token.throwIfRequested()).toThrowError();
|
||||
});
|
||||
|
||||
test('应该可以在请求发出之前取消', async () => {
|
||||
const canceled = vi.fn();
|
||||
const source = CancelToken.source();
|
||||
const cb = vi.fn();
|
||||
const s = CancelToken.source();
|
||||
|
||||
s.cancel();
|
||||
|
||||
source.cancel();
|
||||
axios({
|
||||
adapter: mockAdapterSuccess(),
|
||||
cancelToken: source.token,
|
||||
}).catch(canceled);
|
||||
adapter: mockAdapter(),
|
||||
cancelToken: s.token,
|
||||
}).catch(cb);
|
||||
|
||||
await asyncTimeout();
|
||||
expect(canceled).toBeCalled();
|
||||
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||
expect(cb).toBeCalled();
|
||||
expect(isCancel(cb.mock.calls[0][0])).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该可以在请求发出之后取消', async () => {
|
||||
const canceled = vi.fn();
|
||||
const source = CancelToken.source();
|
||||
const cb = vi.fn();
|
||||
const s = CancelToken.source();
|
||||
|
||||
axios({
|
||||
adapter: mockAdapterSuccess(),
|
||||
cancelToken: source.token,
|
||||
}).catch(canceled);
|
||||
source.cancel();
|
||||
adapter: mockAdapter(),
|
||||
cancelToken: s.token,
|
||||
}).catch(cb);
|
||||
|
||||
s.cancel();
|
||||
|
||||
await asyncTimeout();
|
||||
expect(canceled).toBeCalled();
|
||||
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||
expect(cb).toBeCalled();
|
||||
expect(isCancel(cb.mock.calls[0][0])).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { cleanedStack } from 'scripts/test.utils';
|
||||
import { checkStack } from 'scripts/test.utils';
|
||||
import { createError } from 'src/core/createError';
|
||||
|
||||
describe('src/core/createError.ts', () => {
|
||||
test('应该支持空参数', () => {
|
||||
const config = {};
|
||||
const axiosError = createError('error', config);
|
||||
const c = {};
|
||||
const err = createError('error', c);
|
||||
|
||||
expect(axiosError.isAxiosError).toBeTruthy();
|
||||
expect(axiosError.message).toBe('error');
|
||||
expect(axiosError.config).toBe(config);
|
||||
expect(cleanedStack(axiosError)).toBeTruthy();
|
||||
expect(err.isAxiosError).toBeTruthy();
|
||||
expect(err.message).toBe('error');
|
||||
expect(err.config).toBe(c);
|
||||
expect(checkStack(err)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该支持传入更多参数', () => {
|
||||
const config = {};
|
||||
const request = {};
|
||||
const response = {};
|
||||
const axiosError = createError('error', config, request, response as any);
|
||||
const c = {};
|
||||
const req = {};
|
||||
const res = {};
|
||||
const err = createError('error', c, req, res as any);
|
||||
|
||||
expect(axiosError.message).toBe('error');
|
||||
expect(axiosError.config).toBe(config);
|
||||
expect(axiosError.request).toBe(request);
|
||||
expect(axiosError.response).toBe(response);
|
||||
expect(err.message).toBe('error');
|
||||
expect(err.config).toBe(c);
|
||||
expect(err.request).toBe(req);
|
||||
expect(err.response).toBe(res);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { flattenHeaders } from 'src/core/flattenHeaders';
|
||||
import Axios from 'src/core/Axios';
|
||||
|
||||
describe('src/core/flattenHeaders.ts', () => {
|
||||
const keys = [...Axios.as, ...Axios.pas, ...Axios.das];
|
||||
const baseHeaders = {
|
||||
options: {
|
||||
v1: 'options1',
|
||||
v2: 'options2',
|
||||
},
|
||||
trace: {
|
||||
v1: 'trace1',
|
||||
v2: 'trace2',
|
||||
},
|
||||
connect: {
|
||||
v1: 'connect1',
|
||||
v2: 'connect2',
|
||||
},
|
||||
head: {
|
||||
v1: 'head1',
|
||||
v2: 'head2',
|
||||
},
|
||||
get: {
|
||||
v1: 'get1',
|
||||
v2: 'get2',
|
||||
},
|
||||
delete: {
|
||||
v1: 'delete1',
|
||||
v2: 'delete2',
|
||||
},
|
||||
post: {
|
||||
v1: 'post1',
|
||||
v2: 'post2',
|
||||
},
|
||||
put: {
|
||||
v1: 'put1',
|
||||
v2: 'put2',
|
||||
},
|
||||
};
|
||||
|
||||
test('应该支持空配置', () => {
|
||||
expect(flattenHeaders({})).toBeUndefined();
|
||||
});
|
||||
|
||||
test('应该支持自定义 headers', () => {
|
||||
const h = {
|
||||
v1: '1',
|
||||
v2: '2',
|
||||
};
|
||||
expect(flattenHeaders({ headers: h, method: 'get' })).toEqual(h);
|
||||
});
|
||||
|
||||
test('应该支持别名 headers,并且自定义 headers 优先级应该高于别名 headers', () => {
|
||||
const h1 = baseHeaders;
|
||||
const h2 = { v1: 1, v2: 2 };
|
||||
const h3 = { ...h1, ...h2 };
|
||||
|
||||
keys.forEach((a) => {
|
||||
expect(flattenHeaders({ headers: h1, method: a })).toEqual(h1[a]);
|
||||
expect(flattenHeaders({ headers: h3, method: a })).toEqual(h2);
|
||||
});
|
||||
});
|
||||
|
||||
test('应该支持通用 headers,并且别名 headers 优先级应该高于通用 headers', () => {
|
||||
const h1 = {
|
||||
common: {
|
||||
v1: 'common1',
|
||||
v2: 'common2',
|
||||
},
|
||||
};
|
||||
const h2 = { ...baseHeaders, ...h1 };
|
||||
|
||||
keys.forEach((a) => {
|
||||
expect(flattenHeaders({ headers: h1, method: a })).toEqual(h1.common);
|
||||
expect(flattenHeaders({ headers: h2, method: a })).toEqual(h2[a]);
|
||||
});
|
||||
});
|
||||
|
||||
test.each(
|
||||
keys.map((k) => [
|
||||
k,
|
||||
{
|
||||
common: {
|
||||
v1: 'common1',
|
||||
v2: 'common1',
|
||||
},
|
||||
[k]: {
|
||||
v3: `${k}1`,
|
||||
v4: `${k}2`,
|
||||
},
|
||||
v5: 5,
|
||||
v6: 6,
|
||||
},
|
||||
]),
|
||||
)('应该获取到完整的 %s headers', (k, h) => {
|
||||
const h1 = {
|
||||
v1: 'common1',
|
||||
v2: 'common1',
|
||||
v5: 5,
|
||||
v6: 6,
|
||||
};
|
||||
const h2 = {
|
||||
...h1,
|
||||
v3: `${k}1`,
|
||||
v4: `${k}2`,
|
||||
};
|
||||
|
||||
keys.forEach((a) => {
|
||||
expect(flattenHeaders({ headers: h, method: a })).toEqual(
|
||||
a !== k ? h1 : h2,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,50 +1,19 @@
|
|||
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,
|
||||
});
|
||||
for (const a of [...Axios.as, ...Axios.pas, ...Axios.das]) {
|
||||
expect(generateType({ method: a })).toBe('request');
|
||||
}
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
import { describe, test, expect, vi } from 'vitest';
|
||||
import { ignore } from 'src/helpers/ignore';
|
||||
import { mergeConfig } from 'src/core/mergeConfig';
|
||||
import { CancelToken } from 'src/core/cancel';
|
||||
|
||||
describe('src/core/mergeConfig.ts', () => {
|
||||
test('应该支持空参数', () => {
|
||||
expect(mergeConfig()).toEqual({});
|
||||
expect(mergeConfig({ baseURL: '/api' })).toEqual({ baseURL: '/api' });
|
||||
});
|
||||
|
||||
test('应该只取 config2', () => {
|
||||
const c1 = {
|
||||
url: 'a',
|
||||
method: 'get' as const,
|
||||
data: {},
|
||||
upload: true,
|
||||
download: true,
|
||||
};
|
||||
const c2 = {
|
||||
url: 'b',
|
||||
method: 'post' as const,
|
||||
data: {},
|
||||
upload: false,
|
||||
download: false,
|
||||
};
|
||||
|
||||
expect(mergeConfig(c1, {})).toEqual({});
|
||||
expect(mergeConfig({}, c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, c2)).toEqual(c2);
|
||||
|
||||
Object.keys(c2).forEach((_) => {
|
||||
const key = _ as keyof typeof c2;
|
||||
expect(mergeConfig(ignore(c1, key), c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, ignore(c2, key))).toEqual(ignore(c2, key));
|
||||
});
|
||||
});
|
||||
|
||||
test('应该深度合并', () => {
|
||||
const c1 = {
|
||||
headers: {
|
||||
common: {
|
||||
v1: 1,
|
||||
},
|
||||
v1: 1,
|
||||
},
|
||||
params: {
|
||||
v1: 1,
|
||||
},
|
||||
};
|
||||
const c2 = {
|
||||
headers: {
|
||||
common: {
|
||||
v2: 2,
|
||||
},
|
||||
v2: 2,
|
||||
},
|
||||
params: {
|
||||
v2: 2,
|
||||
},
|
||||
};
|
||||
const mc = {
|
||||
headers: {
|
||||
common: {
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
},
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
},
|
||||
params: {
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeConfig(c1, {})).toEqual(c1);
|
||||
expect(mergeConfig({}, c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, c2)).toEqual(mc);
|
||||
|
||||
Object.keys(c2).forEach((_) => {
|
||||
const key = _ as keyof typeof c2;
|
||||
expect(mergeConfig(ignore(c1, key), c2)).toEqual({
|
||||
...mc,
|
||||
[key]: c2[key],
|
||||
});
|
||||
expect(mergeConfig(c1, ignore(c2, key))).toEqual({
|
||||
...mc,
|
||||
[key]: c1[key],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('应该优先取 config2', () => {
|
||||
const c1 = {
|
||||
adapter: vi.fn(),
|
||||
baseURL: 'https://c1.com',
|
||||
paramsSerializer: vi.fn(),
|
||||
transformRequest: vi.fn(),
|
||||
transformResponse: vi.fn(),
|
||||
errorHandler: vi.fn(),
|
||||
cancelToken: CancelToken.source().token,
|
||||
dataType: 'json',
|
||||
responseType: 'json',
|
||||
timeout: 1000,
|
||||
validateStatus: vi.fn(),
|
||||
onUploadProgress: vi.fn(),
|
||||
onDownloadProgress: vi.fn(),
|
||||
};
|
||||
const c2 = {
|
||||
adapter: vi.fn(),
|
||||
baseURL: 'https://c2.com',
|
||||
paramsSerializer: vi.fn(),
|
||||
transformRequest: vi.fn(),
|
||||
transformResponse: vi.fn(),
|
||||
errorHandler: vi.fn(),
|
||||
cancelToken: CancelToken.source().token,
|
||||
dataType: 'json',
|
||||
responseType: 'json',
|
||||
timeout: 1000,
|
||||
validateStatus: vi.fn(),
|
||||
onUploadProgress: vi.fn(),
|
||||
onDownloadProgress: vi.fn(),
|
||||
};
|
||||
|
||||
expect(mergeConfig(c1, {})).toEqual(c1);
|
||||
expect(mergeConfig({}, c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, c2)).toEqual(c2);
|
||||
|
||||
Object.keys(c2).forEach((_) => {
|
||||
const key = _ as keyof typeof c2;
|
||||
expect(mergeConfig(ignore(c1, key), c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, ignore(c2, key))).toEqual({
|
||||
...c2,
|
||||
[key]: c1[key],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('应该支持自定义配置', () => {
|
||||
const c1 = {
|
||||
custom1: 1,
|
||||
custom2: 'c1',
|
||||
custom3: vi.fn(),
|
||||
custom4: { c1: 1 },
|
||||
custom5: ['c1'],
|
||||
custom6: new Date(),
|
||||
custom7: () => 1,
|
||||
};
|
||||
const c2 = {
|
||||
custom1: 2,
|
||||
custom2: 'c2',
|
||||
custom3: vi.fn(),
|
||||
custom4: { c2: 2 },
|
||||
custom5: ['c2'],
|
||||
custom6: new Date(),
|
||||
custom7: () => 2,
|
||||
};
|
||||
|
||||
expect(mergeConfig(c1, {})).toEqual(c1);
|
||||
expect(mergeConfig({}, c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, c2)).toEqual(c2);
|
||||
|
||||
Object.keys(c2).forEach((_) => {
|
||||
const key = _ as keyof typeof c2;
|
||||
expect(mergeConfig(ignore(c1, key), c2)).toEqual(c2);
|
||||
expect(mergeConfig(c1, ignore(c2, key))).toEqual({
|
||||
...c2,
|
||||
[key]: c1[key],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { request } from 'src/core/request';
|
||||
import {
|
||||
mockAdapter,
|
||||
mockAdapterError,
|
||||
mockAdapterFail,
|
||||
} from 'scripts/test.utils';
|
||||
|
||||
describe('src/core/request.ts', () => {
|
||||
test('应该抛出异常', async () => {
|
||||
await expect(request({})).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
'"[axios-miniprogram]: adapter 不是一个 function"',
|
||||
);
|
||||
await expect(
|
||||
request({ adapter: mockAdapter() }),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
'"[axios-miniprogram]: url 不是一个 string"',
|
||||
);
|
||||
await expect(
|
||||
request({ adapter: mockAdapter(), url: 'test' }),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
'"[axios-miniprogram]: method 不是一个 string"',
|
||||
);
|
||||
});
|
||||
|
||||
test('应该能够取到数据', async () => {
|
||||
await expect(
|
||||
request({
|
||||
adapter: mockAdapter(),
|
||||
url: '/test',
|
||||
method: 'get',
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"config": {
|
||||
"adapter": [Function],
|
||||
"method": "get",
|
||||
"url": "/test",
|
||||
},
|
||||
"data": {},
|
||||
"headers": {},
|
||||
"request": undefined,
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
}
|
||||
`);
|
||||
await expect(
|
||||
request({
|
||||
adapter: mockAdapterError(),
|
||||
url: '/test',
|
||||
method: 'get',
|
||||
}),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
{
|
||||
"config": {
|
||||
"adapter": [Function],
|
||||
"method": "get",
|
||||
"url": "/test",
|
||||
},
|
||||
"data": {},
|
||||
"headers": {},
|
||||
"request": undefined,
|
||||
"status": 400,
|
||||
"statusText": "FAIL",
|
||||
}
|
||||
`);
|
||||
await expect(
|
||||
request({
|
||||
adapter: mockAdapterFail(),
|
||||
url: '/test',
|
||||
method: 'get',
|
||||
}),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot('"网络错误"');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { transformData } from 'src/core/transformData';
|
||||
|
||||
describe('src/core/transformData.ts', () => {
|
||||
test('应该支持空配置', () => {
|
||||
expect(transformData()).toBeUndefined();
|
||||
expect(transformData({})).toEqual({});
|
||||
});
|
||||
|
||||
test('应该支持转换器', () => {
|
||||
const h = {
|
||||
type: 0,
|
||||
};
|
||||
const d = {
|
||||
v1: 1,
|
||||
};
|
||||
const t = {
|
||||
v2: 2,
|
||||
};
|
||||
|
||||
const fn = (data: any, headers: any) => {
|
||||
expect(data).toEqual(d);
|
||||
expect(headers).toEqual(h);
|
||||
return t;
|
||||
};
|
||||
|
||||
expect(transformData(d, h, fn)).toEqual(t);
|
||||
});
|
||||
|
||||
test('应该支持转换器数组', () => {
|
||||
const h = {
|
||||
type: 0,
|
||||
};
|
||||
const d = {
|
||||
v1: 1,
|
||||
};
|
||||
const t1 = {
|
||||
v2: 2,
|
||||
};
|
||||
const t2 = {
|
||||
v3: 3,
|
||||
};
|
||||
|
||||
const fn1 = (data: any, headers: any) => {
|
||||
expect(data).toEqual(d);
|
||||
expect(headers).toEqual(h);
|
||||
return t1;
|
||||
};
|
||||
|
||||
const fn2 = (data: any, headers: any) => {
|
||||
expect(data).toEqual(t1);
|
||||
expect(headers).toEqual(h);
|
||||
return t2;
|
||||
};
|
||||
|
||||
expect(transformData(d, h, [fn1, fn2])).toEqual(t2);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,74 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { transformURL } from 'src/core/transformURL';
|
||||
|
||||
describe('src/core/transformURL.ts', () => {
|
||||
test('应该支持空配置', () => {
|
||||
expect(transformURL({})).toBe('');
|
||||
expect(transformURL({ baseURL: 'http://api.com' })).toBe('http://api.com');
|
||||
expect(transformURL({ url: 'test' })).toBe('/test');
|
||||
});
|
||||
|
||||
test('应该合并 URL', () => {
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'test',
|
||||
}),
|
||||
).toBe('http://api.com/test');
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: '/test',
|
||||
}),
|
||||
).toBe('http://api.com/test');
|
||||
});
|
||||
|
||||
test('应该支持绝对路径', () => {
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'http://api2.com',
|
||||
}),
|
||||
).toBe('http://api2.com');
|
||||
});
|
||||
|
||||
test('应该支持动态 URL', () => {
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'test/:name/:type',
|
||||
params: {
|
||||
name: 'axios',
|
||||
type: 0,
|
||||
},
|
||||
}),
|
||||
).toBe('http://api.com/test/axios/0?name=axios&type=0');
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'test/:name/:type',
|
||||
data: {
|
||||
name: 'axios',
|
||||
type: 0,
|
||||
},
|
||||
}),
|
||||
).toBe('http://api.com/test/axios/0');
|
||||
});
|
||||
|
||||
test('应该支持自定义参数系列化器', () => {
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'test',
|
||||
paramsSerializer: () => 'type=0',
|
||||
}),
|
||||
).toBe('http://api.com/test?type=0');
|
||||
expect(
|
||||
transformURL({
|
||||
baseURL: 'http://api.com',
|
||||
url: 'test?name=axios',
|
||||
paramsSerializer: () => 'type=0',
|
||||
}),
|
||||
).toBe('http://api.com/test?name=axios&type=0');
|
||||
});
|
||||
});
|
|
@ -3,61 +3,61 @@ import { buildURL } from 'src/helpers/buildURL';
|
|||
|
||||
describe('src/helpers/buildURL.ts', () => {
|
||||
test('应该支持空参数', () => {
|
||||
expect(buildURL('/user')).toBe('/user');
|
||||
expect(buildURL('/test')).toBe('/test');
|
||||
});
|
||||
|
||||
test('应该清理哈希值', () => {
|
||||
expect(buildURL('/user#hash')).toBe('/user');
|
||||
expect(buildURL('/test#hash')).toBe('/test');
|
||||
});
|
||||
|
||||
test('应该对参数进行系列化', () => {
|
||||
expect(
|
||||
buildURL('/user#hash', {
|
||||
buildURL('/test#hash', {
|
||||
v1: 1,
|
||||
v2: undefined,
|
||||
v3: null,
|
||||
v4: '4',
|
||||
v5: NaN,
|
||||
}),
|
||||
).toBe('/user?v1=1&v4=4');
|
||||
).toBe('/test?v1=1&v4=4');
|
||||
|
||||
expect(
|
||||
buildURL('/user?v1=1', {
|
||||
buildURL('/test?v1=1', {
|
||||
v2: 2,
|
||||
}),
|
||||
).toBe('/user?v1=1&v2=2');
|
||||
).toBe('/test?v1=1&v2=2');
|
||||
});
|
||||
|
||||
test('应该对数组进行系列化', () => {
|
||||
expect(
|
||||
buildURL('/user', {
|
||||
buildURL('/test', {
|
||||
arr: [1, 2],
|
||||
}),
|
||||
).toBe('/user?arr[]=1&arr[]=2');
|
||||
).toBe('/test?arr[]=1&arr[]=2');
|
||||
});
|
||||
|
||||
test('应该对对象进行系列化', () => {
|
||||
expect(
|
||||
buildURL('/user', {
|
||||
buildURL('/test', {
|
||||
obj: {
|
||||
k1: 1,
|
||||
k2: 2,
|
||||
},
|
||||
}),
|
||||
).toBe('/user?obj[k1]=1&obj[k2]=2');
|
||||
).toBe('/test?obj[k1]=1&obj[k2]=2');
|
||||
});
|
||||
|
||||
test('应该对日期进行系列化', () => {
|
||||
const date = new Date();
|
||||
expect(buildURL('/user', { date })).toBe(
|
||||
`/user?date=${date.toISOString()}`,
|
||||
const d = new Date();
|
||||
expect(buildURL('/test', { date: d })).toBe(
|
||||
`/test?date=${d.toISOString()}`,
|
||||
);
|
||||
});
|
||||
|
||||
test('应该支持自定义序列化器', () => {
|
||||
expect(buildURL('/user', {}, () => 'v1=1&v2=2')).toBe('/user?v1=1&v2=2');
|
||||
expect(buildURL('/user?v1=1', {}, () => 'v2=2&v3=3')).toBe(
|
||||
'/user?v1=1&v2=2&v3=3',
|
||||
expect(buildURL('/test', {}, () => 'v1=1&v2=2')).toBe('/test?v1=1&v2=2');
|
||||
expect(buildURL('/test?v1=1', {}, () => 'v2=2&v3=3')).toBe(
|
||||
'/test?v1=1&v2=2&v3=3',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,17 +9,17 @@ describe('src/helpers/combineURL.ts', () => {
|
|||
});
|
||||
|
||||
test('应该得到拼接后的结果', () => {
|
||||
expect(combineURL('http://api.com', '/user')).toBe('http://api.com/user');
|
||||
expect(combineURL('file://api.com', '/user')).toBe('file://api.com/user');
|
||||
expect(combineURL('unknow://api.com', '/user')).toBe(
|
||||
'unknow://api.com/user',
|
||||
expect(combineURL('http://api.com', '/test')).toBe('http://api.com/test');
|
||||
expect(combineURL('file://api.com', '/test')).toBe('file://api.com/test');
|
||||
expect(combineURL('unknow://api.com', '/test')).toBe(
|
||||
'unknow://api.com/test',
|
||||
);
|
||||
});
|
||||
|
||||
test('应该清理多余的斜线', () => {
|
||||
expect(combineURL('//api//', '//user//')).toBe('/api/user/');
|
||||
expect(combineURL('http://api.com//', '//user//')).toBe(
|
||||
'http://api.com/user/',
|
||||
expect(combineURL('//api//', '//test//')).toBe('/api/test/');
|
||||
expect(combineURL('http://api.com//', '//test//')).toBe(
|
||||
'http://api.com/test/',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,66 +3,50 @@ import { deepMerge } from 'src/helpers/deepMerge';
|
|||
|
||||
describe('src/helpers/deepMerge.ts', () => {
|
||||
test('应该支持空参数', () => {
|
||||
expect(deepMerge()).toEqual({});
|
||||
});
|
||||
|
||||
test('应该直接返回第一个参数', () => {
|
||||
expect(
|
||||
deepMerge({
|
||||
v1: 1,
|
||||
v2: [1],
|
||||
v3: { v: 'v3' },
|
||||
v4: undefined,
|
||||
v5: null,
|
||||
v6: 'v6',
|
||||
}),
|
||||
).toEqual({
|
||||
const o = {
|
||||
v1: 1,
|
||||
v2: [1],
|
||||
v3: { v: 'v3' },
|
||||
v4: undefined,
|
||||
v5: null,
|
||||
v6: 'v6',
|
||||
});
|
||||
};
|
||||
|
||||
expect(deepMerge()).toEqual({});
|
||||
expect(deepMerge(o)).toEqual(o);
|
||||
});
|
||||
|
||||
test('应该进行合并', () => {
|
||||
expect(
|
||||
deepMerge(
|
||||
{
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
v3: 3,
|
||||
},
|
||||
{
|
||||
v2: 22,
|
||||
v3: undefined,
|
||||
v4: 4,
|
||||
},
|
||||
),
|
||||
).toEqual({
|
||||
const o1 = {
|
||||
v1: 1,
|
||||
v2: 2,
|
||||
v3: 3,
|
||||
};
|
||||
const o2 = {
|
||||
v2: 22,
|
||||
v3: undefined,
|
||||
v4: 4,
|
||||
};
|
||||
|
||||
expect(deepMerge<AnyObject>(o1, o2)).toEqual({
|
||||
...o1,
|
||||
...o2,
|
||||
});
|
||||
});
|
||||
|
||||
test('应该合并对象里的对象', () => {
|
||||
expect(
|
||||
deepMerge(
|
||||
{
|
||||
v1: { v: 1 },
|
||||
v2: { v: 2 },
|
||||
v3: 3,
|
||||
},
|
||||
{
|
||||
v1: { vv: 11 },
|
||||
v2: 2,
|
||||
v3: { v: 3 },
|
||||
},
|
||||
),
|
||||
).toEqual({
|
||||
const o1 = {
|
||||
v1: { v: 1 },
|
||||
v2: { v: 2 },
|
||||
v3: 3,
|
||||
};
|
||||
const o2 = {
|
||||
v1: { vv: 11 },
|
||||
v2: 2,
|
||||
v3: { v: 3 },
|
||||
};
|
||||
|
||||
expect(deepMerge<AnyObject>(o1, o2)).toEqual({
|
||||
v1: { v: 1, vv: 11 },
|
||||
v2: 2,
|
||||
v3: { v: 3 },
|
||||
|
|
|
@ -3,20 +3,20 @@ import { dynamicURL } from 'src/helpers/dynamicURL';
|
|||
|
||||
describe('src/helpers/dynamicURL.ts', () => {
|
||||
test('应该替换关键字', () => {
|
||||
expect(dynamicURL('http://api.com/user/:id', {})).toBe(
|
||||
'http://api.com/user/undefined',
|
||||
expect(dynamicURL('http://api.com/test/:id', {})).toBe(
|
||||
'http://api.com/test/undefined',
|
||||
);
|
||||
expect(dynamicURL('http://api.com/user/:id', { id: 1 })).toBe(
|
||||
'http://api.com/user/1',
|
||||
expect(dynamicURL('http://api.com/test/:id', { id: 1 })).toBe(
|
||||
'http://api.com/test/1',
|
||||
);
|
||||
});
|
||||
|
||||
test('应该支持多个关键字', () => {
|
||||
expect(
|
||||
dynamicURL('http://api.com/users/name/:name/age/:age/list', {
|
||||
name: 'my',
|
||||
age: 18,
|
||||
dynamicURL('http://api.com/tests/name/:name/type/:type/list', {
|
||||
name: 'axios',
|
||||
type: 0,
|
||||
}),
|
||||
).toBe('http://api.com/users/name/my/age/18/list');
|
||||
).toBe('http://api.com/tests/name/axios/type/0/list');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, test, expect } from 'vitest';
|
||||
import { captureError, cleanedStack } from 'scripts/test.utils';
|
||||
import { captureError, checkStack } from 'scripts/test.utils';
|
||||
import { assert, throwError, cleanStack } from 'src/helpers/error';
|
||||
|
||||
describe('src/helpers/error.ts', () => {
|
||||
|
@ -9,25 +9,27 @@ describe('src/helpers/error.ts', () => {
|
|||
|
||||
test('第一个参数为 false 时应该抛出异常', () => {
|
||||
expect(() => assert(false, '')).toThrowError();
|
||||
expect(cleanedStack(captureError(() => assert(false, '')))).toBeTruthy();
|
||||
expect(checkStack(captureError(() => assert(false, '')))).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该抛出异常', () => {
|
||||
expect(() => throwError('')).toThrowError('[axios-miniprogram]: ');
|
||||
expect(() => throwError('error')).toThrowError(
|
||||
'[axios-miniprogram]: error',
|
||||
expect(() => throwError('')).toThrowErrorMatchingInlineSnapshot(
|
||||
'"[axios-miniprogram]: "',
|
||||
);
|
||||
expect(cleanedStack(captureError(() => throwError('error')))).toBeTruthy();
|
||||
expect(() => throwError('error')).toThrowErrorMatchingInlineSnapshot(
|
||||
'"[axios-miniprogram]: error"',
|
||||
);
|
||||
expect(checkStack(captureError(() => throwError('error')))).toBeTruthy();
|
||||
});
|
||||
|
||||
test('应该清掉多余的错误栈', () => {
|
||||
const ce = () => new Error();
|
||||
const error = ce();
|
||||
|
||||
expect(cleanedStack(error)).toBeFalsy();
|
||||
expect(checkStack(error)).toBeFalsy();
|
||||
|
||||
cleanStack(error);
|
||||
|
||||
expect(cleanedStack(error)).toBeTruthy();
|
||||
expect(checkStack(error)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,31 +3,37 @@ import { ignore } from 'src/helpers/ignore';
|
|||
|
||||
describe('src/helpers/ignore.ts', () => {
|
||||
test('不应该改变传入的对象', () => {
|
||||
expect(
|
||||
ignore({
|
||||
v1: 1,
|
||||
}),
|
||||
).toEqual({
|
||||
v1: 1,
|
||||
});
|
||||
expect(ignore({ v1: 1 })).toEqual({ v1: 1 });
|
||||
});
|
||||
|
||||
test('应该忽略指定键值', () => {
|
||||
expect(
|
||||
ignore(
|
||||
{
|
||||
v1: 1,
|
||||
v2: {},
|
||||
v3: [],
|
||||
v4: undefined,
|
||||
v5: 5,
|
||||
v6: null,
|
||||
},
|
||||
'v1',
|
||||
'v2',
|
||||
'v3',
|
||||
'v4',
|
||||
),
|
||||
).toEqual({ v5: 5, v6: null });
|
||||
const o = {
|
||||
v1: 1,
|
||||
v2: {},
|
||||
v3: [],
|
||||
};
|
||||
|
||||
expect(ignore(o, 'v1')).toEqual({
|
||||
v2: {},
|
||||
v3: [],
|
||||
});
|
||||
expect(ignore(o, 'v2')).toEqual({
|
||||
v1: 1,
|
||||
v3: [],
|
||||
});
|
||||
expect(ignore(o, 'v3')).toEqual({
|
||||
v1: 1,
|
||||
v2: {},
|
||||
});
|
||||
expect(ignore(o, 'v1', 'v2')).toEqual({
|
||||
v3: [],
|
||||
});
|
||||
expect(ignore(o, 'v2', 'v3')).toEqual({
|
||||
v1: 1,
|
||||
});
|
||||
expect(ignore(o, 'v1', 'v3')).toEqual({
|
||||
v2: {},
|
||||
});
|
||||
expect(ignore(o, 'v1', 'v2', 'v3')).toEqual({});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,17 +4,17 @@ import { isAbsoluteURL } from 'src/helpers/isAbsoluteURL';
|
|||
describe('src/helpers/isAbsoluteURL.ts', () => {
|
||||
test('应该不是绝对路径', () => {
|
||||
expect(isAbsoluteURL('user')).toBeFalsy();
|
||||
expect(isAbsoluteURL('/user')).toBeFalsy();
|
||||
expect(isAbsoluteURL('//user')).toBeFalsy();
|
||||
expect(isAbsoluteURL('://user')).toBeFalsy();
|
||||
expect(isAbsoluteURL('/test')).toBeFalsy();
|
||||
expect(isAbsoluteURL('//test')).toBeFalsy();
|
||||
expect(isAbsoluteURL('://test')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('应该是绝对路径', () => {
|
||||
expect(isAbsoluteURL('http://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('HTTP://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('https://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom-v1.0://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom_v1.0://user')).toBeTruthy();
|
||||
expect(isAbsoluteURL('http://test')).toBeTruthy();
|
||||
expect(isAbsoluteURL('HTTP://test')).toBeTruthy();
|
||||
expect(isAbsoluteURL('https://test')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom://test')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom-v1.0://test')).toBeTruthy();
|
||||
expect(isAbsoluteURL('custom_v1.0://test')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
"noEmit": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["./src", "./test", "./global.d.ts"],
|
||||
"include": ["./src", "./test", "./global.d.ts", "./global.variables.d.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue