From 41beefadae62cf1b5da693b635ca3373209ca422 Mon Sep 17 00:00:00 2001 From: Dmitrii Abramov Date: Fri, 2 Jun 2017 11:39:19 -0700 Subject: [PATCH] jest-circus Timeouts --- .../__snapshots__/timeouts-test.js.snap | 13 ---- integration_tests/__tests__/timeouts-test.js | 6 +- packages/jest-circus/src/run.js | 31 +++++---- packages/jest-circus/src/utils.js | 65 +++++++++++-------- 4 files changed, 61 insertions(+), 54 deletions(-) diff --git a/integration_tests/__tests__/__snapshots__/timeouts-test.js.snap b/integration_tests/__tests__/__snapshots__/timeouts-test.js.snap index 164a5fe634e5..13b00317ee7a 100644 --- a/integration_tests/__tests__/__snapshots__/timeouts-test.js.snap +++ b/integration_tests/__tests__/__snapshots__/timeouts-test.js.snap @@ -17,19 +17,6 @@ Ran all test suites. `; exports[`exceeds the timeout 1`] = ` -" FAIL __tests__/a-banana.js - ● banana - - Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. - - at ../../packages/jest-jasmine2/build/queueRunner.js:53:21 - - ✕ banana - -" -`; - -exports[`exceeds the timeout 2`] = ` "Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total diff --git a/integration_tests/__tests__/timeouts-test.js b/integration_tests/__tests__/timeouts-test.js index d32a895f44a7..4caa36a5824a 100644 --- a/integration_tests/__tests__/timeouts-test.js +++ b/integration_tests/__tests__/timeouts-test.js @@ -36,9 +36,9 @@ test('exceeds the timeout', () => { const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false']); const {rest, summary} = extractSummary(stderr); - expect(status).toBe(1); - expect(rest).toMatchSnapshot(); + expect(rest).toMatch(/(jasmine\.DEFAULT_TIMEOUT_INTERVAL|Exceeded timeout)/); expect(summary).toMatchSnapshot(); + expect(status).toBe(1); }); test('does not exceed the timeout', () => { @@ -57,7 +57,7 @@ test('does not exceed the timeout', () => { const {stderr, status} = runJest(DIR, ['-w=1', '--ci=false']); const {rest, summary} = extractSummary(stderr); - expect(status).toBe(0); expect(rest).toMatchSnapshot(); expect(summary).toMatchSnapshot(); + expect(status).toBe(0); }); diff --git a/packages/jest-circus/src/run.js b/packages/jest-circus/src/run.js index 0ba206adc5ce..02ef2af6c782 100644 --- a/packages/jest-circus/src/run.js +++ b/packages/jest-circus/src/run.js @@ -54,6 +54,16 @@ const _runTestsForDescribeBlock = async (describeBlock: DescribeBlock) => { const _runTest = async (test: TestEntry): Promise => { const testContext = Object.create(null); + + const isSkipped = + test.mode === 'skip' || + (getState().hasFocusedTests && test.mode !== 'only'); + + if (isSkipped) { + dispatch({name: 'test_skip', test}); + return; + } + const {afterEach, beforeEach} = getEachHooksForTest(test); for (const hook of beforeEach) { @@ -69,27 +79,24 @@ const _runTest = async (test: TestEntry): Promise => { const _callHook = (hook: Hook, testContext?: TestContext): Promise => { dispatch({hook, name: 'hook_start'}); - return callAsyncFn(hook.fn, testContext, {isHook: true}) + const {testTimeout: timeout} = getState(); + return callAsyncFn(hook.fn, testContext, {isHook: true, timeout}) .then(() => dispatch({hook, name: 'hook_success'})) .catch(error => dispatch({error, hook, name: 'hook_failure'})); }; -const _callTest = (test: TestEntry, testContext: TestContext): Promise => { - const isSkipped = - test.mode === 'skip' || - (getState().hasFocusedTests && test.mode !== 'only'); - - if (isSkipped) { - dispatch({name: 'test_skip', test}); - return Promise.resolve(); - } - +const _callTest = async ( + test: TestEntry, + testContext: TestContext, +): Promise => { dispatch({name: 'test_start', test}); + const {testTimeout: timeout} = getState(); + if (!test.fn) { throw Error(`Tests with no 'fn' should have 'mode' set to 'skipped'`); } - return callAsyncFn(test.fn, testContext) + return callAsyncFn(test.fn, testContext, {isHook: false, timeout}) .then(() => dispatch({name: 'test_success', test})) .catch(error => dispatch({error, name: 'test_failure', test})); }; diff --git a/packages/jest-circus/src/utils.js b/packages/jest-circus/src/utils.js index 41f5e0578aa5..21a99b246f14 100644 --- a/packages/jest-circus/src/utils.js +++ b/packages/jest-circus/src/utils.js @@ -110,46 +110,59 @@ const getEachHooksForTest = ( return result; }; +const _makeTimeoutMessage = (timeout, isHook) => { + const message = `Exceeded timeout of ${timeout}ms for a ${isHook ? 'hook' : 'test'}.`; + return new Error(message); +}; + const callAsyncFn = ( fn: AsyncFn, testContext: ?TestContext, - {isHook}: {isHook?: boolean} = {isHook: false}, + { + isHook, + timeout, + test, + }: {isHook?: ?boolean, timeout: number, test?: TestEntry}, ): Promise => { - // If this fn accepts `done` callback we return a promise that fullfills as - // soon as `done` called. - if (fn.length) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { + setTimeout(() => reject(_makeTimeoutMessage(timeout, isHook)), timeout); + + // If this fn accepts `done` callback we return a promise that fullfills as + // soon as `done` called. + if (fn.length) { const done = (reason?: Error | string): void => reason ? reject(reason) : resolve(); - fn.call(testContext, done); - }); - } + return fn.call(testContext, done); + } - let returnedValue; - try { - returnedValue = fn.call(testContext); - } catch (error) { - return Promise.reject(error); - } + let returnedValue; + try { + returnedValue = fn.call(testContext); + } catch (error) { + return reject(error); + } - // If it's a Promise, return it. - if (returnedValue instanceof Promise) { - return returnedValue; - } + // If it's a Promise, return it. + if (returnedValue instanceof Promise) { + return returnedValue.then(resolve).catch(reject); + } - if (!isHook && returnedValue !== void 0) { - throw new Error( - ` + if (!isHook && returnedValue !== void 0) { + return reject( + new Error( + ` test functions can only return Promise or undefined. Returned value: ${String(returnedValue)} `, - ); - } + ), + ); + } - // Otherwise this test is synchronous, and if it didn't throw it means - // it passed. - return Promise.resolve(); + // Otherwise this test is synchronous, and if it didn't throw it means + // it passed. + return resolve(); + }); }; const getTestDuration = (test: TestEntry): ?number => {