test: 测试 cancel
parent
0d1e1c18e8
commit
7a61fda10e
|
@ -0,0 +1,50 @@
|
||||||
|
export function asyncNext() {
|
||||||
|
return Promise.resolve().then;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function asyncTimeout(delay = 0) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, delay);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function captureError<T = any>(fn: () => void): T {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
throw new Error('fn not fail...');
|
||||||
|
} catch (err) {
|
||||||
|
return err as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function noop() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockResponse(
|
||||||
|
status: number,
|
||||||
|
statusText: string,
|
||||||
|
headers: AnyObject,
|
||||||
|
data: AnyObject,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
status,
|
||||||
|
statusText,
|
||||||
|
headers,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockSuccessResponse(
|
||||||
|
headers: AnyObject = {},
|
||||||
|
data: AnyObject = {},
|
||||||
|
) {
|
||||||
|
return mockResponse(200, 'OK', headers, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockFailResponse(
|
||||||
|
headers: AnyObject = {},
|
||||||
|
data: AnyObject = {},
|
||||||
|
) {
|
||||||
|
return mockResponse(400, 'FAIL', headers, data);
|
||||||
|
}
|
|
@ -37,11 +37,11 @@ export function isCancel(value: unknown): value is Cancel {
|
||||||
export class CancelToken {
|
export class CancelToken {
|
||||||
private reason?: Cancel;
|
private reason?: Cancel;
|
||||||
|
|
||||||
public listener: Promise<Cancel>;
|
public onCancel: Promise<Cancel>['then'];
|
||||||
|
|
||||||
public constructor(executor: CancelExecutor) {
|
public constructor(executor: CancelExecutor) {
|
||||||
let action!: CancelAction;
|
let action!: CancelAction;
|
||||||
this.listener = new Promise<Cancel>((resolve) => {
|
const promise = new Promise<Cancel>((resolve) => {
|
||||||
action = (message) => {
|
action = (message) => {
|
||||||
if (this.reason) {
|
if (this.reason) {
|
||||||
return;
|
return;
|
||||||
|
@ -53,6 +53,8 @@ export class CancelToken {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.onCancel = promise.then.bind(promise);
|
||||||
|
|
||||||
executor(action);
|
executor(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ export function request<TData = unknown>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelToken(config.cancelToken)) {
|
if (isCancelToken(config.cancelToken)) {
|
||||||
config.cancelToken.listener.then((reason: unknown) => {
|
config.cancelToken.onCancel((reason: unknown) => {
|
||||||
if (isPlainObject(adapterTask)) {
|
if (isPlainObject(adapterTask)) {
|
||||||
tryToggleProgressUpdate(adapterConfig, adapterTask.offProgressUpdate);
|
tryToggleProgressUpdate(adapterConfig, adapterTask.offProgressUpdate);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { describe, test, expect, vi } from 'vitest';
|
||||||
|
import {
|
||||||
|
asyncNext,
|
||||||
|
captureError,
|
||||||
|
mockSuccessResponse,
|
||||||
|
noop,
|
||||||
|
asyncTimeout,
|
||||||
|
} from 'scripts/test.utils';
|
||||||
|
import axios from 'src/axios';
|
||||||
|
import { Cancel, isCancel, CancelToken, isCancelToken } from 'src/core/cancel';
|
||||||
|
|
||||||
|
describe('测试 src/helpers/cancel.ts', () => {
|
||||||
|
test('应该支持空参数', () => {
|
||||||
|
const cancel = new Cancel();
|
||||||
|
|
||||||
|
expect(cancel.message).toBeUndefined();
|
||||||
|
expect(cancel.toString()).toBe('Cancel');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('传入参数时应该有正确的返回结果', () => {
|
||||||
|
const cancel = new Cancel('error');
|
||||||
|
|
||||||
|
expect(cancel.message).toBe('error');
|
||||||
|
expect(cancel.toString()).toBe('Cancel: error');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该正确判断 Cancel', () => {
|
||||||
|
expect(isCancel(undefined)).toBeFalsy();
|
||||||
|
expect(isCancel({})).toBeFalsy();
|
||||||
|
expect(new Cancel()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该可以取消', () => {
|
||||||
|
let cancelAction!: () => void;
|
||||||
|
const cancelToken = new CancelToken((action) => {
|
||||||
|
cancelAction = action;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(cancelToken.throwIfRequested()).toBeUndefined();
|
||||||
|
cancelAction();
|
||||||
|
expect(() => cancelToken.throwIfRequested()).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该抛出正确的异常信息', async () => {
|
||||||
|
let cancelAction!: (msg: string) => void;
|
||||||
|
const cancelToken = new CancelToken((action) => {
|
||||||
|
cancelAction = action;
|
||||||
|
});
|
||||||
|
|
||||||
|
cancelAction('stop');
|
||||||
|
const error = captureError<Cancel>(() => cancelToken.throwIfRequested());
|
||||||
|
expect(error.message).toBe('stop');
|
||||||
|
expect(error.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();
|
||||||
|
|
||||||
|
cancelAction();
|
||||||
|
|
||||||
|
expect(canceled).not.toBeCalled();
|
||||||
|
|
||||||
|
await asyncNext();
|
||||||
|
expect(canceled).toBeCalled();
|
||||||
|
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该正确判断 CancelToken', () => {
|
||||||
|
expect(isCancelToken(undefined)).toBeFalsy();
|
||||||
|
expect(isCancelToken({})).toBeFalsy();
|
||||||
|
expect(isCancelToken(new CancelToken(noop))).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该有正确返回结果', () => {
|
||||||
|
const source = CancelToken.source();
|
||||||
|
|
||||||
|
expect(source.cancel).toBeTypeOf('function');
|
||||||
|
expect(isCancelToken(source.token)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该可以取消', () => {
|
||||||
|
const source = CancelToken.source();
|
||||||
|
|
||||||
|
expect(source.token.throwIfRequested()).toBeUndefined();
|
||||||
|
|
||||||
|
source.cancel();
|
||||||
|
|
||||||
|
expect(() => source.token.throwIfRequested()).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该可以在请求发出之前取消', async () => {
|
||||||
|
const canceled = vi.fn();
|
||||||
|
const source = CancelToken.source();
|
||||||
|
|
||||||
|
source.cancel();
|
||||||
|
axios({
|
||||||
|
adapter: ({ success }) => success(mockSuccessResponse()),
|
||||||
|
cancelToken: source.token,
|
||||||
|
}).catch(canceled);
|
||||||
|
|
||||||
|
await asyncTimeout();
|
||||||
|
expect(canceled).toBeCalled();
|
||||||
|
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('应该可以在请求发出之后取消', async () => {
|
||||||
|
const canceled = vi.fn();
|
||||||
|
const source = CancelToken.source();
|
||||||
|
|
||||||
|
axios({
|
||||||
|
adapter: ({ success }) => success(mockSuccessResponse()),
|
||||||
|
cancelToken: source.token,
|
||||||
|
}).catch(canceled);
|
||||||
|
source.cancel();
|
||||||
|
|
||||||
|
await asyncTimeout();
|
||||||
|
expect(canceled).toBeCalled();
|
||||||
|
expect(isCancel(canceled.mock.calls[0][0])).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, test, expect } from 'vitest';
|
import { describe, test, expect } from 'vitest';
|
||||||
import { assert, throwError } from '../../src/helpers/error';
|
import { assert, throwError } from 'src/helpers/error';
|
||||||
|
|
||||||
describe('测试 src/helpers/error.ts', () => {
|
describe('测试 src/helpers/error.ts', () => {
|
||||||
test('第一个参数为 true 时应该无事发生', () => {
|
test('第一个参数为 true 时应该无事发生', () => {
|
||||||
|
|
Loading…
Reference in New Issue