From a7fd3605d158b00dd51f8d5cdda9b5ddbb09e7aa Mon Sep 17 00:00:00 2001 From: "Juan Picado @jotadeveloper" Date: Sat, 30 Jun 2018 01:13:25 +0200 Subject: [PATCH] chore: update jest and eslint --- .eslintrc | 1 + .../npm/{jest_v22.x.x.js => jest_v23.x.x.js} | 538 ++++++++++++++++-- package.json | 20 +- yarn.lock | Bin 347462 -> 348500 bytes 4 files changed, 510 insertions(+), 49 deletions(-) rename flow-typed/npm/{jest_v22.x.x.js => jest_v23.x.x.js} (53%) diff --git a/.eslintrc b/.eslintrc index 4b4184023..f0ed31c46 100644 --- a/.eslintrc +++ b/.eslintrc @@ -31,6 +31,7 @@ }, "rules": { "no-useless-escape": 2, + "react/no-deprecated": 1, "handle-callback-err": 2, "no-fallthrough": 2, "no-new-require": 2, diff --git a/flow-typed/npm/jest_v22.x.x.js b/flow-typed/npm/jest_v23.x.x.js similarity index 53% rename from flow-typed/npm/jest_v22.x.x.js rename to flow-typed/npm/jest_v23.x.x.js index b10789020..a6ac47578 100644 --- a/flow-typed/npm/jest_v22.x.x.js +++ b/flow-typed/npm/jest_v23.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6e1fc0a644aa956f79029fec0709e597 -// flow-typed version: 07ebad4796/jest_v22.x.x/flow_>=v0.39.x +// flow-typed signature: 4cacceffd326bb118e4a3c1b4d629e98 +// flow-typed version: e737b9832f/jest_v23.x.x/flow_>=v0.39.x type JestMockFn, TReturn> = { (...args: TArguments): TReturn, @@ -55,6 +55,11 @@ type JestMockFn, TReturn> = { mockImplementationOnce( fn: (...args: TArguments) => TReturn ): JestMockFn, + /** + * Accepts a string to use in test result output in place of "jest.fn()" to + * indicate which mock function is being referenced. + */ + mockName(name: string): JestMockFn, /** * Just a simple sugar function for returning `this` */ @@ -66,7 +71,23 @@ type JestMockFn, TReturn> = { /** * Sugar for only returning a value once inside your mock */ - mockReturnValueOnce(value: TReturn): JestMockFn + mockReturnValueOnce(value: TReturn): JestMockFn, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.resolve(value)) + */ + mockResolvedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.resolve(value)) + */ + mockResolvedValueOnce(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.reject(value)) + */ + mockRejectedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.reject(value)) + */ + mockRejectedValueOnce(value: TReturn): JestMockFn> }; type JestAsymmetricEqualityType = { @@ -113,6 +134,12 @@ type JestPromiseType = { resolves: JestExpectType }; +/** + * Jest allows functions and classes to be used as test names in test() and + * describe() + */ +type JestTestName = string | Function; + /** * Plugin: jest-enzyme */ @@ -120,14 +147,16 @@ type EnzymeMatchersType = { toBeChecked(): void, toBeDisabled(): void, toBeEmpty(): void, + toBeEmptyRender(): void, toBePresent(): void, toContainReact(element: React$Element): void, + toExist(): void, toHaveClassName(className: string): void, toHaveHTML(html: string): void, - toHaveProp(propKey: string, propValue?: any): void, + toHaveProp: ((propKey: string, propValue?: any) => void) & ((props: Object) => void), toHaveRef(refName: string): void, - toHaveState(stateKey: string, stateValue?: any): void, - toHaveStyle(styleKey: string, styleValue?: any): void, + toHaveState: ((stateKey: string, stateValue?: any) => void) & ((state: Object) => void), + toHaveStyle: ((styleKey: string, styleValue?: any) => void) & ((style: Object) => void), toHaveTagName(tagName: string): void, toHaveText(text: string): void, toIncludeText(text: string): void, @@ -136,8 +165,342 @@ type EnzymeMatchersType = { toMatchSelector(selector: string): void }; -type JestExpectType = { - not: JestExpectType & EnzymeMatchersType, +// DOM testing library extensions https://github.com/kentcdodds/dom-testing-library#custom-jest-matchers +type DomTestingLibraryType = { + toBeInTheDOM(): void, + toHaveTextContent(content: string): void, + toHaveAttribute(name: string, expectedValue?: string): void +}; + +// Jest JQuery Matchers: https://github.com/unindented/custom-jquery-matchers +type JestJQueryMatchersType = { + toExist(): void, + toHaveLength(len: number): void, + toHaveId(id: string): void, + toHaveClass(className: string): void, + toHaveTag(tag: string): void, + toHaveAttr(key: string, val?: any): void, + toHaveProp(key: string, val?: any): void, + toHaveText(text: string | RegExp): void, + toHaveData(key: string, val?: any): void, + toHaveValue(val: any): void, + toHaveCss(css: {[key: string]: any}): void, + toBeChecked(): void, + toBeDisabled(): void, + toBeEmpty(): void, + toBeHidden(): void, + toBeSelected(): void, + toBeVisible(): void, + toBeFocused(): void, + toBeInDom(): void, + toBeMatchedBy(sel: string): void, + toHaveDescendant(sel: string): void, + toHaveDescendantWithText(sel: string, text: string | RegExp): void +}; + + +// Jest Extended Matchers: https://github.com/jest-community/jest-extended +type JestExtendedMatchersType = { + /** + * Note: Currently unimplemented + * Passing assertion + * + * @param {String} message + */ + // pass(message: string): void; + + /** + * Note: Currently unimplemented + * Failing assertion + * + * @param {String} message + */ + // fail(message: string): void; + + /** + * Use .toBeEmpty when checking if a String '', Array [] or Object {} is empty. + */ + toBeEmpty(): void; + + /** + * Use .toBeOneOf when checking if a value is a member of a given Array. + * @param {Array.<*>} members + */ + toBeOneOf(members: any[]): void; + + /** + * Use `.toBeNil` when checking a value is `null` or `undefined`. + */ + toBeNil(): void; + + /** + * Use `.toSatisfy` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean`. + * @param {Function} predicate + */ + toSatisfy(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeArray` when checking if a value is an `Array`. + */ + toBeArray(): void; + + /** + * Use `.toBeArrayOfSize` when checking if a value is an `Array` of size x. + * @param {Number} x + */ + toBeArrayOfSize(x: number): void; + + /** + * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. + * @param {Array.<*>} members + */ + toIncludeAllMembers(members: any[]): void; + + /** + * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. + * @param {Array.<*>} members + */ + toIncludeAnyMembers(members: any[]): void; + + /** + * Use `.toSatisfyAll` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean` for all values in an array. + * @param {Function} predicate + */ + toSatisfyAll(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeBoolean` when checking if a value is a `Boolean`. + */ + toBeBoolean(): void; + + /** + * Use `.toBeTrue` when checking a value is equal (===) to `true`. + */ + toBeTrue(): void; + + /** + * Use `.toBeFalse` when checking a value is equal (===) to `false`. + */ + toBeFalse(): void; + + /** + * Use .toBeDate when checking if a value is a Date. + */ + toBeDate(): void; + + /** + * Use `.toBeFunction` when checking if a value is a `Function`. + */ + toBeFunction(): void; + + /** + * Use `.toHaveBeenCalledBefore` when checking if a `Mock` was called before another `Mock`. + * + * Note: Required Jest version >22 + * Note: Your mock functions will have to be asynchronous to cause the timestamps inside of Jest to occur in a differentJS event loop, otherwise the mock timestamps will all be the same + * + * @param {Mock} mock + */ + toHaveBeenCalledBefore(mock: JestMockFn): void; + + /** + * Use `.toBeNumber` when checking if a value is a `Number`. + */ + toBeNumber(): void; + + /** + * Use `.toBeNaN` when checking a value is `NaN`. + */ + toBeNaN(): void; + + /** + * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. + */ + toBeFinite(): void; + + /** + * Use `.toBePositive` when checking if a value is a positive `Number`. + */ + toBePositive(): void; + + /** + * Use `.toBeNegative` when checking if a value is a negative `Number`. + */ + toBeNegative(): void; + + /** + * Use `.toBeEven` when checking if a value is an even `Number`. + */ + toBeEven(): void; + + /** + * Use `.toBeOdd` when checking if a value is an odd `Number`. + */ + toBeOdd(): void; + + /** + * Use `.toBeWithin` when checking if a number is in between the given bounds of: start (inclusive) and end (exclusive). + * + * @param {Number} start + * @param {Number} end + */ + toBeWithin(start: number, end: number): void; + + /** + * Use `.toBeObject` when checking if a value is an `Object`. + */ + toBeObject(): void; + + /** + * Use `.toContainKey` when checking if an object contains the provided key. + * + * @param {String} key + */ + toContainKey(key: string): void; + + /** + * Use `.toContainKeys` when checking if an object has all of the provided keys. + * + * @param {Array.} keys + */ + toContainKeys(keys: string[]): void; + + /** + * Use `.toContainAllKeys` when checking if an object only contains all of the provided keys. + * + * @param {Array.} keys + */ + toContainAllKeys(keys: string[]): void; + + /** + * Use `.toContainAnyKeys` when checking if an object contains at least one of the provided keys. + * + * @param {Array.} keys + */ + toContainAnyKeys(keys: string[]): void; + + /** + * Use `.toContainValue` when checking if an object contains the provided value. + * + * @param {*} value + */ + toContainValue(value: any): void; + + /** + * Use `.toContainValues` when checking if an object contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainValues(values: any[]): void; + + /** + * Use `.toContainAllValues` when checking if an object only contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainAllValues(values: any[]): void; + + /** + * Use `.toContainAnyValues` when checking if an object contains at least one of the provided values. + * + * @param {Array.<*>} values + */ + toContainAnyValues(values: any[]): void; + + /** + * Use `.toContainEntry` when checking if an object contains the provided entry. + * + * @param {Array.} entry + */ + toContainEntry(entry: [string, string]): void; + + /** + * Use `.toContainEntries` when checking if an object contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainAllEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. + * + * @param {Array.>} entries + */ + toContainAnyEntries(entries: [string, string][]): void; + + /** + * Use `.toBeExtensible` when checking if an object is extensible. + */ + toBeExtensible(): void; + + /** + * Use `.toBeFrozen` when checking if an object is frozen. + */ + toBeFrozen(): void; + + /** + * Use `.toBeSealed` when checking if an object is sealed. + */ + toBeSealed(): void; + + /** + * Use `.toBeString` when checking if a value is a `String`. + */ + toBeString(): void; + + /** + * Use `.toEqualCaseInsensitive` when checking if a string is equal (===) to another ignoring the casing of both strings. + * + * @param {String} string + */ + toEqualCaseInsensitive(string: string): void; + + /** + * Use `.toStartWith` when checking if a `String` starts with a given `String` prefix. + * + * @param {String} prefix + */ + toStartWith(prefix: string): void; + + /** + * Use `.toEndWith` when checking if a `String` ends with a given `String` suffix. + * + * @param {String} suffix + */ + toEndWith(suffix: string): void; + + /** + * Use `.toInclude` when checking if a `String` includes the given `String` substring. + * + * @param {String} substring + */ + toInclude(substring: string): void; + + /** + * Use `.toIncludeRepeated` when checking if a `String` includes the given `String` substring the correct number of times. + * + * @param {String} substring + * @param {Number} times + */ + toIncludeRepeated(substring: string, times: number): void; + + /** + * Use `.toIncludeMultiple` when checking if a `String` includes all of the given substrings. + * + * @param {Array.} substring + */ + toIncludeMultiple(substring: string[]): void; +}; + +interface JestExpectType { + not: JestExpectType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, /** * If you have a mock function, you can use .lastCalledWith to test what * arguments it was last called with. @@ -148,10 +511,6 @@ type JestExpectType = { * strict equality. */ toBe(value: any): void, - /** - * Use .toHaveBeenCalled to ensure that a mock function got called. - */ - toBeCalled(): void, /** * Use .toBeCalledWith to ensure that a mock function was called with * specific arguments. @@ -227,21 +586,55 @@ type JestExpectType = { * Use .toHaveBeenCalled to ensure that a mock function got called. */ toHaveBeenCalled(): void, + toBeCalled(): void; /** * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact * number of times. */ toHaveBeenCalledTimes(number: number): void, + toBeCalledTimes(number: number): void; + /** + * + */ + toHaveBeenNthCalledWith(nthCall: number, ...args: Array): void; + nthCalledWith(nthCall: number, ...args: Array): void; + /** + * + */ + toHaveReturned(): void; + toReturn(): void; + /** + * + */ + toHaveReturnedTimes(number: number): void; + toReturnTimes(number: number): void; + /** + * + */ + toHaveReturnedWith(value: any): void; + toReturnWith(value: any): void; + /** + * + */ + toHaveLastReturnedWith(value: any): void; + lastReturnedWith(value: any): void; + /** + * + */ + toHaveNthReturnedWith(nthCall: number, value: any): void; + nthReturnedWith(nthCall: number, value: any): void; /** * Use .toHaveBeenCalledWith to ensure that a mock function was called with * specific arguments. */ toHaveBeenCalledWith(...args: Array): void, + toBeCalledWith(...args: Array): void, /** * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called * with specific arguments. */ toHaveBeenLastCalledWith(...args: Array): void, + lastCalledWith(...args: Array): void, /** * Check that an object has a .length property and it is set to a certain * numeric value. @@ -260,9 +653,17 @@ type JestExpectType = { */ toMatchObject(object: Object | Array): void, /** - * This ensures that a React component matches the most recent snapshot. + * Use .toStrictEqual to check that a javascript object matches a subset of the properties of an object. */ - toMatchSnapshot(name?: string): void, + toStrictEqual(value: any): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(propertyMatchers?: {[key: string]: JestAsymmetricEqualityType}, name?: string): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(name: string): void, /** * Use .toThrow to test that a function throws when it is called. * If you want to test that a specific error gets thrown, you can provide an @@ -278,7 +679,7 @@ type JestExpectType = { * matching the most recent snapshot when it is called. */ toThrowErrorMatchingSnapshot(): void -}; +} type JestObjectType = { /** @@ -391,6 +792,13 @@ type JestObjectType = { * Executes only the macro task queue (i.e. all tasks queued by setTimeout() * or setInterval() and setImmediate()). */ + advanceTimersByTime(msToRun: number): void, + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + * + * Renamed to `advanceTimersByTime`. + */ runTimersToTime(msToRun: number): void, /** * Executes only the macro-tasks that are currently pending (i.e., only the @@ -424,7 +832,7 @@ type JestObjectType = { * Creates a mock function similar to jest.fn but also tracks calls to * object[methodName]. */ - spyOn(object: Object, methodName: string): JestMockFn, + spyOn(object: Object, methodName: string, accessType?: "get" | "set"): JestMockFn, /** * Set the default timeout interval for tests and before/after hooks in milliseconds. * Note: The default timeout interval is 5 seconds if this method is not called. @@ -462,17 +870,17 @@ declare var describe: { /** * Creates a block that groups together several related tests in one "test suite" */ - (name: string, fn: () => void): void, + (name: JestTestName, fn: () => void): void, /** * Only run this describe block */ - only(name: string, fn: () => void): void, + only(name: JestTestName, fn: () => void): void, /** * Skip running this describe block */ - skip(name: string, fn: () => void): void + skip(name: JestTestName, fn: () => void): void }; /** An individual test unit */ @@ -480,54 +888,54 @@ declare var it: { /** * An individual test unit * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ ( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Only run this test * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ only( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Skip running this test * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ skip( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void, /** * Run the test concurrently * - * @param {string} Name of Test + * @param {JestTestName} Name of Test * @param {Function} Test * @param {number} Timeout for the test, in milliseconds. */ concurrent( - name: string, + name: JestTestName, fn?: (done: () => void) => ?Promise, timeout?: number ): void }; declare function fit( - name: string, + name: JestTestName, fn: (done: () => void) => ?Promise, timeout?: number ): void; @@ -542,23 +950,75 @@ declare var xit: typeof it; /** A disabled individual test */ declare var xtest: typeof it; +type JestPrettyFormatColors = { + comment: { close: string, open: string }, + content: { close: string, open: string }, + prop: { close: string, open: string }, + tag: { close: string, open: string }, + value: { close: string, open: string }, +}; + +type JestPrettyFormatIndent = string => string; +type JestPrettyFormatRefs = Array; +type JestPrettyFormatPrint = any => string; +type JestPrettyFormatStringOrNull = string | null; + +type JestPrettyFormatOptions = {| + callToJSON: boolean, + edgeSpacing: string, + escapeRegex: boolean, + highlight: boolean, + indent: number, + maxDepth: number, + min: boolean, + plugins: JestPrettyFormatPlugins, + printFunctionName: boolean, + spacing: string, + theme: {| + comment: string, + content: string, + prop: string, + tag: string, + value: string, + |}, +|}; + +type JestPrettyFormatPlugin = { + print: ( + val: any, + serialize: JestPrettyFormatPrint, + indent: JestPrettyFormatIndent, + opts: JestPrettyFormatOptions, + colors: JestPrettyFormatColors, + ) => string, + test: any => boolean, +}; + +type JestPrettyFormatPlugins = Array; + /** The expect function is used every time you want to test a value */ declare var expect: { /** The object that you want to make assertions against */ - (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType, + (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType & DomTestingLibraryType & JestJQueryMatchersType & JestExtendedMatchersType, /** Add additional Jasmine matchers to Jest's roster */ extend(matchers: { [name: string]: JestMatcher }): void, /** Add a module that formats application-specific data structures. */ - addSnapshotSerializer(serializer: (input: Object) => string): void, + addSnapshotSerializer(pluginModule: JestPrettyFormatPlugin): void, assertions(expectedAssertions: number): void, hasAssertions(): void, any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, - objectContaining(value: Object): void, + anything(): any, + arrayContaining(value: Array): Array, + objectContaining(value: Object): Object, /** Matches any received string that contains the exact expected string. */ - stringContaining(value: string): void, - stringMatching(value: string | RegExp): void + stringContaining(value: string): string, + stringMatching(value: string | RegExp): string, + not: { + arrayContaining: (value: $ReadOnlyArray) => Array, + objectContaining: (value: {}) => Object, + stringContaining: (value: string) => string, + stringMatching: (value: string | RegExp) => string, + }, }; // TODO handle return type @@ -575,14 +1035,14 @@ declare var jest: JestObjectType; declare var jasmine: { DEFAULT_TIMEOUT_INTERVAL: number, any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, + anything(): any, + arrayContaining(value: Array): Array, clock(): JestClockType, createSpy(name: string): JestSpyType, createSpyObj( baseName: string, methodNames: Array ): { [methodName: string]: JestSpyType }, - objectContaining(value: Object): void, - stringMatching(value: string): void + objectContaining(value: Object): Object, + stringMatching(value: string): string }; diff --git a/package.json b/package.json index 005352639..c96760d10 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "babel-cli": "6.26.0", "babel-core": "6.26.0", "babel-eslint": "8.2.2", - "babel-jest": "22.4.3", + "babel-jest": "23.2.0", "babel-loader": "7.1.4", "babel-plugin-flow-runtime": "0.17.0", "babel-plugin-syntax-dynamic-import": "6.18.0", @@ -82,14 +82,14 @@ "element-theme-default": "1.4.13", "enzyme": "3.3.0", "enzyme-adapter-react-16": "1.1.1", - "eslint": "4.18.2", + "eslint": "5.0.1", "eslint-config-google": "0.9.1", "eslint-loader": "2.0.0", "eslint-plugin-babel": "4.1.2", - "eslint-plugin-flowtype": "2.46.1", - "eslint-plugin-import": "2.9.0", - "eslint-plugin-jest": "21.14.0", - "eslint-plugin-react": "7.7.0", + "eslint-plugin-flowtype": "2.49.3", + "eslint-plugin-import": "2.13.0", + "eslint-plugin-jest": "21.17.0", + "eslint-plugin-react": "7.10.0", "file-loader": "1.1.11", "flow-bin": "0.69.0", "flow-runtime": "0.17.0", @@ -99,10 +99,10 @@ "husky": "0.15.0-rc.8", "identity-obj-proxy": "3.0.0", "in-publish": "2.0.0", - "jest": "22.4.3", - "jest-environment-jsdom": "22.4.3", - "jest-environment-jsdom-global": "1.0.3", - "jest-environment-node": "22.4.3", + "jest": "23.2.0", + "jest-environment-jsdom": "23.2.0", + "jest-environment-jsdom-global": "1.1.0", + "jest-environment-node": "23.2.0", "localstorage-memory": "1.0.2", "mini-css-extract-plugin": "0.4.0", "node-mocks-http": "1.6.7", diff --git a/yarn.lock b/yarn.lock index 687249e56d3a04a1901554668523daa204d43219..49bc26051c0b68dbe8cd1c3686f189d67bb01310 100644 GIT binary patch delta 7867 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 delta 6772 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