diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dfdb20b44cc..570249abec1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/packages/expect/package.json b/packages/expect/package.json index 3f74a119c97e..1cf724969380 100644 --- a/packages/expect/package.json +++ b/packages/expect/package.json @@ -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", diff --git a/packages/expect/src/asymmetricMatchers.js b/packages/expect/src/asymmetricMatchers.ts similarity index 81% rename from packages/expect/src/asymmetricMatchers.js rename to packages/expect/src/asymmetricMatchers.ts index 404cb3eb8cc9..1742cf7711ae 100644 --- a/packages/expect/src/asymmetricMatchers.js +++ b/packages/expect/src/asymmetricMatchers.ts @@ -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 { + 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 { + 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; } @@ -91,8 +89,8 @@ class Any extends AsymmetricMatcher { } } -class Anything extends AsymmetricMatcher { - asymmetricMatch(other: any) { +class Anything extends AsymmetricMatcher { + asymmetricMatch(other: unknown) { return !isUndefined(other) && other !== null; } @@ -107,16 +105,13 @@ class Anything extends AsymmetricMatcher { } } -class ArrayContaining extends AsymmetricMatcher { - sample: Array; - - constructor(sample: Array, inverse: boolean = false) { - super(); - this.sample = sample; +class ArrayContaining extends AsymmetricMatcher> { + constructor(sample: Array, inverse: boolean = false) { + super(sample); this.inverse = inverse; } - asymmetricMatch(other: Array) { + asymmetricMatch(other: Array) { if (!Array.isArray(this.sample)) { throw new Error( `You must provide an array to ${this.toString()}, not '` + @@ -144,16 +139,13 @@ class ArrayContaining extends AsymmetricMatcher { } } -class ObjectContaining extends AsymmetricMatcher { - sample: Object; - +class ObjectContaining extends AsymmetricMatcher { 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 '` + @@ -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; @@ -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; } @@ -198,19 +190,16 @@ class ObjectContaining extends AsymmetricMatcher { } } -class StringContaining extends AsymmetricMatcher { - sample: string; - +class StringContaining extends AsymmetricMatcher { 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; @@ -225,20 +214,17 @@ class StringContaining extends AsymmetricMatcher { } } -class StringMatching extends AsymmetricMatcher { - sample: RegExp; - +class StringMatching extends AsymmetricMatcher { 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; @@ -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) => +export const arrayContaining = (sample: Array) => new ArrayContaining(sample); -export const arrayNotContaining = (sample: Array) => +export const arrayNotContaining = (sample: Array) => new ArrayContaining(sample, true); export const objectContaining = (sample: Object) => new ObjectContaining(sample); diff --git a/packages/expect/src/extractExpectedAssertionsErrors.js b/packages/expect/src/extractExpectedAssertionsErrors.ts similarity index 99% rename from packages/expect/src/extractExpectedAssertionsErrors.js rename to packages/expect/src/extractExpectedAssertionsErrors.ts index 1a52a8424760..de97acc2dc4d 100644 --- a/packages/expect/src/extractExpectedAssertionsErrors.js +++ b/packages/expect/src/extractExpectedAssertionsErrors.ts @@ -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 { diff --git a/packages/expect/src/fakeChalk.js b/packages/expect/src/fakeChalk.ts similarity index 88% rename from packages/expect/src/fakeChalk.js rename to packages/expect/src/fakeChalk.ts index c113b8db0703..a01ba4d1d566 100644 --- a/packages/expect/src/fakeChalk.js +++ b/packages/expect/src/fakeChalk.ts @@ -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})) @@ -21,4 +20,4 @@ Object.keys(allColorsAsFunc) Object.assign(returnInput, style); }); -module.exports = allColorsAsFunc; +export = allColorsAsFunc; diff --git a/packages/expect/src/index.js b/packages/expect/src/index.ts similarity index 89% rename from packages/expect/src/index.js rename to packages/expect/src/index.ts index 1f50866a4e67..500b2f19945c 100644 --- a/packages/expect/src/index.js +++ b/packages/expect/src/index.ts @@ -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, @@ -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'; @@ -50,21 +49,27 @@ import { import extractExpectedAssertionsErrors from './extractExpectedAssertionsErrors'; class JestAssertionError extends Error { - matcherResult: any; + matcherResult?: SyncExpectationResult; } -const isPromise = obj => +const isPromise = (obj: any): obj is PromiseLike => !!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 ( @@ -77,13 +82,13 @@ const getPromiseMatcher = (name, matcher) => { return null; }; -const expect = (actual: any, ...rest): ExpectationObject => { +const expect: any = (actual: any, ...rest: Array): 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: {}}, @@ -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.'); @@ -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. @@ -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); @@ -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); } @@ -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; @@ -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' || @@ -376,7 +384,7 @@ function assertions(expected: number) { getState().expectedAssertionsNumber = expected; getState().expectedAssertionsNumberError = error; } -function hasAssertions(...args) { +function hasAssertions(...args: Array) { const error = new Error(); if (Error.captureStackTrace) { Error.captureStackTrace(error, hasAssertions); @@ -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; @@ -399,4 +407,4 @@ expect.getState = getState; expect.setState = setState; expect.extractExpectedAssertionsErrors = extractExpectedAssertionsErrors; -module.exports = (expect: Expect); +export = expect as Expect; diff --git a/packages/expect/src/jasmineUtils.js b/packages/expect/src/jasmineUtils.ts similarity index 91% rename from packages/expect/src/jasmineUtils.js rename to packages/expect/src/jasmineUtils.ts index 1063666af818..f9945dbf64df 100644 --- a/packages/expect/src/jasmineUtils.js +++ b/packages/expect/src/jasmineUtils.ts @@ -20,17 +20,16 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -@flow */ /* eslint-disable */ -type Tester = (a: any, b: any) => boolean | typeof undefined; +import {Tester} from './types'; // Extracted out of jasmine 2.5.2 export function equals( - a: any, - b: any, + a: unknown, + b: unknown, customTesters?: Array, strictCheck?: boolean, ): boolean { @@ -38,11 +37,11 @@ export function equals( return eq(a, b, [], [], customTesters, strictCheck ? hasKey : hasDefinedKey); } -function isAsymmetric(obj) { +function isAsymmetric(obj: any) { return !!obj && isA('Function', obj.asymmetricMatch); } -function asymmetricMatch(a, b) { +function asymmetricMatch(a: any, b: any) { var asymmetricA = isAsymmetric(a), asymmetricB = isAsymmetric(b); @@ -62,7 +61,14 @@ function asymmetricMatch(a, b) { // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) -function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { +function eq( + a: any, + b: any, + aStack: any, + bStack: any, + customTesters: any, + hasKey: any, +): boolean { var result = true; var asymmetricResult = asymmetricMatch(a, b); @@ -204,7 +210,11 @@ function eq(a, b, aStack, bStack, customTesters, hasKey): boolean { return result; } -function keys(obj, isArray, hasKey) { +function keys( + obj: object, + isArray: boolean, + hasKey: (obj: object, key: string) => boolean, +) { var allKeys = (function(o) { var keys = []; for (var key in o) { @@ -213,9 +223,10 @@ function keys(obj, isArray, hasKey) { } } return keys.concat( - (Object.getOwnPropertySymbols(o): Array).filter( + (Object.getOwnPropertySymbols(o) as Array).filter( //$FlowFixMe Jest complains about nullability, but we know for sure that property 'symbol' does exist. - symbol => Object.getOwnPropertyDescriptor(o, symbol).enumerable, + symbol => + (Object.getOwnPropertyDescriptor(o, symbol) as any).enumerable, ), ); })(obj); @@ -238,19 +249,19 @@ function keys(obj, isArray, hasKey) { return extraKeys; } -function hasDefinedKey(obj, key) { +function hasDefinedKey(obj: any, key: string) { return hasKey(obj, key) && obj[key] !== undefined; } -function hasKey(obj, key) { +function hasKey(obj: any, key: string) { return Object.prototype.hasOwnProperty.call(obj, key); } -export function isA(typeName: string, value: any) { +export function isA(typeName: string, value: unknown) { return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; } -function isDomNode(obj) { +function isDomNode(obj: any): obj is Node { return ( obj !== null && typeof obj === 'object' && @@ -272,7 +283,7 @@ export function isUndefined(obj: any) { return obj === void 0; } -function getPrototype(obj) { +function getPrototype(obj: object) { if (Object.getPrototypeOf) { return Object.getPrototypeOf(obj); } @@ -284,7 +295,7 @@ function getPrototype(obj) { return obj.constructor.prototype; } -export function hasProperty(obj: Object | null, property: string) { +export function hasProperty(obj: object | null, property: string): boolean { if (!obj) { return false; } diff --git a/packages/expect/src/jestMatchersObject.js b/packages/expect/src/jestMatchersObject.ts similarity index 66% rename from packages/expect/src/jestMatchersObject.js rename to packages/expect/src/jestMatchersObject.ts index c2fc9600f5eb..e9563113c219 100644 --- a/packages/expect/src/jestMatchersObject.js +++ b/packages/expect/src/jestMatchersObject.ts @@ -4,15 +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 {AsymmetricMatcher} from './asymmetricMatchers'; -import type { - Expect, - MatchersObject, - SyncExpectationResult, -} from 'types/Matchers'; +import {Expect, MatchersObject, SyncExpectationResult} from './types'; // Global matchers object holds the list of available matchers and // the state, that can hold matcher specific values that change over time. @@ -22,7 +17,7 @@ const JEST_MATCHERS_OBJECT = Symbol.for('$$jest-matchers-object'); // Jest may override the stack trace of Errors thrown by internal matchers. export const INTERNAL_MATCHER_FLAG = Symbol.for('$$jest-internal-matcher'); -if (!global[JEST_MATCHERS_OBJECT]) { +if (!(global as any)[JEST_MATCHERS_OBJECT]) { Object.defineProperty(global, JEST_MATCHERS_OBJECT, { value: { matchers: Object.create(null), @@ -36,13 +31,13 @@ if (!global[JEST_MATCHERS_OBJECT]) { }); } -export const getState = () => global[JEST_MATCHERS_OBJECT].state; +export const getState = () => (global as any)[JEST_MATCHERS_OBJECT].state; -export const setState = (state: Object) => { - Object.assign(global[JEST_MATCHERS_OBJECT].state, state); +export const setState = (state: object) => { + Object.assign((global as any)[JEST_MATCHERS_OBJECT].state, state); }; -export const getMatchers = () => global[JEST_MATCHERS_OBJECT].matchers; +export const getMatchers = () => (global as any)[JEST_MATCHERS_OBJECT].matchers; export const setMatchers = ( matchers: MatchersObject, @@ -58,20 +53,17 @@ export const setMatchers = ( if (!isInternal) { // expect is defined - class CustomMatcher extends AsymmetricMatcher { - sample: Array; - - constructor(inverse: boolean = false, ...sample: Array) { - super(); + class CustomMatcher extends AsymmetricMatcher<[unknown, unknown]> { + constructor(inverse: boolean = false, ...sample: [unknown, unknown]) { + super(sample); this.inverse = inverse; - this.sample = sample; } - asymmetricMatch(other: any) { - const {pass} = ((matcher( - (other: any), - ...(this.sample: any), - ): any): SyncExpectationResult); + asymmetricMatch(other: unknown) { + const {pass} = matcher( + other, + ...this.sample, + ) as SyncExpectationResult; return this.inverse ? !pass : pass; } @@ -89,15 +81,15 @@ export const setMatchers = ( } } - expect[key] = (...sample: Array) => + expect[key] = (...sample: [unknown, unknown]) => new CustomMatcher(false, ...sample); if (!expect.not) { expect.not = {}; } - expect.not[key] = (...sample: Array) => + expect.not[key] = (...sample: [unknown, unknown]) => new CustomMatcher(true, ...sample); } }); - Object.assign(global[JEST_MATCHERS_OBJECT].matchers, matchers); + Object.assign((global as any)[JEST_MATCHERS_OBJECT].matchers, matchers); }; diff --git a/packages/expect/src/matchers.js b/packages/expect/src/matchers.ts similarity index 89% rename from packages/expect/src/matchers.js rename to packages/expect/src/matchers.ts index bc093152ca15..6154f33d7873 100644 --- a/packages/expect/src/matchers.js +++ b/packages/expect/src/matchers.ts @@ -4,11 +4,8 @@ * 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 {MatchersObject} from 'types/Matchers'; - import getType from 'jest-get-type'; import {escapeStrForRegex} from 'jest-regex-util'; import { @@ -25,7 +22,9 @@ import { printReceived, printExpected, printWithType, + MatcherHintOptions, } from 'jest-matcher-utils'; +import {MatchersObject, MatcherState} from './types'; import { getObjectSubset, getPath, @@ -38,14 +37,14 @@ import { import {equals} from './jasmineUtils'; type ContainIterable = - | Array - | Set - | NodeList + | Array + | Set + | NodeListOf | DOMTokenList - | HTMLCollection; + | HTMLCollectionOf; const matchers: MatchersObject = { - toBe(received: any, expected: any) { + toBe(this: MatcherState, received: unknown, expected: unknown) { const comment = 'Object.is equality'; const pass = Object.is(received, expected); @@ -87,10 +86,15 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toBe', pass}; }, - toBeCloseTo(received: number, expected: number, precision?: number = 2) { - const secondArgument = arguments.length === 3 ? 'precision' : null; + toBeCloseTo( + this: MatcherState, + received: number, + expected: number, + precision: number = 2, + ) { + const secondArgument = arguments.length === 3 ? 'precision' : undefined; const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, secondArgument, @@ -136,8 +140,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeDefined(received: any, expected: void) { - const options = { + toBeDefined(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -153,8 +157,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeFalsy(received: any, expected: void) { - const options = { + toBeFalsy(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -170,9 +174,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThan(received: number, expected: number) { + toBeGreaterThan(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -189,9 +193,13 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeGreaterThanOrEqual(received: number, expected: number) { + toBeGreaterThanOrEqual( + this: MatcherState, + received: number, + expected: number, + ) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -208,7 +216,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeInstanceOf(received: any, constructor: Function) { + toBeInstanceOf(this: MatcherState, received: any, constructor: Function) { const constType = getType(constructor); if (constType !== 'function') { @@ -252,9 +260,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThan(received: number, expected: number) { + toBeLessThan(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -271,9 +279,9 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeLessThanOrEqual(received: number, expected: number) { + toBeLessThanOrEqual(this: MatcherState, received: number, expected: number) { const isNot = this.isNot; - const options = { + const options: MatcherHintOptions = { isNot, promise: this.promise, }; @@ -290,8 +298,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeNaN(received: any, expected: void) { - const options = { + toBeNaN(this: MatcherState, received: any, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -307,8 +315,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeNull(received: any, expected: void) { - const options = { + toBeNull(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -324,8 +332,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeTruthy(received: any, expected: void) { - const options = { + toBeTruthy(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -341,8 +349,8 @@ const matchers: MatchersObject = { return {message, pass}; }, - toBeUndefined(received: any, expected: void) { - const options = { + toBeUndefined(this: MatcherState, received: unknown, expected: void) { + const options: MatcherHintOptions = { isNot: this.isNot, promise: this.promise, }; @@ -358,10 +366,14 @@ const matchers: MatchersObject = { return {message, pass}; }, - toContain(collection: ContainIterable | string, value: any) { + toContain( + this: MatcherState, + collection: ContainIterable | string, + value: unknown, + ) { const collectionType = getType(collection); - let converted = null; + let converted: any = null; if (Array.isArray(collection) || typeof collection === 'string') { // strings have `indexOf` so we don't need to convert // arrays have `indexOf` and we don't want to make a copy @@ -413,7 +425,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toContainEqual(collection: ContainIterable, value: any) { + toContainEqual( + this: MatcherState, + collection: ContainIterable, + value: unknown, + ) { const collectionType = getType(collection); let converted = null; if (Array.isArray(collection)) { @@ -458,7 +474,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toEqual(received: any, expected: any) { + toEqual(this: MatcherState, received: unknown, expected: unknown) { const pass = equals(received, expected, [iterableEquality]); const message = pass @@ -490,7 +506,7 @@ const matchers: MatchersObject = { return {actual: received, expected, message, name: 'toEqual', pass}; }, - toHaveLength(received: any, length: number) { + toHaveLength(this: MatcherState, received: any, length: number) { if ( typeof received !== 'string' && (!received || typeof received.length !== 'number') @@ -547,7 +563,12 @@ const matchers: MatchersObject = { return {message, pass}; }, - toHaveProperty(object: Object, keyPath: string | Array, value?: any) { + toHaveProperty( + this: MatcherState, + object: object, + keyPath: string | Array, + value?: unknown, + ) { const valuePassed = arguments.length === 3; const secondArgument = valuePassed ? 'value' : null; @@ -557,7 +578,7 @@ const matchers: MatchersObject = { matcherHint('.toHaveProperty', undefined, 'path', { isNot: this.isNot, secondArgument, - }), + } as MatcherHintOptions), `${RECEIVED_COLOR('received')} value must not be null nor undefined`, printWithType('Received', object, printReceived), ), @@ -572,7 +593,7 @@ const matchers: MatchersObject = { matcherHint('.toHaveProperty', undefined, 'path', { isNot: this.isNot, secondArgument, - }), + } as MatcherHintOptions), `${EXPECTED_COLOR('expected')} path must be a string or array`, printWithType('Expected', keyPath, printExpected), ), @@ -592,7 +613,7 @@ const matchers: MatchersObject = { ? () => matcherHint('.not.toHaveProperty', 'object', 'path', { secondArgument, - }) + + } as MatcherHintOptions) + '\n\n' + `Expected the object:\n` + ` ${printReceived(object)}\n` + @@ -607,7 +628,7 @@ const matchers: MatchersObject = { return ( matcherHint('.toHaveProperty', 'object', 'path', { secondArgument, - }) + + } as MatcherHintOptions) + '\n\n' + `Expected the object:\n` + ` ${printReceived(object)}\n` + @@ -634,7 +655,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toMatch(received: string, expected: string | RegExp) { + toMatch(this: MatcherState, received: string, expected: string | RegExp) { if (typeof received !== 'string') { throw new Error( matcherErrorMessage( @@ -648,8 +669,8 @@ const matchers: MatchersObject = { } if ( - !(expected && typeof expected.test === 'function') && - !(typeof expected === 'string') + !(typeof expected === 'string') && + !(expected && typeof expected.test === 'function') ) { throw new Error( matcherErrorMessage( @@ -684,7 +705,11 @@ const matchers: MatchersObject = { return {message, pass}; }, - toMatchObject(receivedObject: Object, expectedObject: Object) { + toMatchObject( + this: MatcherState, + receivedObject: object, + expectedObject: object, + ) { if (typeof receivedObject !== 'object' || receivedObject === null) { throw new Error( matcherErrorMessage( @@ -742,7 +767,7 @@ const matchers: MatchersObject = { return {message, pass}; }, - toStrictEqual(received: any, expected: any) { + toStrictEqual(this: MatcherState, received: unknown, expected: unknown) { const pass = equals( received, expected, diff --git a/packages/expect/src/spyMatchers.js b/packages/expect/src/spyMatchers.ts similarity index 89% rename from packages/expect/src/spyMatchers.js rename to packages/expect/src/spyMatchers.ts index 50f623d9c770..60e5606d6a6a 100644 --- a/packages/expect/src/spyMatchers.js +++ b/packages/expect/src/spyMatchers.ts @@ -4,14 +4,8 @@ * 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 {MatchersObject} from 'types/Matchers'; - -const CALL_PRINT_LIMIT = 3; -const RETURN_PRINT_LIMIT = 5; -const LAST_CALL_PRINT_LIMIT = 1; import { diff, ensureExpectedIsNumber, @@ -25,10 +19,18 @@ import { printWithType, RECEIVED_COLOR, } from 'jest-matcher-utils'; +import {MatchersObject, SyncExpectationResult} from './types'; import {equals} from './jasmineUtils'; import {iterableEquality, partition, isOneline} from './utils'; -const createToBeCalledMatcher = matcherName => (received, expected) => { +const CALL_PRINT_LIMIT = 3; +const RETURN_PRINT_LIMIT = 5; +const LAST_CALL_PRINT_LIMIT = 1; + +const createToBeCalledMatcher = (matcherName: string) => ( + received: any, + expected: unknown, +): SyncExpectationResult => { ensureNoExpected(expected, matcherName); ensureMock(received, matcherName); @@ -43,7 +45,7 @@ const createToBeCalledMatcher = matcherName => (received, expected) => { ? received.calls.count() : received.mock.calls.length; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = count > 0; const message = pass @@ -60,7 +62,10 @@ const createToBeCalledMatcher = matcherName => (received, expected) => { return {message, pass}; }; -const createToReturnMatcher = matcherName => (received, expected) => { +const createToReturnMatcher = (matcherName: string) => ( + received: any, + expected: unknown, +): SyncExpectationResult => { ensureNoExpected(expected, matcherName); ensureMock(received, matcherName); @@ -72,8 +77,8 @@ const createToReturnMatcher = matcherName => (received, expected) => { // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => result.type === 'return') - .map(result => result.value); + .filter((result: any) => result.type === 'return') + .map((result: any) => result.value); const count = returnValues.length; const pass = count > 0; @@ -95,7 +100,7 @@ const createToReturnMatcher = matcherName => (received, expected) => { const createToBeCalledTimesMatcher = (matcherName: string) => ( received: any, expected: number, -) => { +): SyncExpectationResult => { ensureExpectedIsNumber(expected, matcherName); ensureMock(received, matcherName); @@ -130,7 +135,7 @@ const createToBeCalledTimesMatcher = (matcherName: string) => ( const createToReturnTimesMatcher = (matcherName: string) => ( received: any, expected: number, -) => { +): SyncExpectationResult => { ensureExpectedIsNumber(expected, matcherName); ensureMock(received, matcherName); @@ -142,7 +147,7 @@ const createToReturnTimesMatcher = (matcherName: string) => ( // List of return results that correspond only to calls that returned const returnResults = received.mock.results.filter( - result => result.type === 'return', + (result: any) => result.type === 'return', ); const count = returnResults.length; @@ -165,10 +170,10 @@ const createToReturnTimesMatcher = (matcherName: string) => ( return {message, pass}; }; -const createToBeCalledWithMatcher = matcherName => ( +const createToBeCalledWithMatcher = (matcherName: string) => ( received: any, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); @@ -180,7 +185,7 @@ const createToBeCalledWithMatcher = matcherName => ( : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const [match, fail] = partition(calls, call => @@ -203,10 +208,10 @@ const createToBeCalledWithMatcher = matcherName => ( return {message, pass}; }; -const createToReturnWithMatcher = matcherName => ( +const createToReturnWithMatcher = (matcherName: string) => ( received: any, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedName = received.getMockName(); @@ -217,8 +222,8 @@ const createToReturnWithMatcher = matcherName => ( // List of return values that correspond only to calls that returned const returnValues = received.mock.results - .filter(result => result.type === 'return') - .map(result => result.value); + .filter((result: any) => result.type === 'return') + .map((result: any) => result.value); const [match] = partition(returnValues, value => equals(expected, value, [iterableEquality]), @@ -246,10 +251,10 @@ const createToReturnWithMatcher = matcherName => ( return {message, pass}; }; -const createLastCalledWithMatcher = matcherName => ( +const createLastCalledWithMatcher = (matcherName: string) => ( received: any, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); @@ -260,7 +265,7 @@ const createLastCalledWithMatcher = matcherName => ( ? type : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = equals(calls[calls.length - 1], expected, [iterableEquality]); @@ -279,10 +284,10 @@ const createLastCalledWithMatcher = matcherName => ( return {message, pass}; }; -const createLastReturnedMatcher = matcherName => ( +const createLastReturnedMatcher = (matcherName: string) => ( received: any, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedName = received.getMockName(); @@ -327,13 +332,14 @@ const createLastReturnedMatcher = matcherName => ( const createNthCalledWithMatcher = (matcherName: string) => ( received: any, nth: number, - ...expected: any -) => { + ...expected: Array +): SyncExpectationResult => { ensureMock(received, matcherName); const receivedIsSpy = isSpy(received); const type = receivedIsSpy ? 'spy' : 'mock function'; + // @ts-ignore if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { const message = () => `nth value ${printReceived( @@ -349,7 +355,7 @@ const createNthCalledWithMatcher = (matcherName: string) => ( ? type : `${type} "${receivedName}"`; const calls = receivedIsSpy - ? received.calls.all().map(x => x.args) + ? received.calls.all().map((x: any) => x.args) : received.mock.calls; const pass = equals(calls[nth - 1], expected, [iterableEquality]); @@ -379,10 +385,11 @@ const createNthCalledWithMatcher = (matcherName: string) => ( const createNthReturnedWithMatcher = (matcherName: string) => ( received: any, nth: number, - expected: any, -) => { + expected: unknown, +): SyncExpectationResult => { ensureMock(received, matcherName); + //@ts-ignore if (typeof nth !== 'number' || parseInt(nth, 10) !== nth || nth < 1) { const message = () => `nth value ${printReceived( @@ -462,9 +469,9 @@ const spyMatchers: MatchersObject = { toReturnWith: createToReturnWithMatcher('.toReturnWith'), }; -const isSpy = spy => spy.calls && typeof spy.calls.count === 'function'; +const isSpy = (spy: any) => spy.calls && typeof spy.calls.count === 'function'; -const ensureMock = (mockOrSpy, matcherName) => { +const ensureMock = (mockOrSpy: any, matcherName: any) => { if ( !mockOrSpy || ((mockOrSpy.calls === undefined || mockOrSpy.calls.all === undefined) && @@ -510,7 +517,11 @@ const getPrintedReturnValues = (calls: any[], limit: number): string => { return result.join('\n\n '); }; -const formatReceivedCalls = (calls, limit, options) => { +const formatReceivedCalls = ( + calls: Array, + limit: number, + options: any, +) => { if (calls.length) { const but = options && options.sameSentence ? 'but' : 'But'; const count = calls.length - limit; @@ -528,7 +539,11 @@ const formatReceivedCalls = (calls, limit, options) => { } }; -const formatMismatchedCalls = (calls, expected, limit) => { +const formatMismatchedCalls = ( + calls: Array, + expected: any, + limit: number, +): string => { if (calls.length) { return getPrintedCalls( calls, @@ -544,7 +559,11 @@ const formatMismatchedCalls = (calls, expected, limit) => { } }; -const formatMismatchedReturnValues = (returnValues, expected, limit) => { +const formatMismatchedReturnValues = ( + returnValues: Array, + expected: any, + limit: number, +): string => { if (returnValues.length) { return ( ` ${printExpected(expected)}\n` + @@ -559,7 +578,7 @@ const formatMismatchedReturnValues = (returnValues, expected, limit) => { } }; -const formatMismatchedArgs = (expected, received) => { +const formatMismatchedArgs = (expected: any, received: any): string => { const length = Math.max(expected.length, received.length); const printedArgs = []; @@ -584,7 +603,7 @@ const formatMismatchedArgs = (expected, received) => { return printedArgs.join('\n'); }; -const nthToString = (nth: number) => { +const nthToString = (nth: number): string => { switch (nth) { case 1: return 'first'; diff --git a/packages/expect/src/toThrowMatchers.js b/packages/expect/src/toThrowMatchers.ts similarity index 92% rename from packages/expect/src/toThrowMatchers.js rename to packages/expect/src/toThrowMatchers.ts index a53e6979e461..150e1c7fe725 100644 --- a/packages/expect/src/toThrowMatchers.js +++ b/packages/expect/src/toThrowMatchers.ts @@ -4,11 +4,8 @@ * 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 {MatcherHintOptions, MatchersObject} from 'types/Matchers'; - import {formatStackTrace, separateMessageFromStack} from 'jest-message-util'; import { EXPECTED_COLOR, @@ -18,23 +15,30 @@ import { printExpected, printReceived, printWithType, + MatcherHintOptions, } from 'jest-matcher-utils'; +import { + MatchersObject, + MatcherState, + RawMatcherFn, + SyncExpectationResult, +} from './types'; import {isError} from './utils'; const DID_NOT_THROW = 'Received function did not throw'; type Thrown = | { - hasMessage: true, - isError: true, - message: string, - value: Error, + hasMessage: true; + isError: true; + message: string; + value: Error; } | { - hasMessage: boolean, - isError: false, - message: string, - value: any, + hasMessage: boolean; + isError: false; + message: string; + value: any; }; const getThrown = (e: any): Thrown => { @@ -58,8 +62,11 @@ const getThrown = (e: any): Thrown => { }; }; -export const createMatcher = (matcherName: string, fromPromise?: boolean) => - function(received: Function, expected: any) { +export const createMatcher = ( + matcherName: string, + fromPromise?: boolean, +): RawMatcherFn => + function(this: MatcherState, received: Function, expected: any) { const options = { isNot: this.isNot, promise: this.promise, @@ -128,7 +135,7 @@ const toThrowExpectedRegExp = ( options: MatcherHintOptions, thrown: Thrown | null, expected: RegExp, -) => { +): SyncExpectationResult => { const pass = thrown !== null && expected.test(thrown.message); const message = pass @@ -155,7 +162,7 @@ const toThrowExpectedRegExp = ( }; type AsymmetricMatcher = { - asymmetricMatch: (received: any) => boolean, + asymmetricMatch: (received: unknown) => boolean; }; const toThrowExpectedAsymmetric = ( @@ -163,7 +170,7 @@ const toThrowExpectedAsymmetric = ( options: MatcherHintOptions, thrown: Thrown | null, expected: AsymmetricMatcher, -) => { +): SyncExpectationResult => { const pass = thrown !== null && expected.asymmetricMatch(thrown.value); const message = pass @@ -197,8 +204,8 @@ const toThrowExpectedObject = ( matcherName: string, options: MatcherHintOptions, thrown: Thrown | null, - expected: Object, -) => { + expected: any, +): SyncExpectationResult => { const pass = thrown !== null && thrown.message === expected.message; const message = pass @@ -229,7 +236,7 @@ const toThrowExpectedClass = ( options: MatcherHintOptions, thrown: Thrown | null, expected: Function, -) => { +): SyncExpectationResult => { const pass = thrown !== null && thrown.value instanceof expected; const message = pass @@ -264,7 +271,7 @@ const toThrowExpectedString = ( options: MatcherHintOptions, thrown: Thrown | null, expected: string, -) => { +): SyncExpectationResult => { const pass = thrown !== null && thrown.message.includes(expected); const message = pass @@ -294,7 +301,7 @@ const toThrow = ( matcherName: string, options: MatcherHintOptions, thrown: Thrown | null, -) => { +): SyncExpectationResult => { const pass = thrown !== null; const message = pass @@ -314,7 +321,7 @@ const toThrow = ( return {message, pass}; }; -const formatExpected = (label: string, expected: any) => +const formatExpected = (label: string, expected: unknown) => label + printExpected(expected) + '\n'; const formatReceived = (label: string, thrown: Thrown | null, key: string) => { @@ -343,6 +350,7 @@ const formatStack = (thrown: Thrown | null) => thrown === null || !thrown.isError ? '' : formatStackTrace( + // @ts-ignore separateMessageFromStack(thrown.value.stack).stack, { rootDir: process.cwd(), diff --git a/packages/expect/src/types.ts b/packages/expect/src/types.ts new file mode 100644 index 000000000000..106abcb03995 --- /dev/null +++ b/packages/expect/src/types.ts @@ -0,0 +1,103 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ +import {Config} from '@jest/types'; +import * as jestMatcherUtils from 'jest-matcher-utils'; + +export type SyncExpectationResult = { + pass: boolean; + message: () => string; +}; + +export type AsyncExpectationResult = Promise; + +export type ExpectationResult = SyncExpectationResult | AsyncExpectationResult; + +export type RawMatcherFn = ( + expected: any, + actual: any, + options?: any, +) => ExpectationResult; + +export type ThrowingMatcherFn = (actual: any) => void; +export type PromiseMatcherFn = (actual: any) => Promise; + +export type Tester = (a: any, b: any) => boolean | undefined; + +export type MatcherState = { + assertionCalls: number; + currentTestName?: string; + dontThrow?: () => void; + error?: Error; + equals: ( + a: unknown, + b: unknown, + customTesters?: Array, + strictCheck?: boolean, + ) => boolean; + expand?: boolean; + expectedAssertionsNumber?: number; + isExpectingAssertions?: boolean; + isNot: boolean; + promise: string; + suppressedErrors: Array; + testPath?: Config.Path; + utils: typeof jestMatcherUtils & { + iterableEquality: Tester; + subsetEquality: Tester; + }; +}; + +export type AsymmetricMatcher = Object; +export type MatchersObject = {[id: string]: RawMatcherFn}; +export type Expect = { + (expected: any): ExpectationObject; + addSnapshotSerializer(arg0: any): void; + assertions(arg0: number): void; + extend(arg0: any): void; + extractExpectedAssertionsErrors: () => Array<{ + actual: string | number; + error: Error; + expected: string; + }>; + getState(): MatcherState; + hasAssertions(): void; + setState(arg0: any): void; + + any(expectedObject: any): AsymmetricMatcher; + anything(): AsymmetricMatcher; + arrayContaining(sample: Array): AsymmetricMatcher; + objectContaining(sample: Object): AsymmetricMatcher; + stringContaining(expected: string): AsymmetricMatcher; + stringMatching(expected: string | RegExp): AsymmetricMatcher; + [id: string]: AsymmetricMatcher; + not: {[id: string]: AsymmetricMatcher}; +}; + +type resolvesFn = { + [id: string]: PromiseMatcherFn; +} & { + not: {[id: string]: PromiseMatcherFn}; +}; + +type rejectsFn = { + [id: string]: PromiseMatcherFn; +} & { + not: {[id: string]: PromiseMatcherFn}; +}; + +type notFn = { + [id: string]: ThrowingMatcherFn; +}; + +export type ExpectationObject = { + [id: string]: ThrowingMatcherFn; +} & { + resolves: resolvesFn; + rejects: rejectsFn; + not: notFn; +}; diff --git a/packages/expect/src/utils.js b/packages/expect/src/utils.ts similarity index 87% rename from packages/expect/src/utils.js rename to packages/expect/src/utils.ts index 680c5faa0c1b..42384bbb8bc2 100644 --- a/packages/expect/src/utils.js +++ b/packages/expect/src/utils.ts @@ -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 { @@ -15,14 +14,14 @@ import { } from './jasmineUtils'; type GetPath = { - hasEndProp?: boolean, - lastTraversedObject: ?Object, - traversedPath: Array, - value?: any, + hasEndProp?: boolean; + lastTraversedObject: unknown; + traversedPath: Array; + value?: unknown; }; // Return whether object instance inherits getter from its class. -const hasGetterFromConstructor = (object: Object, key: string) => { +const hasGetterFromConstructor = (object: object, key: string) => { const constructor = object.constructor; if (constructor === Object) { // A literal object has Object as constructor. @@ -44,22 +43,22 @@ const hasGetterFromConstructor = (object: Object, key: string) => { return descriptor !== undefined && typeof descriptor.get === 'function'; }; -export const hasOwnProperty = (object: Object, key: string) => +export const hasOwnProperty = (object: object, key: string) => Object.prototype.hasOwnProperty.call(object, key) || hasGetterFromConstructor(object, key); export const getPath = ( - object: Object, + object: object, propertyPath: string | Array, ): GetPath => { if (!Array.isArray(propertyPath)) { - propertyPath = propertyPath.split('.'); + propertyPath = (propertyPath as string).split('.'); } if (propertyPath.length) { const lastProp = propertyPath.length === 1; const prop = propertyPath[0]; - const newObject = object[prop]; + const newObject = (object as any)[prop]; if (!lastProp && (newObject === null || newObject === undefined)) { // This is not the last prop in the chain. If we keep recursing it will @@ -99,10 +98,12 @@ export const getPath = ( // Strip properties from object that are not present in the subset. Useful for // printing the diff for toMatchObject() without adding unrelated noise. -export const getObjectSubset = (object: Object, subset: Object) => { +export const getObjectSubset = (object: any, subset: any): any => { if (Array.isArray(object)) { if (Array.isArray(subset) && subset.length === object.length) { - return subset.map((sub, i) => getObjectSubset(object[i], sub)); + return subset.map((sub: any, i: number) => + getObjectSubset(object[i], sub), + ); } } else if (object instanceof Date) { return object; @@ -112,7 +113,7 @@ export const getObjectSubset = (object: Object, subset: Object) => { typeof subset === 'object' && subset !== null ) { - const trimmed = {}; + const trimmed: any = {}; Object.keys(subset) .filter(key => hasOwnProperty(object, key)) .forEach( @@ -128,7 +129,8 @@ export const getObjectSubset = (object: Object, subset: Object) => { const IteratorSymbol = Symbol.iterator; -const hasIterator = object => !!(object != null && object[IteratorSymbol]); +const hasIterator = (object: any) => + !!(object != null && object[IteratorSymbol]); export const iterableEquality = (a: any, b: any) => { if ( typeof a !== 'object' || @@ -215,14 +217,17 @@ export const iterableEquality = (a: any, b: any) => { return true; }; -const isObjectWithKeys = a => +const isObjectWithKeys = (a: any) => a !== null && typeof a === 'object' && !(a instanceof Error) && !(a instanceof Array) && !(a instanceof Date); -export const subsetEquality = (object: Object, subset: Object) => { +export const subsetEquality = ( + object: any, + subset: any, +): undefined | boolean => { if (!isObjectWithKeys(subset)) { return undefined; } @@ -243,7 +248,7 @@ export const typeEquality = (a: any, b: any) => { return false; }; -export const sparseArrayEquality = (a: any, b: any) => { +export const sparseArrayEquality = (a: unknown, b: unknown) => { if (!Array.isArray(a) || !Array.isArray(b)) { return undefined; } @@ -256,9 +261,9 @@ export const sparseArrayEquality = (a: any, b: any) => { export const partition = ( items: Array, - predicate: T => boolean, + predicate: (arg: T) => boolean, ): [Array, Array] => { - const result = [[], []]; + const result: [Array, Array] = [[], []]; items.forEach(item => result[predicate(item) ? 0 : 1].push(item)); @@ -266,7 +271,7 @@ export const partition = ( }; // Copied from https://github.com/graingert/angular.js/blob/a43574052e9775cbc1d7dd8a086752c979b0f020/src/Angular.js#L685-L693 -export const isError = (value: any) => { +export const isError = (value: unknown) => { switch (Object.prototype.toString.call(value)) { case '[object Error]': return true; @@ -285,7 +290,7 @@ export function emptyObject(obj: any) { const MULTILINE_REGEXP = /[\r\n]/; -export const isOneline = (expected: any, received: any) => +export const isOneline = (expected: any, received: any): boolean => typeof expected === 'string' && typeof received === 'string' && (!MULTILINE_REGEXP.test(expected) || !MULTILINE_REGEXP.test(received)); diff --git a/packages/expect/tsconfig.json b/packages/expect/tsconfig.json new file mode 100644 index 000000000000..9bc3707b280d --- /dev/null +++ b/packages/expect/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build" + }, + "references": [ + {"path": "../jest-get-type"}, + {"path": "../jest-matcher-utils"}, + {"path": "../jest-message-util"}, + {"path": "../jest-regex-util"}, + {"path": "../jest-types"} + ] +} diff --git a/packages/jest-snapshot/src/index.ts b/packages/jest-snapshot/src/index.ts index b9b60ec9bab8..68a42e200c0a 100644 --- a/packages/jest-snapshot/src/index.ts +++ b/packages/jest-snapshot/src/index.ts @@ -20,6 +20,7 @@ import SnapshotState from './State'; import {addSerializer, getSerializers} from './plugins'; import * as utils from './utils'; +// TODO: use MatcherState directly from `expect` once whole project is migrated type Context = Matchers.MatcherState & { snapshotState: SnapshotState; }; diff --git a/packages/jest-types/src/Matchers.ts b/packages/jest-types/src/Matchers.ts index e4e419e0ae23..f1a4b9b94ce9 100644 --- a/packages/jest-types/src/Matchers.ts +++ b/packages/jest-types/src/Matchers.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -// TODO: Move this to `expect` when it's migrated +// TODO: Remove this when whole project is migrated import {Path} from './Config'; diff --git a/scripts/checkCopyrightHeaders.js b/scripts/checkCopyrightHeaders.js index 2cd3c0623a17..73eeffc644ba 100755 --- a/scripts/checkCopyrightHeaders.js +++ b/scripts/checkCopyrightHeaders.js @@ -99,7 +99,7 @@ const CUSTOM_IGNORED_PATTERNS = [ '\\.(example|map)$', '^examples/.*', '^flow-typed/.*', - '^packages/expect/src/jasmineUtils\\.js$', + '^packages/expect/src/jasmineUtils\\.ts$', '^packages/jest-config/src/vendor/jsonlint\\.js$', ].map(createRegExp);