Skip to content

Commit

Permalink
jest-circus test failures
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronabramov committed Jun 8, 2017
1 parent 8719053 commit 005140f
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ exports[`not throwing Error objects 4`] = `
Expected two assertions to be called but only received one assertion call.
at ../../packages/jest-jasmine2/build/setup-jest-globals.js:68:21
● .assertions() › throws on redeclare of assertion count
Expand All @@ -62,15 +61,13 @@ exports[`not throwing Error objects 4`] = `
Expected zero assertions to be called but only received one assertion call.
at ../../packages/jest-jasmine2/build/setup-jest-globals.js:68:21
● .hasAssertions() › throws when there are not assertions
expect.hasAssertions()
Expected at least one assertion to be called but received none.
at ../../packages/jest-jasmine2/build/setup-jest-globals.js:88:21
.assertions()
✕ throws
Expand Down
21 changes: 20 additions & 1 deletion integration_tests/__tests__/failures-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ const normalizeDots = text => text.replace(/\.{1,}$/gm, '.');

skipOnWindows.suite();

const cleanupStackTrace = stderr => {
const STACK_REGEXP = /at.*(setup-jest-globals|extractExpectedAssertionsErrors).*\n/gm;
if (!STACK_REGEXP.test(stderr)) {
throw new Error(
`
This function is used to remove inconsistent stack traces between
jest-jasmine2 and jest-circus. If you see this error, that means the
stack traces are no longer inconsistent and this function can be
safely removed.
output:
${stderr}
`,
);
}

return stderr.replace(STACK_REGEXP, '');
};

