diff --git a/jest.config.node.ts b/jest.config.node.ts index 8dcf465..94352ba 100644 --- a/jest.config.node.ts +++ b/jest.config.node.ts @@ -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: ['/test/setup.ts'], // The number of seconds after which a test is considered as slow and reported as such in the results. // slowTestThreshold: 5, diff --git a/jest.config.ts b/jest.config.ts index 5ceda82..029b711 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -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: ['/test/setup.ts'], // The number of seconds after which a test is considered as slow and reported as such in the results. // slowTestThreshold: 5, diff --git a/src/MockRequest.ts b/src/MockRequest.ts index 498113e..4262521 100644 --- a/src/MockRequest.ts +++ b/src/MockRequest.ts @@ -91,6 +91,12 @@ export default function MockRequest( 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((resolve, reject) => { rejectFn = reject; timer = setTimeout(() => { @@ -104,7 +110,14 @@ export default function MockRequest( headers: requestHeaders }) : mockDataRaw; - resolve(res); + + // 这段代码表示,将内部reject赋值到外部,如果超时了则立即触发reject,或者等待res(如果res为promise)resolve + resolve( + new Promise((resolveInner, rejectInner) => { + rejectFn = rejectInner; + Promise.resolve(res).then(resolveInner).catch(rejectInner); + }) + ); } catch (error) { reject(error); } @@ -166,7 +179,7 @@ export default function MockRequest( headers: () => resonpsePromise.then(({ headers }) => headers), abort: () => { clearTimeout(timer); - rejectFn('The user abort request'); + rejectFn(new Error('The user abort request')); } }; }; diff --git a/test/mockRequest.spec.ts b/test/mockRequest.spec.ts index 3f4cf85..4a03579 100644 --- a/test/mockRequest.spec.ts +++ b/test/mockRequest.spec.ts @@ -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'; @@ -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'); + }); }); diff --git a/test/setup.ts b/test/setup.ts new file mode 100644 index 0000000..b566fd3 --- /dev/null +++ b/test/setup.ts @@ -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); + } +}; diff --git a/tsconfig.json b/tsconfig.json index baa312c..a4a9a4f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,6 @@ "noImplicitAny": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, - "charset": "utf8", "noEmitOnError": false, "noUnusedLocals": true, "noUnusedParameters": false, diff --git a/typings/index.d.ts b/typings/index.d.ts index b10bf7a..90a58c0 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1,10 +1,10 @@ -import { AlovaRequestAdapter, Method } from 'alova'; +import { AlovaRequestAdapter, Method, RequestBody } from 'alova'; interface MockServerRequest { headers: Record; query: Record; params: Record; - data: Record; + data?: RequestBody; } interface ResponseHeaders {