Skip to content

Commit

Permalink
feat(timeout): add feature timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
JOU-amjs committed May 7, 2023
1 parent 969d9ca commit b3468a1
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 8 deletions.
2 changes: 1 addition & 1 deletion jest.config.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default {
// setupFiles: [],

// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],

// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
Expand Down
2 changes: 1 addition & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export default {
// setupFiles: [],

// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],

// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
Expand Down
17 changes: 15 additions & 2 deletions src/MockRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ export default function MockRequest<RC, RE, RH>(

let timer: NodeJS.Timeout;
let rejectFn: (reason?: any) => void = noop;
const timeout = method.config.timeout || 0;
if (timeout > 0) {
setTimeout(() => {
rejectFn(new Error('request timeout'));
}, timeout);
}
const resonpsePromise = new Promise<any>((resolve, reject) => {
rejectFn = reject;
timer = setTimeout(() => {
Expand All @@ -104,7 +110,14 @@ export default function MockRequest<RC, RE, RH>(
headers: requestHeaders
})
: mockDataRaw;
resolve(res);

// 这段代码表示,将内部reject赋值到外部,如果超时了则立即触发reject,或者等待res(如果res为promise)resolve
resolve(
new Promise<any>((resolveInner, rejectInner) => {
rejectFn = rejectInner;
Promise.resolve(res).then(resolveInner).catch(rejectInner);
})
);
} catch (error) {
reject(error);
}
Expand Down Expand Up @@ -166,7 +179,7 @@ export default function MockRequest<RC, RE, RH>(
headers: () => resonpsePromise.then(({ headers }) => headers),
abort: () => {
clearTimeout(timer);
rejectFn('The user abort request');
rejectFn(new Error('The user abort request'));
}
};
};
Expand Down
120 changes: 119 additions & 1 deletion test/mockRequest.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createAlova } from 'alova';
import { createAlova, useRequest } from 'alova';
import VueHook from 'alova/vue';
import createAlovaMockAdapter from '../src/createAlovaMockAdapter';
import defineMock from '../src/defineMock';
Expand Down Expand Up @@ -269,4 +269,122 @@ describe('mock request', () => {

await expect(() => alovaInst.Post('/detail').send()).rejects.toThrow('new error:network error');
});

test('should abort request when call abort manually', async () => {
const mocks = defineMock({
'[POST]/detail': async () => {
return [];
}
});

// 模拟数据请求适配器
const mockRequestAdapter = createAlovaMockAdapter([mocks], {
delay: 1000
});

const alovaInst = createAlova({
baseURL: 'http://xxx',
statesHook: VueHook,
requestAdapter: mockRequestAdapter
});

const { abort, error } = useRequest(alovaInst.Post('/detail'));
expect(error.value).toBeUndefined();
await new Promise(resolve => {
setTimeout(resolve, 500);
});
abort();
await new Promise(resolve => {
setTimeout(resolve, 100);
});
expect(error.value?.message).toBe('The user abort request');
});

test('should abort request even if delay in mock function', async () => {
const mocks = defineMock({
'[POST]/detail': async () => {
await new Promise(resolve => {
setTimeout(resolve, 1000);
});
return [];
}
});

// 模拟数据请求适配器
const mockRequestAdapter = createAlovaMockAdapter([mocks], {
delay: 10
});

const alovaInst = createAlova({
baseURL: 'http://xxx',
statesHook: VueHook,
requestAdapter: mockRequestAdapter
});
const { abort, error } = useRequest(alovaInst.Post('/detail'));
expect(error.value).toBeUndefined();
await new Promise(resolve => {
setTimeout(resolve, 500);
});
abort();
await new Promise(resolve => {
setTimeout(resolve, 100);
});
expect(error.value?.message).toBe('The user abort request');
});

test('should timeout when timeout', async () => {
const mocks = defineMock({
'[POST]/detail': async () => {
return [];
}
});

// 模拟数据请求适配器
const mockRequestAdapter = createAlovaMockAdapter([mocks], {
delay: 1000
});

const alovaInst = createAlova({
baseURL: 'http://xxx',
statesHook: VueHook,
timeout: 500,
requestAdapter: mockRequestAdapter
});

const { error, onError } = useRequest(alovaInst.Post('/detail'));
expect(error.value).toBeUndefined();
await new Promise(resolve => {
onError(resolve);
});
expect(error.value?.message).toBe('request timeout');
});

test('should timeout even if delay in mock function', async () => {
const mocks = defineMock({
'[POST]/detail': async () => {
await new Promise(resolve => {
setTimeout(resolve, 1000);
});
return [];
}
});

// 模拟数据请求适配器
const mockRequestAdapter = createAlovaMockAdapter([mocks], {
delay: 10
});
const alovaInst = createAlova({
baseURL: 'http://xxx',
statesHook: VueHook,
timeout: 500,
requestAdapter: mockRequestAdapter
});

const { error, onError } = useRequest(alovaInst.Post('/detail'));
expect(error.value).toBeUndefined();
await new Promise(resolve => {
onError(resolve);
});
expect(error.value?.message).toBe('request timeout');
});
});
8 changes: 8 additions & 0 deletions test/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// 防止Vue warn打印
const warn = console.warn;
console.warn = (...args: any[]) => {
args = args.filter((a: any) => !/vue warn/i.test(a));
if (args.length > 0) {
warn.apply(console, args);
}
};
1 change: 0 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"noImplicitAny": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"charset": "utf8",
"noEmitOnError": false,
"noUnusedLocals": true,
"noUnusedParameters": false,
Expand Down
4 changes: 2 additions & 2 deletions typings/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AlovaRequestAdapter, Method } from 'alova';
import { AlovaRequestAdapter, Method, RequestBody } from 'alova';

interface MockServerRequest {
headers: Record<string, any>;
query: Record<string, any>;
params: Record<string, any>;
data: Record<string, any>;
data?: RequestBody;
}

interface ResponseHeaders {
Expand Down

0 comments on commit b3468a1

Please sign in to comment.