Skip to content

Commit

Permalink
Add support for async matchers
Browse files Browse the repository at this point in the history
  • Loading branch information
bilby91 committed Apr 3, 2018
1 parent 0d58623 commit 54cdc2d
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 29 deletions.
10 changes: 10 additions & 0 deletions packages/expect/src/__tests__/matchers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,16 @@ describe('.toMatch()', () => {
});
});

describe('.toHaveLengthAsync', () => {
[[[1, 2], 2]].forEach(([received, length]) => {
test(`{pass: true} expect(${stringify(
received,
)}).toHaveLengthAsync(${length})`, async () => {
await jestExpect(received).toHaveLengthAsync(Promise.resolve(length));
});
});
});

describe('.toHaveLength', () => {
[[[1, 2], 2], [[], 0], [['a', 'b'], 2], ['abc', 3], ['', 0]].forEach(
([received, length]) => {
Expand Down
66 changes: 39 additions & 27 deletions packages/expect/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type {
Expect,
ExpectationObject,
SyncExpectationResult,
ExpectationResult,
MatcherState,
MatchersObject,
Expand Down Expand Up @@ -212,10 +213,46 @@ const makeThrowingMatcher = (
utils,
},
);
let result: ExpectationResult;

const processResult = (result: SyncExpectationResult) => {
_validateResult(result);

getState().assertionCalls++;

if ((result.pass && isNot) || (!result.pass && !isNot)) {
// XOR
const message = getMessage(result.message);
const error = new JestAssertionError(message);
// Passing the result of the matcher with the error so that a custom
// reporter could access the actual and expected objects of the result
// for example in order to display a custom visual diff
error.matcherResult = result;
// Try to remove this function from the stack trace frame.
// Guard for some environments (browsers) that do not support this feature.
if (Error.captureStackTrace) {
Error.captureStackTrace(error, throwingMatcher);
}

if (throws) {
throw error;
} else {
getState().suppressedErrors.push(error);
}
}
};

let potentialResult: ExpectationResult;

try {
result = matcher.apply(matcherContext, [actual].concat(args));
potentialResult = matcher.apply(matcherContext, [actual].concat(args));

if (isPromise(potentialResult)) {
potentialResult.then(aResult => {
processResult(aResult);
});
} else {
processResult(potentialResult);
}
} catch (error) {
if (
matcher[INTERNAL_MATCHER_FLAG] === true &&
Expand All @@ -229,31 +266,6 @@ const makeThrowingMatcher = (
}
throw error;
}

_validateResult(result);

getState().assertionCalls++;

if ((result.pass && isNot) || (!result.pass && !isNot)) {
// XOR
const message = getMessage(result.message);
const error = new JestAssertionError(message);
// Passing the result of the matcher with the error so that a custom
// reporter could access the actual and expected objects of the result
// for example in order to display a custom visual diff
error.matcherResult = result;
// Try to remove this function from the stack trace frame.
// Guard for some environments (browsers) that do not support this feature.
if (Error.captureStackTrace) {
Error.captureStackTrace(error, throwingMatcher);
}

if (throws) {
throw error;
} else {
getState().suppressedErrors.push(error);
}
}
};
};

Expand Down
43 changes: 43 additions & 0 deletions packages/expect/src/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,49 @@ const matchers: MatchersObject = {
return {message, pass};
},

async toHaveLengthAsync(received: any, lengthPromise: Promise<number>) {
const length = await lengthPromise;

if (
typeof received !== 'string' &&
(!received || typeof received.length !== 'number')
) {
throw new Error(
matcherHint('[.not].toHaveLength', 'received', 'length') +
'\n\n' +
`Expected value to have a 'length' property that is a number. ` +
`Received:\n` +
` ${printReceived(received)}\n` +
(received
? `received.length:\n ${printReceived(received.length)}`
: ''),
);
}

const pass = received.length === length;
const message = pass
? () =>
matcherHint('.not.toHaveLength', 'received', 'length') +
'\n\n' +
`Expected value to not have length:\n` +
` ${printExpected(length)}\n` +
`Received:\n` +
` ${printReceived(received)}\n` +
`received.length:\n` +
` ${printReceived(received.length)}`
: () =>
matcherHint('.toHaveLength', 'received', 'length') +
'\n\n' +
`Expected value to have length:\n` +
` ${printExpected(length)}\n` +
`Received:\n` +
` ${printReceived(received)}\n` +
`received.length:\n` +
` ${printReceived(received.length)}`;

return {message, pass};
},

toHaveProperty(object: Object, keyPath: string | Array<any>, value?: any) {
const valuePassed = arguments.length === 3;
const secondArgument = valuePassed ? 'value' : null;
Expand Down
6 changes: 5 additions & 1 deletion types/Matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import type {Path} from 'types/Config';
import type {SnapshotState} from 'jest-snapshot';

export type ExpectationResult = {
export type SyncExpectationResult = {
pass: boolean,
message: () => string,
};

export type AsyncExpectationResult = Promise<SyncExpectationResult>;

export type ExpectationResult = SyncExpectationResult | AsyncExpectationResult;

export type RawMatcherFn = (
expected: any,
actual: any,
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8445,7 +8445,7 @@ supports-color@^3.1.2:
dependencies:
has-flag "^1.0.0"

supports-color@^5.2.0, supports-color@^5.3.0:
supports-color@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
dependencies:
Expand Down

0 comments on commit 54cdc2d

Please sign in to comment.