Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jest-circus Timeouts #3760

Merged
merged 2 commits into from
Jun 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/en/JestObjectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The `jest` object is automatically in scope within every test file. The methods
- [`jest.runTimersToTime(msToRun)`](#jestruntimerstotimemstorun)
- [`jest.runOnlyPendingTimers()`](#jestrunonlypendingtimers)
- [`jest.setMock(moduleName, moduleExports)`](#jestsetmockmodulename-moduleexports)
- [`jest.setTestTimeout(timeout)`](#jestsettesttimeouttimeout)
- [`jest.setTimeout(timeout)`](#jestsettimeouttimeout)
- [`jest.unmock(moduleName)`](#jestunmockmodulename)
- [`jest.useFakeTimers()`](#jestusefaketimers)
- [`jest.useRealTimers()`](#jestuserealtimers)
Expand Down Expand Up @@ -205,16 +205,16 @@ Returns the `jest` object for chaining.

*Note It is recommended to use [`jest.mock()`](#jestmockmodulename-factory-options) instead. The `jest.mock` API's second argument is a module factory instead of the expected exported module object.*

### `jest.setTestTimeout(timeout)`
### `jest.setTimeout(timeout)`

Set the default timeout interval for tests in milliseconds.
Set the default timeout interval for tests and before/after hooks in milliseconds.

*Note: The default timeout interval is 5 seconds if this method is not called.*

Example:

```js
jest.setTestTimeout(1000); // 1 second
jest.setTimeout(1000); // 1 second
```

### `jest.unmock(moduleName)`
Expand Down
4 changes: 2 additions & 2 deletions docs/en/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ Consider replacing the global promise implementation with your own, for example
used Promise libraries to a single one.

If your test is long running, you may want to consider to increase the timeout
by calling `jest.setTestTimeout`
by calling `jest.setTimeout`

```
jest.setTestTimeout(10000); // 10 second timeout
jest.setTimeout(10000); // 10 second timeout
```

### Watchman Issues
Expand Down
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
10 changes: 5 additions & 5 deletions integration_tests/__tests__/timeouts-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ afterAll(() => cleanup(DIR));
test('exceeds the timeout', () => {
writeFiles(DIR, {
'__tests__/a-banana.js': `
jest.setTestTimeout(20);
jest.setTimeout(20);

test('banana', () => {
return new Promise(resolve => {
Expand All @@ -36,15 +36,15 @@ 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', () => {
writeFiles(DIR, {
'__tests__/a-banana.js': `
jest.setTestTimeout(100);
jest.setTimeout(100);

test('banana', () => {
return new Promise(resolve => {
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
1 change: 0 additions & 1 deletion packages/jest-circus/src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import type {Event, State, EventHandler} from '../types';


const {makeDescribe} = require('./utils');
const eventHandler = require('./eventHandler');

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) =>
new Error(
`Exceeded timeout of ${timeout}ms for a ${isHook ? 'hook' : 'test'}.\nUse jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test.`,
);

const callAsyncFn = (
fn: AsyncFn,
testContext: ?TestContext,
{isHook}: {isHook?: boolean} = {isHook: false},
{
isHook,
test,
timeout,
}: {isHook?: ?boolean, test?: TestEntry, timeout: number},
): 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, 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
2 changes: 1 addition & 1 deletion packages/jest-cli/src/__tests__/SearchSource-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/
'use strict';

jest.setTestTimeout(15000);
jest.setTimeout(15000);

const path = require('path');
const skipOnWindows = require('skipOnWindows');
Expand Down
4 changes: 2 additions & 2 deletions packages/jest-runtime/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ class Runtime {
const fn = this._moduleMocker.fn.bind(this._moduleMocker);
const spyOn = this._moduleMocker.spyOn.bind(this._moduleMocker);

const setTestTimeout = (timeout: number) => {
const setTimeout = (timeout: number) => {
this._environment.global.jasmine
? (this._environment.global.jasmine.DEFAULT_TIMEOUT_INTERVAL = timeout)
: (this._environment.global[
Expand Down Expand Up @@ -758,7 +758,7 @@ class Runtime {

setMock: (moduleName: string, mock: Object) =>
setMockFactory(moduleName, () => mock),
setTestTimeout,
setTimeout,
spyOn,

unmock,
Expand Down
2 changes: 1 addition & 1 deletion testSetupFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const jasmineReporters = require('jasmine-reporters');

// Some of the `jest-runtime` tests are very slow and cause
// timeouts on travis
jest.setTestTimeout(70000);
jest.setTimeout(70000);

if (global.jasmine && process.env.APPVEYOR_API_URL) {
// Running on AppVeyor, add the custom reporter.
Expand Down