Skip to content

Commit

Permalink
Migrate expect to typescript (#7919)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alcedo Nathaniel De Guzman Jr authored and SimenB committed Feb 20, 2019
1 parent b8a9a71 commit be2703c
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 248 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- `[@jest/transform]`: Migrate to TypeScript ([#7918](https://github.com/facebook/jest/pull/7918))
- `[docs]` Add missing import to docs ([#7928](https://github.com/facebook/jest/pull/7928))
- `[jest-resolve-dependencies]`: Migrate to TypeScript ([#7922](https://github.com/facebook/jest/pull/7922))
- `[expect]`: Migrate to TypeScript ([#7919](https://github.com/facebook/jest/pull/7919))

### Performance

Expand Down
2 changes: 2 additions & 0 deletions packages/expect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
},
"license": "MIT",
"main": "build/index.js",
"types": "build/index.d.ts",
"browser": "build-es5/index.js",
"dependencies": {
"@jest/types": "^24.1.0",
"ansi-styles": "^3.2.0",
"jest-get-type": "^24.0.0",
"jest-matcher-utils": "^24.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,35 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {equals, fnNameFor, hasProperty, isA, isUndefined} from './jasmineUtils';

import {emptyObject} from './utils';

export class AsymmetricMatcher {
export class AsymmetricMatcher<T> {
protected sample: T;
$$typeof: Symbol;
inverse: boolean;
inverse?: boolean;

constructor() {
constructor(sample: T) {
this.$$typeof = Symbol.for('jest.asymmetricMatcher');
this.sample = sample;
}
}

class Any extends AsymmetricMatcher {
sample: any;

constructor(sample: any) {
super();
class Any extends AsymmetricMatcher<any> {
constructor(sample: unknown) {
if (typeof sample === 'undefined') {
throw new TypeError(
'any() expects to be passed a constructor function. ' +
'Please pass one or use anything() to match any object.',
);
}
this.sample = sample;
super(sample);
}

asymmetricMatch(other: any) {
asymmetricMatch(other: unknown) {
if (this.sample == String) {
return typeof other == 'string' || other instanceof String;
}
Expand Down Expand Up @@ -91,8 +89,8 @@ class Any extends AsymmetricMatcher {
}
}

class Anything extends AsymmetricMatcher {
asymmetricMatch(other: any) {
class Anything extends AsymmetricMatcher<void> {
asymmetricMatch(other: unknown) {
return !isUndefined(other) && other !== null;
}

Expand All @@ -107,16 +105,13 @@ class Anything extends AsymmetricMatcher {
}
}

class ArrayContaining extends AsymmetricMatcher {
sample: Array<any>;

constructor(sample: Array<any>, inverse: boolean = false) {
super();
this.sample = sample;
class ArrayContaining extends AsymmetricMatcher<Array<unknown>> {
constructor(sample: Array<unknown>, inverse: boolean = false) {
super(sample);
this.inverse = inverse;
}

asymmetricMatch(other: Array<any>) {
asymmetricMatch(other: Array<unknown>) {
if (!Array.isArray(this.sample)) {
throw new Error(
`You must provide an array to ${this.toString()}, not '` +
Expand Down Expand Up @@ -144,16 +139,13 @@ class ArrayContaining extends AsymmetricMatcher {
}
}

class ObjectContaining extends AsymmetricMatcher {
sample: Object;

class ObjectContaining extends AsymmetricMatcher<Object> {
constructor(sample: Object, inverse: boolean = false) {
super();
this.sample = sample;
super(sample);
this.inverse = inverse;
}

asymmetricMatch(other: Object) {
asymmetricMatch(other: any) {
if (typeof this.sample !== 'object') {
throw new Error(
`You must provide an object to ${this.toString()}, not '` +
Expand All @@ -166,8 +158,8 @@ class ObjectContaining extends AsymmetricMatcher {
for (const property in this.sample) {
if (
hasProperty(other, property) &&
equals(this.sample[property], other[property]) &&
!emptyObject(this.sample[property]) &&
equals((this.sample as any)[property], other[property]) &&
!emptyObject((this.sample as any)[property]) &&
!emptyObject(other[property])
) {
return false;
Expand All @@ -179,7 +171,7 @@ class ObjectContaining extends AsymmetricMatcher {
for (const property in this.sample) {
if (
!hasProperty(other, property) ||
!equals(this.sample[property], other[property])
!equals((this.sample as any)[property], other[property])
) {
return false;
}
Expand All @@ -198,19 +190,16 @@ class ObjectContaining extends AsymmetricMatcher {
}
}

class StringContaining extends AsymmetricMatcher {
sample: string;

class StringContaining extends AsymmetricMatcher<string> {
constructor(sample: string, inverse: boolean = false) {
super();
if (!isA('String', sample)) {
throw new Error('Expected is not a string');
}
this.sample = sample;
super(sample);
this.inverse = inverse;
}

asymmetricMatch(other: any) {
asymmetricMatch(other: string) {
const result = isA('String', other) && other.includes(this.sample);

return this.inverse ? !result : result;
Expand All @@ -225,20 +214,17 @@ class StringContaining extends AsymmetricMatcher {
}
}

class StringMatching extends AsymmetricMatcher {
sample: RegExp;

class StringMatching extends AsymmetricMatcher<RegExp> {
constructor(sample: string | RegExp, inverse: boolean = false) {
super();
if (!isA('String', sample) && !isA('RegExp', sample)) {
throw new Error('Expected is not a String or a RegExp');
}
super(new RegExp(sample));

this.sample = new RegExp(sample);
this.inverse = inverse;
}

asymmetricMatch(other: any) {
asymmetricMatch(other: string) {
const result = isA('String', other) && this.sample.test(other);

return this.inverse ? !result : result;
Expand All @@ -255,9 +241,9 @@ class StringMatching extends AsymmetricMatcher {

export const any = (expectedObject: any) => new Any(expectedObject);
export const anything = () => new Anything();
export const arrayContaining = (sample: Array<any>) =>
export const arrayContaining = (sample: Array<unknown>) =>
new ArrayContaining(sample);
export const arrayNotContaining = (sample: Array<any>) =>
export const arrayNotContaining = (sample: Array<unknown>) =>
new ArrayContaining(sample, true);
export const objectContaining = (sample: Object) =>
new ObjectContaining(sample);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @flow
*/

import ansiStyles from 'ansi-styles';

const returnInput = str => str;
const returnInput = (str: string) => str;

const allColorsAsFunc = Object.keys(ansiStyles)
.map(style => ({[style]: returnInput}))
Expand All @@ -21,4 +20,4 @@ Object.keys(allColorsAsFunc)
Object.assign(returnInput, style);
});

module.exports = allColorsAsFunc;
export = allColorsAsFunc;
60 changes: 34 additions & 26 deletions packages/expect/src/index.js → packages/expect/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {
Expect,
ExpectationObject,
import * as matcherUtils from 'jest-matcher-utils';
import {
AsyncExpectationResult,
SyncExpectationResult,
ExpectationResult,
Expand All @@ -18,9 +16,10 @@ import type {
RawMatcherFn,
ThrowingMatcherFn,
PromiseMatcherFn,
} from 'types/Matchers';
ExpectationObject,
Expect,
} from './types';

import * as matcherUtils from 'jest-matcher-utils';
import {iterableEquality, subsetEquality} from './utils';
import matchers from './matchers';
import spyMatchers from './spyMatchers';
Expand Down Expand Up @@ -50,21 +49,27 @@ import {
import extractExpectedAssertionsErrors from './extractExpectedAssertionsErrors';

class JestAssertionError extends Error {
matcherResult: any;
matcherResult?: SyncExpectationResult;
}

const isPromise = obj =>
const isPromise = <T extends any>(obj: any): obj is PromiseLike<T> =>
!!obj &&
(typeof obj === 'object' || typeof obj === 'function') &&
typeof obj.then === 'function';

const createToThrowErrorMatchingSnapshotMatcher = function(matcher) {
return function(received: any, testNameOrInlineSnapshot?: string) {
const createToThrowErrorMatchingSnapshotMatcher = function(
matcher: RawMatcherFn,
) {
return function(
this: MatcherState,
received: any,
testNameOrInlineSnapshot?: string,
) {
return matcher.apply(this, [received, testNameOrInlineSnapshot, true]);
};
};

const getPromiseMatcher = (name, matcher) => {
const getPromiseMatcher = (name: string, matcher: any) => {
if (name === 'toThrow' || name === 'toThrowError') {
return createThrowMatcher(name, true);
} else if (
Expand All @@ -77,13 +82,13 @@ const getPromiseMatcher = (name, matcher) => {
return null;
};

const expect = (actual: any, ...rest): ExpectationObject => {
const expect: any = (actual: any, ...rest: Array<any>): ExpectationObject => {
if (rest.length !== 0) {
throw new Error('Expect takes at most one argument.');
}

const allMatchers = getMatchers();
const expectation = {
const expectation: any = {
not: {},
rejects: {not: {}},
resolves: {not: {}},
Expand Down Expand Up @@ -131,7 +136,7 @@ const expect = (actual: any, ...rest): ExpectationObject => {
return expectation;
};

const getMessage = message =>
const getMessage = (message?: () => string) =>
(message && message()) ||
matcherUtils.RECEIVED_COLOR('No message was specified for this matcher.');

Expand Down Expand Up @@ -294,7 +299,7 @@ const makeThrowingMatcher = (

const handlError = (error: Error) => {
if (
matcher[INTERNAL_MATCHER_FLAG] === true &&
(matcher as any)[INTERNAL_MATCHER_FLAG] === true &&
!(error instanceof JestAssertionError) &&
error.name !== 'PrettyFormatPluginError' &&
// Guard for some environments (browsers) that do not support this feature.
Expand All @@ -309,10 +314,13 @@ const makeThrowingMatcher = (
let potentialResult: ExpectationResult;

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

if (isPromise((potentialResult: any))) {
const asyncResult = ((potentialResult: any): AsyncExpectationResult);
if (isPromise(potentialResult)) {
const asyncResult = potentialResult as AsyncExpectationResult;
const asyncError = new JestAssertionError();
if (Error.captureStackTrace) {
Error.captureStackTrace(asyncError, throwingMatcher);
Expand All @@ -322,7 +330,7 @@ const makeThrowingMatcher = (
.then(aResult => processResult(aResult, asyncError))
.catch(error => handlError(error));
} else {
const syncResult = ((potentialResult: any): SyncExpectationResult);
const syncResult = potentialResult as SyncExpectationResult;

return processResult(syncResult);
}
Expand All @@ -332,7 +340,7 @@ const makeThrowingMatcher = (
};

expect.extend = (matchers: MatchersObject): void =>
setMatchers(matchers, false, expect);
setMatchers(matchers, false, expect as any);

expect.anything = anything;
expect.any = any;
Expand All @@ -349,7 +357,7 @@ expect.arrayContaining = arrayContaining;
expect.stringContaining = stringContaining;
expect.stringMatching = stringMatching;

const _validateResult = result => {
const _validateResult = (result: any) => {
if (
typeof result !== 'object' ||
typeof result.pass !== 'boolean' ||
Expand All @@ -376,7 +384,7 @@ function assertions(expected: number) {
getState().expectedAssertionsNumber = expected;
getState().expectedAssertionsNumberError = error;
}
function hasAssertions(...args) {
function hasAssertions(...args: Array<any>) {
const error = new Error();
if (Error.captureStackTrace) {
Error.captureStackTrace(error, hasAssertions);
Expand All @@ -388,9 +396,9 @@ function hasAssertions(...args) {
}

// add default jest matchers
setMatchers(matchers, true, expect);
setMatchers(spyMatchers, true, expect);
setMatchers(toThrowMatchers, true, expect);
setMatchers(matchers, true, expect as Expect);
setMatchers(spyMatchers, true, expect as Expect);
setMatchers(toThrowMatchers, true, expect as Expect);

expect.addSnapshotSerializer = () => void 0;
expect.assertions = assertions;
Expand All @@ -399,4 +407,4 @@ expect.getState = getState;
expect.setState = setState;
expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors;

module.exports = (expect: Expect);
export = expect as Expect;
Loading

0 comments on commit be2703c

Please sign in to comment.