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

Migrate expect to typescript #7919

Merged
merged 48 commits into from Feb 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3e04c16
Rename src files
natealcedo Feb 17, 2019
43320f3
Migrate utils.ts
natealcedo Feb 17, 2019
bf35c81
Add tsconfig
natealcedo Feb 17, 2019
110729b
Add jest-types reference
natealcedo Feb 17, 2019
6ce71f6
Implement types.ts for expect
natealcedo Feb 17, 2019
fc978a6
Add a types key in package.json
natealcedo Feb 17, 2019
221074c
Migrate toThrowMatchers
natealcedo Feb 17, 2019
1cd1006
Migrate spyMatchers
natealcedo Feb 17, 2019
9e0d8e6
Migrate matchers
natealcedo Feb 17, 2019
ab7d1e1
Migrate jasmineUtils
natealcedo Feb 17, 2019
73042ba
migrate fakechalk
natealcedo Feb 17, 2019
30efca3
Migrate extractExpectedAssertionsErrors
natealcedo Feb 17, 2019
032c6ee
Migrate asymmetricMatchers
natealcedo Feb 17, 2019
f4f5536
Migrate jestMatchersObject
natealcedo Feb 17, 2019
bf8198c
Upate Expectation object definition
natealcedo Feb 17, 2019
a62f3a4
Migrate expect/index.ts
natealcedo Feb 17, 2019
428620d
Add a diff file since it wont fit in the PR description
natealcedo Feb 17, 2019
1d9fe02
Run eslint
natealcedo Feb 17, 2019
feb8e86
Declare jest types as a depenency
natealcedo Feb 17, 2019
b221710
import type localy
natealcedo Feb 17, 2019
4258222
Sort dependencies in package.json
natealcedo Feb 18, 2019
2801634
Change returnInput argument type
natealcedo Feb 18, 2019
88cb209
Type syncResult and asyncResult
natealcedo Feb 18, 2019
8ab2dae
Remove MatcherHintOptions in types.ts
natealcedo Feb 18, 2019
59ab17d
Type this in matchers to use MatcherState and options to use MatcherH…
natealcedo Feb 18, 2019
a3e1d44
Remove flow declaration
natealcedo Feb 18, 2019
a48b31f
Better type asmmetricMathchers
natealcedo Feb 18, 2019
27b2049
Export Tester type
natealcedo Feb 18, 2019
282b15e
Improve types for jasmineUtils
natealcedo Feb 18, 2019
2d41e89
Improve types for jestMatchersObject
natealcedo Feb 18, 2019
dddf3d2
Improve typings for matchers
natealcedo Feb 18, 2019
d99c26c
Improve spyMatchers typings
natealcedo Feb 18, 2019
7f5575d
Improve types for toThrowMatchers
natealcedo Feb 18, 2019
a8ff374
Improve types for utils.ts
natealcedo Feb 18, 2019
7f2aef4
Update diff file
natealcedo Feb 18, 2019
602e34c
Add a typeguard for jasmineUtils
natealcedo Feb 19, 2019
bf61f89
Update createToThrowErrorMatchingSnapshotMatcher typing
natealcedo Feb 19, 2019
4ede592
Type getMessage and isPromise
natealcedo Feb 19, 2019
72feb3e
Type case matcher call
natealcedo Feb 19, 2019
bf24385
tweak
SimenB Feb 19, 2019
db1389d
remove diff file
SimenB Feb 19, 2019
90dffa8
tweak promise type guard
SimenB Feb 19, 2019
cc42c7c
revert ignorefile change
SimenB Feb 19, 2019
8fef3e4
prettier on jasmine utils
SimenB Feb 19, 2019
fbb94e3
fix copyright header
SimenB Feb 20, 2019
a432bb5
Merge branch 'master' into migrate/expect
SimenB Feb 20, 2019
b51b16a
update changelog
SimenB Feb 20, 2019
e14c798
small type adjustments; use jestMatcherUtils for MatcherState
thymikee Feb 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
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
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
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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed so that we don't have to re-declare this property the matchers inheriting AsymmetricMatcher


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) {
SimenB marked this conversation as resolved.
Show resolved Hide resolved
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
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
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
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;