Skip to content

Commit

Permalink
jest-circus Timeouts
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronabramov committed Jun 7, 2017
1 parent 7c3cef7 commit 41beefa
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 54 deletions.
13 changes: 0 additions & 13 deletions integration_tests/__tests__/__snapshots__/timeouts-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions integration_tests/__tests__/timeouts-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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);
});
31 changes: 19 additions & 12 deletions packages/jest-circus/src/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ const _runTestsForDescribeBlock = async (describeBlock: DescribeBlock) => {

const _runTest = async (test: TestEntry): Promise<void> => {
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) {
Expand All @@ -69,27 +79,24 @@ const _runTest = async (test: TestEntry): Promise<void> => {

const _callHook = (hook: Hook, testContext?: TestContext): Promise<any> => {
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<any> => {
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<any> => {
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}));
};
Expand Down
65 changes: 39 additions & 26 deletions packages/jest-circus/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> => {
// 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 => {
Expand Down

0 comments on commit 41beefa

Please sign in to comment.