test('not throwing Error objects', () => {
let stderr;
stderr = runJest(dir, ['throw-number-test.js']).stderr;
Expand All @@ -26,7 +45,7 @@ test('not throwing Error objects', () => {
stderr = runJest(dir, ['throw-object-test.js']).stderr;
expect(extractSummary(stderr).rest).toMatchSnapshot();
stderr = runJest(dir, ['assertion-count-test.js']).stderr;
expect(extractSummary(stderr).rest).toMatchSnapshot();
expect(extractSummary(cleanupStackTrace(stderr)).rest).toMatchSnapshot();
});

test('works with node assert', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-circus/src/formatNodeAssertErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @flow
*/

import type {DiffOptions} from 'jest-diff/src/diffStrings';
import type {DiffOptions} from 'jest-diff/src/diffStrings';
import type {Event, State} from '../types';

const {printReceived, printExpected} = require('jest-matcher-utils');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import type {TestResult, Status} from 'types/TestResult';
import type {GlobalConfig, Path, ProjectConfig} from 'types/Config';
import type {Event, TestEntry} from '../../types';

const {getState, setState} = require('jest-matchers');
const {
extractExpectedAssertionsErrors,
getState,
setState,
} = require('jest-matchers');
const {formatResultsErrors} = require('jest-message-util');
const {SnapshotState, addSerializer} = require('jest-snapshot');
const {addEventHandler, ROOT_DESCRIBE_BLOCK_NAME} = require('../state');
Expand Down Expand Up @@ -157,11 +161,18 @@ const eventHandler = (event: Event) => {
case 'test_success':
case 'test_failure': {
_addSuppressedErrors(event.test);
_addExpectedAssertionErrors(event.test);
break;
}
}
};

const _addExpectedAssertionErrors = (test: TestEntry) => {
const errors = extractExpectedAssertionsErrors();
errors.length && (test.status = 'fail');
test.errors = test.errors.concat(errors);
};

// Get suppressed errors from ``jest-matchers`` that weren't throw during
// test execution and add them to the test result, potentially failing
// a passing test.
Expand Down
64 changes: 64 additions & 0 deletions packages/jest-matchers/src/extractExpectedAssertionsErrors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

const {
EXPECTED_COLOR,
RECEIVED_COLOR,
matcherHint,
pluralize,
} = require('jest-matcher-utils');

const {getState, setState} = require('./global_object');

// Create and format all errors related to the mismatched number of `expect`
// calls and reset the matchers state.
const extractExpectedAssertionsErrors = () => {
const result = [];
const {
assertionCalls,
expectedAssertionsNumber,
isExpectingAssertions,
} = getState();
setState({assertionCalls: 0, expectedAssertionsNumber: null});
if (
typeof expectedAssertionsNumber === 'number' &&
assertionCalls !== expectedAssertionsNumber
) {
const numOfAssertionsExpected = EXPECTED_COLOR(
pluralize('assertion', expectedAssertionsNumber),
);
const error = new Error(
matcherHint('.assertions', '', String(expectedAssertionsNumber), {
isDirectExpectCall: true,
}) +
'\n\n' +
`Expected ${numOfAssertionsExpected} to be called but only received ` +
RECEIVED_COLOR(pluralize('assertion call', assertionCalls || 0)) +
'.',
);
result.push(error);
}
if (isExpectingAssertions && assertionCalls === 0) {
const expected = EXPECTED_COLOR('at least one assertion');
const received = RECEIVED_COLOR('received none');
const error = new Error(
matcherHint('.hasAssertions', '', '', {
isDirectExpectCall: true,
}) +
'\n\n' +
`Expected ${expected} to be called but ${received}.`,
);
result.push(error);
}

return result;
};

module.exports = extractExpectedAssertionsErrors;
48 changes: 48 additions & 0 deletions packages/jest-matchers/src/global_object.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

import type {MatchersObject} from 'types/Matchers';

// Global matchers object holds the list of available matchers and
// the state, that can hold matcher specific values that change over time.
const JEST_MATCHERS_OBJECT = Symbol.for('$$jest-matchers-object');

if (!global[JEST_MATCHERS_OBJECT]) {
Object.defineProperty(global, JEST_MATCHERS_OBJECT, {
value: {
matchers: Object.create(null),
state: {
assertionCalls: 0,
expectedAssertionsNumber: null,
isExpectingAssertions: false,
suppressedErrors: [], // errors that are not thrown immediately.
},
},
});
}

const getState = () => global[JEST_MATCHERS_OBJECT].state;

const setState = (state: Object) => {
Object.assign(global[JEST_MATCHERS_OBJECT].state, state);
};

const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers;

const setMatchers = (matchers: MatchersObject) => {
Object.assign(global[JEST_MATCHERS_OBJECT].matchers, matchers);
};

module.exports = {
getMatchers,
getState,
setMatchers,
setState,
};
46 changes: 17 additions & 29 deletions packages/jest-matchers/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ import type {
} from 'types/Matchers';

const utils = require('jest-matcher-utils');
const extractExpectedAssertionsErrors = require('./extractExpectedAssertionsErrors');
const {
getMatchers,
getState,
setMatchers,
setState,
} = require('./global_object');
const matchers = require('./matchers');
const spyMatchers = require('./spyMatchers');
const toThrowMatchers = require('./toThrowMatchers');
Expand All @@ -33,8 +40,6 @@ const {
stringMatching,
} = require('./asymmetric-matchers');

const GLOBAL_STATE = Symbol.for('$$jest-matchers-object');

class JestAssertionError extends Error {
matcherResult: any;
}
Expand All @@ -47,22 +52,8 @@ const isPromise = obj => {
);
};

if (!global[GLOBAL_STATE]) {
Object.defineProperty(global, GLOBAL_STATE, {
value: {
matchers: Object.create(null),
state: {
assertionCalls: 0,
expectedAssertionsNumber: null,
isExpectingAssertions: false,
suppressedErrors: [],
},
},
});
}

const expect = (actual: any): ExpectationObject => {
const allMatchers = global[GLOBAL_STATE].matchers;
const allMatchers = getMatchers();
const expectation = {
not: {},
rejects: {not: {}},
Expand Down Expand Up @@ -199,7 +190,7 @@ const makeThrowingMatcher = (
// snapshot matcher uses it because we want to log all snapshot
// failures in a test.
{dontThrow: () => (throws = false)},
global[GLOBAL_STATE].state,
getState(),
{
equals,
isNot,
Expand All @@ -218,7 +209,7 @@ const makeThrowingMatcher = (

_validateResult(result);

global[GLOBAL_STATE].state.assertionCalls++;
getState().assertionCalls++;

if ((result.pass && isNot) || (!result.pass && !isNot)) {
// XOR
Expand All @@ -234,15 +225,13 @@ const makeThrowingMatcher = (
if (throws) {
throw error;
} else {
global[GLOBAL_STATE].state.suppressedErrors.push(error);
getState().suppressedErrors.push(error);
}
}
};
};

expect.extend = (matchers: MatchersObject): void => {
Object.assign(global[GLOBAL_STATE].matchers, matchers);
};
expect.extend = (matchers: MatchersObject): void => setMatchers(matchers);

expect.anything = anything;
expect.any = any;
Expand Down Expand Up @@ -276,15 +265,14 @@ expect.extend(toThrowMatchers);

expect.addSnapshotSerializer = () => void 0;
expect.assertions = (expected: number) => {
global[GLOBAL_STATE].state.expectedAssertionsNumber = expected;
getState().expectedAssertionsNumber = expected;
};
expect.hasAssertions = expected => {
utils.ensureNoExpected(expected, '.hasAssertions');
global[GLOBAL_STATE].state.isExpectingAssertions = true;
};
expect.setState = (state: Object) => {
Object.assign(global[GLOBAL_STATE].state, state);
getState().isExpectingAssertions = true;
};
expect.getState = () => global[GLOBAL_STATE].state;
expect.getState = getState;
expect.setState = setState;
expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors;

module.exports = (expect: Expect);
1 change: 1 addition & 0 deletions types/Matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type Expect = {
addSnapshotSerializer(any): void,
assertions(number): void,
extend(any): void,
extractExpectedAssertionsErrors(): Array<Error>,
getState(): MatcherState,
hasAssertions(): void,
setState(Object): void,
Expand Down

0 comments on commit 005140f

Please sign in to comment.