From 71469e62447ff0783206d171362217498f4d69cd Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 15:30:57 +0000 Subject: [PATCH 01/35] Move Jest setup files to /dev/ subdirectory --- package.json | 8 ++++---- scripts/jest/{ => dev}/environment.js | 0 scripts/jest/{ => dev}/jest.d.ts | 0 scripts/jest/{ => dev}/preprocessor.js | 6 +++--- scripts/jest/{ => dev}/setup.js | 0 scripts/jest/{ => dev}/setupSpecEquivalenceReporter.js | 0 scripts/jest/{ => dev}/test-framework-setup.js | 0 scripts/jest/{ => dev}/ts-preprocessor.js | 0 8 files changed, 7 insertions(+), 7 deletions(-) rename scripts/jest/{ => dev}/environment.js (100%) rename scripts/jest/{ => dev}/jest.d.ts (100%) rename scripts/jest/{ => dev}/preprocessor.js (91%) rename scripts/jest/{ => dev}/setup.js (100%) rename scripts/jest/{ => dev}/setupSpecEquivalenceReporter.js (100%) rename scripts/jest/{ => dev}/test-framework-setup.js (100%) rename scripts/jest/{ => dev}/ts-preprocessor.js (100%) diff --git a/package.json b/package.json index 901af480d2a..330f3e6d788 100644 --- a/package.json +++ b/package.json @@ -114,13 +114,13 @@ "/scripts/bench/" ], "transform": { - ".*": "./scripts/jest/preprocessor.js" + ".*": "./scripts/jest/dev/preprocessor.js" }, "setupFiles": [ - "./scripts/jest/setup.js", - "./scripts/jest/environment.js" + "./scripts/jest/dev/setup.js", + "./scripts/jest/dev/environment.js" ], - "setupTestFrameworkScriptFile": "./scripts/jest/test-framework-setup.js", + "setupTestFrameworkScriptFile": "./scripts/jest/dev/test-framework-setup.js", "testRegex": "/__tests__/.*(\\.js|\\.coffee|[^d]\\.ts)$", "moduleFileExtensions": [ "js", diff --git a/scripts/jest/environment.js b/scripts/jest/dev/environment.js similarity index 100% rename from scripts/jest/environment.js rename to scripts/jest/dev/environment.js diff --git a/scripts/jest/jest.d.ts b/scripts/jest/dev/jest.d.ts similarity index 100% rename from scripts/jest/jest.d.ts rename to scripts/jest/dev/jest.d.ts diff --git a/scripts/jest/preprocessor.js b/scripts/jest/dev/preprocessor.js similarity index 91% rename from scripts/jest/preprocessor.js rename to scripts/jest/dev/preprocessor.js index 074b6252d64..d919d7d53a4 100644 --- a/scripts/jest/preprocessor.js +++ b/scripts/jest/dev/preprocessor.js @@ -17,13 +17,13 @@ var pathToBabel = path.join( 'package.json' ); var pathToBabelPluginDevWithCode = require.resolve( - '../error-codes/replace-invariant-error-codes' + '../../error-codes/replace-invariant-error-codes' ); var pathToBabelPluginAsyncToGenerator = require.resolve( 'babel-plugin-transform-async-to-generator' ); -var pathToBabelrc = path.join(__dirname, '..', '..', '.babelrc'); -var pathToErrorCodes = require.resolve('../error-codes/codes.json'); +var pathToBabelrc = path.join(__dirname, '..', '..', '..', '.babelrc'); +var pathToErrorCodes = require.resolve('../../error-codes/codes.json'); var babelOptions = { plugins: [ diff --git a/scripts/jest/setup.js b/scripts/jest/dev/setup.js similarity index 100% rename from scripts/jest/setup.js rename to scripts/jest/dev/setup.js diff --git a/scripts/jest/setupSpecEquivalenceReporter.js b/scripts/jest/dev/setupSpecEquivalenceReporter.js similarity index 100% rename from scripts/jest/setupSpecEquivalenceReporter.js rename to scripts/jest/dev/setupSpecEquivalenceReporter.js diff --git a/scripts/jest/test-framework-setup.js b/scripts/jest/dev/test-framework-setup.js similarity index 100% rename from scripts/jest/test-framework-setup.js rename to scripts/jest/dev/test-framework-setup.js diff --git a/scripts/jest/ts-preprocessor.js b/scripts/jest/dev/ts-preprocessor.js similarity index 100% rename from scripts/jest/ts-preprocessor.js rename to scripts/jest/dev/ts-preprocessor.js From d97968f82769d74c9ac745953f07af9803726fc9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 15:31:26 +0000 Subject: [PATCH 02/35] Clone Jest /dev/ files into /prod/ --- scripts/jest/prod/environment.js | 21 ++++ scripts/jest/prod/jest.d.ts | 72 ++++++++++++++ scripts/jest/prod/preprocessor.js | 80 ++++++++++++++++ scripts/jest/prod/setup.js | 12 +++ .../jest/prod/setupSpecEquivalenceReporter.js | 29 ++++++ scripts/jest/prod/test-framework-setup.js | 73 ++++++++++++++ scripts/jest/prod/ts-preprocessor.js | 96 +++++++++++++++++++ 7 files changed, 383 insertions(+) create mode 100644 scripts/jest/prod/environment.js create mode 100644 scripts/jest/prod/jest.d.ts create mode 100644 scripts/jest/prod/preprocessor.js create mode 100644 scripts/jest/prod/setup.js create mode 100644 scripts/jest/prod/setupSpecEquivalenceReporter.js create mode 100644 scripts/jest/prod/test-framework-setup.js create mode 100644 scripts/jest/prod/ts-preprocessor.js diff --git a/scripts/jest/prod/environment.js b/scripts/jest/prod/environment.js new file mode 100644 index 00000000000..ba3bbb81cf2 --- /dev/null +++ b/scripts/jest/prod/environment.js @@ -0,0 +1,21 @@ +/* eslint-disable */ +global.__DEV__ = true; + +// For testing DOM Fiber. +global.requestAnimationFrame = function(callback) { + setTimeout(callback); +}; + +global.requestIdleCallback = function(callback) { + return setTimeout(() => { + callback({ + timeRemaining() { + return Infinity; + }, + }); + }); +}; + +global.cancelIdleCallback = function(callbackID) { + clearTimeout(callbackID); +}; diff --git a/scripts/jest/prod/jest.d.ts b/scripts/jest/prod/jest.d.ts new file mode 100644 index 00000000000..c3e69596f80 --- /dev/null +++ b/scripts/jest/prod/jest.d.ts @@ -0,0 +1,72 @@ +declare var jasmine: any; + +declare function afterEach(fn: any): any; +declare function beforeEach(fn: any): any; +declare function describe(name: string, fn: any): void; +declare var it: { + (name: string, fn: any): void; + only: (name: string, fn: any) => void; +} +declare function expect(val: any): Expect; +declare var jest: Jest; +declare function pit(name: string, fn: any): void; +declare function spyOn(obj: any, key: string): any; +declare function xdescribe(name: string, fn: any): void; +declare function xit(name: string, fn: any): void; + +interface Expect { + not: Expect + toThrow(message?: string): void + toThrowError(message?: string): void + toBe(value: any): void + toEqual(value: any): void + toBeFalsy(): void + toBeTruthy(): void + toBeNull(): void + toBeUndefined(): void + toBeDefined(): void + toMatch(regexp: RegExp): void + toContain(string: string): void + toBeCloseTo(number: number, delta: number): void + toBeGreaterThan(number: number): void + toBeLessThan(number: number): void + toBeCalled(): void + toBeCalledWith(...arguments): void + lastCalledWith(...arguments): void +} + +interface Jest { + autoMockOff(): void + autoMockOn(): void + clearAllTimers(): void + dontMock(moduleName: string): void + genMockFromModule(moduleObj: Object): Object + genMockFunction(): MockFunction + genMockFn(): MockFunction + mock(moduleName: string): void + runAllTicks(): void + runAllTimers(): void + runOnlyPendingTimers(): void + setMock(moduleName: string, moduleExports: Object): void +} + +interface MockFunction { + (...arguments): any + mock: { + calls: Array> + instances: Array + } + mockClear(): void + mockImplementation(fn: Function): MockFunction + mockImpl(fn: Function): MockFunction + mockReturnThis(): MockFunction + mockReturnValue(value: any): MockFunction + mockReturnValueOnce(value: any): MockFunction +} + +// Allow importing jasmine-check +declare module 'jasmine-check' { + export function install(global?: any): void; +} +declare var check: any; +declare var gen: any; diff --git a/scripts/jest/prod/preprocessor.js b/scripts/jest/prod/preprocessor.js new file mode 100644 index 00000000000..d919d7d53a4 --- /dev/null +++ b/scripts/jest/prod/preprocessor.js @@ -0,0 +1,80 @@ +'use strict'; + +process.env.NODE_ENV = 'development'; + +var path = require('path'); + +var babel = require('babel-core'); +var coffee = require('coffee-script'); + +var tsPreprocessor = require('./ts-preprocessor'); +var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction'); + +// Use require.resolve to be resilient to file moves, npm updates, etc +var pathToBabel = path.join( + require.resolve('babel-core'), + '..', + 'package.json' +); +var pathToBabelPluginDevWithCode = require.resolve( + '../../error-codes/replace-invariant-error-codes' +); +var pathToBabelPluginAsyncToGenerator = require.resolve( + 'babel-plugin-transform-async-to-generator' +); +var pathToBabelrc = path.join(__dirname, '..', '..', '..', '.babelrc'); +var pathToErrorCodes = require.resolve('../../error-codes/codes.json'); + +var babelOptions = { + plugins: [ + // For Node environment only. For builds, Rollup takes care of ESM. + require.resolve('babel-plugin-transform-es2015-modules-commonjs'), + + pathToBabelPluginDevWithCode, + // Keep stacks detailed in tests. + // Don't put this in .babelrc so that we don't embed filenames + // into ReactART builds that include JSX. + // TODO: I have not verified that this actually works. + require.resolve('babel-plugin-transform-react-jsx-source'), + ], + retainLines: true, +}; + +module.exports = { + process: function(src, filePath) { + if (filePath.match(/\.coffee$/)) { + return coffee.compile(src, {bare: true}); + } + if (filePath.match(/\.ts$/) && !filePath.match(/\.d\.ts$/)) { + return tsPreprocessor.compile(src, filePath); + } + if (!filePath.match(/\/third_party\//)) { + // for test files, we also apply the async-await transform, but we want to + // make sure we don't accidentally apply that transform to product code. + var isTestFile = !!filePath.match(/\/__tests__\//); + return babel.transform( + src, + Object.assign( + {filename: path.relative(process.cwd(), filePath)}, + babelOptions, + isTestFile + ? { + plugins: [pathToBabelPluginAsyncToGenerator].concat( + babelOptions.plugins + ), + } + : {} + ) + ).code; + } + return src; + }, + + getCacheKey: createCacheKeyFunction([ + __filename, + pathToBabel, + pathToBabelrc, + pathToBabelPluginDevWithCode, + pathToErrorCodes, + ]), +}; diff --git a/scripts/jest/prod/setup.js b/scripts/jest/prod/setup.js new file mode 100644 index 00000000000..63bdc9fbbf6 --- /dev/null +++ b/scripts/jest/prod/setup.js @@ -0,0 +1,12 @@ +'use strict'; + +jest.mock('shared/ReactFeatureFlags', () => { + // We can alter flags based on environment here + // (e.g. for CI runs with different flags). + return require.requireActual('shared/ReactFeatureFlags'); +}); + +// Error logging varies between Fiber and Stack; +// Rather than fork dozens of tests, mock the error-logging file by default. +// TODO: direct imports like some-package/src/* are bad. Fix me. +jest.mock('react-reconciler/src/ReactFiberErrorLogger'); diff --git a/scripts/jest/prod/setupSpecEquivalenceReporter.js b/scripts/jest/prod/setupSpecEquivalenceReporter.js new file mode 100644 index 00000000000..d27c2e61eea --- /dev/null +++ b/scripts/jest/prod/setupSpecEquivalenceReporter.js @@ -0,0 +1,29 @@ +/*! + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +'use strict'; + +var expect = global.expect; + +var numExpectations = 0; + +global.expect = function() { + numExpectations += 1; + return expect.apply(this, arguments); +}; + +beforeEach(() => (numExpectations = 0)); + +jasmine.currentEnv_.addReporter({ + specDone: spec => { + console.log( + `EQUIVALENCE: ${spec.description}, ` + + `status: ${spec.status}, ` + + `numExpectations: ${numExpectations}` + ); + }, +}); diff --git a/scripts/jest/prod/test-framework-setup.js b/scripts/jest/prod/test-framework-setup.js new file mode 100644 index 00000000000..28ec0defae8 --- /dev/null +++ b/scripts/jest/prod/test-framework-setup.js @@ -0,0 +1,73 @@ +'use strict'; + +if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { + // Inside the class equivalence tester, we have a custom environment, let's + // require that instead. + require('./setupSpecEquivalenceReporter.js'); +} else { + var env = jasmine.getEnv(); + + // TODO: Stop using spyOn in all the test since that seem deprecated. + // This is a legacy upgrade path strategy from: + // https://github.com/facebook/jest/blob/v20.0.4/packages/jest-matchers/src/spyMatchers.js#L160 + const isSpy = spy => spy.calls && typeof spy.calls.count === 'function'; + + ['error', 'warn'].forEach(methodName => { + var oldMethod = console[methodName]; + var newMethod = function() { + newMethod.__callCount++; + oldMethod.apply(this, arguments); + }; + newMethod.__callCount = 0; + console[methodName] = newMethod; + + env.beforeEach(() => { + newMethod.__callCount = 0; + }); + + env.afterEach(() => { + if (console[methodName] !== newMethod && !isSpy(console[methodName])) { + throw new Error( + 'Test did not tear down console.' + methodName + ' mock properly.' + ); + } + if (console[methodName].__callCount !== 0) { + throw new Error( + 'Expected test not to call console.' + + methodName + + '(). ' + + 'If the warning is expected, mock it out using ' + + "spyOn(console, '" + + methodName + + "') and test that the " + + 'warning occurs.' + ); + } + }); + }); + + var wrapDevMatcher = function(obj, name) { + const original = obj[name]; + obj[name] = function devMatcher() { + try { + original.apply(this, arguments); + } catch (e) { + global.__hadDevFailures = e.stack; + } + }; + }; + + const expectDev = function expectDev(actual) { + const expectation = expect(actual); + if (global.__suppressDevFailures) { + Object.keys(expectation).forEach(name => { + wrapDevMatcher(expectation, name); + wrapDevMatcher(expectation.not, name); + }); + } + return expectation; + }; + global.expectDev = expectDev; + + require('jasmine-check').install(); +} diff --git a/scripts/jest/prod/ts-preprocessor.js b/scripts/jest/prod/ts-preprocessor.js new file mode 100644 index 00000000000..1345888c2c0 --- /dev/null +++ b/scripts/jest/prod/ts-preprocessor.js @@ -0,0 +1,96 @@ +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var ts = require('typescript'); + +var tsOptions = { + module: ts.ModuleKind.CommonJS, + jsx: ts.JsxEmit.React, +}; + +function formatErrorMessage(error) { + return ( + error.file.filename + + '(' + + error.file.getLineAndCharacterOfPosition(error.start).line + + '): ' + + error.messageText + ); +} + +function compile(content, contentFilename) { + var output = null; + var compilerHost = { + getSourceFile(filename, languageVersion) { + var source; + var jestRegex = /jest\.d\.ts/; + var reactRegex = /(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$/; + + // `path.normalize` is used to turn forward slashes in + // the file path into backslashes on Windows. + filename = path.normalize(filename); + if (filename === 'lib.d.ts') { + source = fs + .readFileSync(require.resolve('typescript/lib/lib.d.ts')) + .toString(); + } else if (filename.match(jestRegex)) { + source = fs.readFileSync(path.join(__dirname, 'jest.d.ts')).toString(); + } else if (filename === contentFilename) { + source = content; + } else if (reactRegex.test(filename)) { + // TypeScript will look for the .d.ts files in each ancestor directory, + // so there may not be a file at the referenced path as it climbs the + // hierarchy. + try { + source = fs.readFileSync(filename).toString(); + } catch (e) { + if (e.code === 'ENOENT') { + return undefined; + } + throw e; + } + } else { + throw new Error('Unexpected filename ' + filename); + } + return ts.createSourceFile(filename, source, 'ES5', '0'); + }, + writeFile(name, text, writeByteOrderMark) { + if (output === null) { + output = text; + } else { + throw new Error('Expected only one dependency.'); + } + }, + getCanonicalFileName(filename) { + return filename; + }, + getCurrentDirectory() { + return ''; + }, + getNewLine() { + return '\n'; + }, + fileExists(filename) { + return ts.sys.fileExists(filename); + }, + useCaseSensitiveFileNames() { + return ts.sys.useCaseSensitiveFileNames; + }, + }; + var program = ts.createProgram( + ['lib.d.ts', 'jest.d.ts', contentFilename], + tsOptions, + compilerHost + ); + var emitResult = program.emit(); + var errors = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + if (errors.length) { + throw new Error(errors.map(formatErrorMessage).join('\n')); + } + return output; +} + +module.exports = { + compile: compile, +}; From 3c7438affca83aaef9726fef2ca7595dc340f998 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 15:34:59 +0000 Subject: [PATCH 03/35] Move shared code into scripts/jest --- scripts/jest/dev/preprocessor.js | 2 +- scripts/jest/dev/test-framework-setup.js | 2 +- scripts/jest/{dev => }/jest.d.ts | 0 scripts/jest/prod/jest.d.ts | 72 -------------- scripts/jest/prod/preprocessor.js | 2 +- .../jest/prod/setupSpecEquivalenceReporter.js | 29 ------ scripts/jest/prod/test-framework-setup.js | 2 +- scripts/jest/prod/ts-preprocessor.js | 96 ------------------- .../{dev => }/setupSpecEquivalenceReporter.js | 0 scripts/jest/{dev => }/ts-preprocessor.js | 0 10 files changed, 4 insertions(+), 201 deletions(-) rename scripts/jest/{dev => }/jest.d.ts (100%) delete mode 100644 scripts/jest/prod/jest.d.ts delete mode 100644 scripts/jest/prod/setupSpecEquivalenceReporter.js delete mode 100644 scripts/jest/prod/ts-preprocessor.js rename scripts/jest/{dev => }/setupSpecEquivalenceReporter.js (100%) rename scripts/jest/{dev => }/ts-preprocessor.js (100%) diff --git a/scripts/jest/dev/preprocessor.js b/scripts/jest/dev/preprocessor.js index d919d7d53a4..d181d3ac5c3 100644 --- a/scripts/jest/dev/preprocessor.js +++ b/scripts/jest/dev/preprocessor.js @@ -7,7 +7,7 @@ var path = require('path'); var babel = require('babel-core'); var coffee = require('coffee-script'); -var tsPreprocessor = require('./ts-preprocessor'); +var tsPreprocessor = require('../ts-preprocessor'); var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction'); // Use require.resolve to be resilient to file moves, npm updates, etc diff --git a/scripts/jest/dev/test-framework-setup.js b/scripts/jest/dev/test-framework-setup.js index 28ec0defae8..8d2974a4871 100644 --- a/scripts/jest/dev/test-framework-setup.js +++ b/scripts/jest/dev/test-framework-setup.js @@ -3,7 +3,7 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { // Inside the class equivalence tester, we have a custom environment, let's // require that instead. - require('./setupSpecEquivalenceReporter.js'); + require('../setupSpecEquivalenceReporter.js'); } else { var env = jasmine.getEnv(); diff --git a/scripts/jest/dev/jest.d.ts b/scripts/jest/jest.d.ts similarity index 100% rename from scripts/jest/dev/jest.d.ts rename to scripts/jest/jest.d.ts diff --git a/scripts/jest/prod/jest.d.ts b/scripts/jest/prod/jest.d.ts deleted file mode 100644 index c3e69596f80..00000000000 --- a/scripts/jest/prod/jest.d.ts +++ /dev/null @@ -1,72 +0,0 @@ -declare var jasmine: any; - -declare function afterEach(fn: any): any; -declare function beforeEach(fn: any): any; -declare function describe(name: string, fn: any): void; -declare var it: { - (name: string, fn: any): void; - only: (name: string, fn: any) => void; -} -declare function expect(val: any): Expect; -declare var jest: Jest; -declare function pit(name: string, fn: any): void; -declare function spyOn(obj: any, key: string): any; -declare function xdescribe(name: string, fn: any): void; -declare function xit(name: string, fn: any): void; - -interface Expect { - not: Expect - toThrow(message?: string): void - toThrowError(message?: string): void - toBe(value: any): void - toEqual(value: any): void - toBeFalsy(): void - toBeTruthy(): void - toBeNull(): void - toBeUndefined(): void - toBeDefined(): void - toMatch(regexp: RegExp): void - toContain(string: string): void - toBeCloseTo(number: number, delta: number): void - toBeGreaterThan(number: number): void - toBeLessThan(number: number): void - toBeCalled(): void - toBeCalledWith(...arguments): void - lastCalledWith(...arguments): void -} - -interface Jest { - autoMockOff(): void - autoMockOn(): void - clearAllTimers(): void - dontMock(moduleName: string): void - genMockFromModule(moduleObj: Object): Object - genMockFunction(): MockFunction - genMockFn(): MockFunction - mock(moduleName: string): void - runAllTicks(): void - runAllTimers(): void - runOnlyPendingTimers(): void - setMock(moduleName: string, moduleExports: Object): void -} - -interface MockFunction { - (...arguments): any - mock: { - calls: Array> - instances: Array - } - mockClear(): void - mockImplementation(fn: Function): MockFunction - mockImpl(fn: Function): MockFunction - mockReturnThis(): MockFunction - mockReturnValue(value: any): MockFunction - mockReturnValueOnce(value: any): MockFunction -} - -// Allow importing jasmine-check -declare module 'jasmine-check' { - export function install(global?: any): void; -} -declare var check: any; -declare var gen: any; diff --git a/scripts/jest/prod/preprocessor.js b/scripts/jest/prod/preprocessor.js index d919d7d53a4..d181d3ac5c3 100644 --- a/scripts/jest/prod/preprocessor.js +++ b/scripts/jest/prod/preprocessor.js @@ -7,7 +7,7 @@ var path = require('path'); var babel = require('babel-core'); var coffee = require('coffee-script'); -var tsPreprocessor = require('./ts-preprocessor'); +var tsPreprocessor = require('../ts-preprocessor'); var createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction'); // Use require.resolve to be resilient to file moves, npm updates, etc diff --git a/scripts/jest/prod/setupSpecEquivalenceReporter.js b/scripts/jest/prod/setupSpecEquivalenceReporter.js deleted file mode 100644 index d27c2e61eea..00000000000 --- a/scripts/jest/prod/setupSpecEquivalenceReporter.js +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * Copyright (c) 2015-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -var expect = global.expect; - -var numExpectations = 0; - -global.expect = function() { - numExpectations += 1; - return expect.apply(this, arguments); -}; - -beforeEach(() => (numExpectations = 0)); - -jasmine.currentEnv_.addReporter({ - specDone: spec => { - console.log( - `EQUIVALENCE: ${spec.description}, ` + - `status: ${spec.status}, ` + - `numExpectations: ${numExpectations}` - ); - }, -}); diff --git a/scripts/jest/prod/test-framework-setup.js b/scripts/jest/prod/test-framework-setup.js index 28ec0defae8..8d2974a4871 100644 --- a/scripts/jest/prod/test-framework-setup.js +++ b/scripts/jest/prod/test-framework-setup.js @@ -3,7 +3,7 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { // Inside the class equivalence tester, we have a custom environment, let's // require that instead. - require('./setupSpecEquivalenceReporter.js'); + require('../setupSpecEquivalenceReporter.js'); } else { var env = jasmine.getEnv(); diff --git a/scripts/jest/prod/ts-preprocessor.js b/scripts/jest/prod/ts-preprocessor.js deleted file mode 100644 index 1345888c2c0..00000000000 --- a/scripts/jest/prod/ts-preprocessor.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var path = require('path'); -var ts = require('typescript'); - -var tsOptions = { - module: ts.ModuleKind.CommonJS, - jsx: ts.JsxEmit.React, -}; - -function formatErrorMessage(error) { - return ( - error.file.filename + - '(' + - error.file.getLineAndCharacterOfPosition(error.start).line + - '): ' + - error.messageText - ); -} - -function compile(content, contentFilename) { - var output = null; - var compilerHost = { - getSourceFile(filename, languageVersion) { - var source; - var jestRegex = /jest\.d\.ts/; - var reactRegex = /(?:React|ReactDOM|PropTypes)(?:\.d)?\.ts$/; - - // `path.normalize` is used to turn forward slashes in - // the file path into backslashes on Windows. - filename = path.normalize(filename); - if (filename === 'lib.d.ts') { - source = fs - .readFileSync(require.resolve('typescript/lib/lib.d.ts')) - .toString(); - } else if (filename.match(jestRegex)) { - source = fs.readFileSync(path.join(__dirname, 'jest.d.ts')).toString(); - } else if (filename === contentFilename) { - source = content; - } else if (reactRegex.test(filename)) { - // TypeScript will look for the .d.ts files in each ancestor directory, - // so there may not be a file at the referenced path as it climbs the - // hierarchy. - try { - source = fs.readFileSync(filename).toString(); - } catch (e) { - if (e.code === 'ENOENT') { - return undefined; - } - throw e; - } - } else { - throw new Error('Unexpected filename ' + filename); - } - return ts.createSourceFile(filename, source, 'ES5', '0'); - }, - writeFile(name, text, writeByteOrderMark) { - if (output === null) { - output = text; - } else { - throw new Error('Expected only one dependency.'); - } - }, - getCanonicalFileName(filename) { - return filename; - }, - getCurrentDirectory() { - return ''; - }, - getNewLine() { - return '\n'; - }, - fileExists(filename) { - return ts.sys.fileExists(filename); - }, - useCaseSensitiveFileNames() { - return ts.sys.useCaseSensitiveFileNames; - }, - }; - var program = ts.createProgram( - ['lib.d.ts', 'jest.d.ts', contentFilename], - tsOptions, - compilerHost - ); - var emitResult = program.emit(); - var errors = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); - if (errors.length) { - throw new Error(errors.map(formatErrorMessage).join('\n')); - } - return output; -} - -module.exports = { - compile: compile, -}; diff --git a/scripts/jest/dev/setupSpecEquivalenceReporter.js b/scripts/jest/setupSpecEquivalenceReporter.js similarity index 100% rename from scripts/jest/dev/setupSpecEquivalenceReporter.js rename to scripts/jest/setupSpecEquivalenceReporter.js diff --git a/scripts/jest/dev/ts-preprocessor.js b/scripts/jest/ts-preprocessor.js similarity index 100% rename from scripts/jest/dev/ts-preprocessor.js rename to scripts/jest/ts-preprocessor.js From 6e6ce2662f6c4bdb80bde771dea38a567aacafae Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 15:53:38 +0000 Subject: [PATCH 04/35] Move Jest config into the scripts folder --- package.json | 32 +------------------------------- scripts/jest/dev/config.js | 22 ++++++++++++++++++++++ scripts/jest/prod/config.js | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 scripts/jest/dev/config.js create mode 100644 scripts/jest/prod/config.js diff --git a/package.json b/package.json index 330f3e6d788..b6158b8179f 100644 --- a/package.json +++ b/package.json @@ -102,40 +102,10 @@ "linc": "node ./scripts/tasks/linc.js", "lint": "node ./scripts/tasks/eslint.js", "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", - "test": "jest", + "test": "jest --config scripts/jest/dev/config.js", "flow": "node ./scripts/tasks/flow.js", "prettier": "node ./scripts/prettier/index.js write-changed", "prettier-all": "node ./scripts/prettier/index.js write", "version-check": "node ./scripts/tasks/version-check.js" - }, - "jest": { - "modulePathIgnorePatterns": [ - "/scripts/rollup/shims/", - "/scripts/bench/" - ], - "transform": { - ".*": "./scripts/jest/dev/preprocessor.js" - }, - "setupFiles": [ - "./scripts/jest/dev/setup.js", - "./scripts/jest/dev/environment.js" - ], - "setupTestFrameworkScriptFile": "./scripts/jest/dev/test-framework-setup.js", - "testRegex": "/__tests__/.*(\\.js|\\.coffee|[^d]\\.ts)$", - "moduleFileExtensions": [ - "js", - "json", - "node", - "coffee", - "ts" - ], - "roots": [ - "/packages", - "/scripts" - ], - "collectCoverageFrom": [ - "packages/**/*.js" - ], - "timers": "fake" } } diff --git a/scripts/jest/dev/config.js b/scripts/jest/dev/config.js new file mode 100644 index 00000000000..bdb92820f16 --- /dev/null +++ b/scripts/jest/dev/config.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = { + modulePathIgnorePatterns: [ + '/scripts/rollup/shims/', + '/scripts/bench/', + ], + transform: { + '.*': require.resolve('./preprocessor.js'), + }, + setupFiles: [ + require.resolve('./setup.js'), + require.resolve('./environment.js'), + ], + setupTestFrameworkScriptFile: require.resolve('./test-framework-setup.js'), + testRegex: '/__tests__/.*(\\.js|\\.coffee|[^d]\\.ts)$', + moduleFileExtensions: ['js', 'json', 'node', 'coffee', 'ts'], + rootDir: process.cwd(), + roots: ['/packages', '/scripts'], + collectCoverageFrom: ['packages/**/*.js'], + timers: 'fake', +}; diff --git a/scripts/jest/prod/config.js b/scripts/jest/prod/config.js new file mode 100644 index 00000000000..bdb92820f16 --- /dev/null +++ b/scripts/jest/prod/config.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = { + modulePathIgnorePatterns: [ + '/scripts/rollup/shims/', + '/scripts/bench/', + ], + transform: { + '.*': require.resolve('./preprocessor.js'), + }, + setupFiles: [ + require.resolve('./setup.js'), + require.resolve('./environment.js'), + ], + setupTestFrameworkScriptFile: require.resolve('./test-framework-setup.js'), + testRegex: '/__tests__/.*(\\.js|\\.coffee|[^d]\\.ts)$', + moduleFileExtensions: ['js', 'json', 'node', 'coffee', 'ts'], + rootDir: process.cwd(), + roots: ['/packages', '/scripts'], + collectCoverageFrom: ['packages/**/*.js'], + timers: 'fake', +}; From d751fe58dbdabf91f46001de538a63669c413df1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 16:14:21 +0000 Subject: [PATCH 05/35] Fix the equivalence test It fails because the config is now passed to Jest explicitly. But the test doesn't know about the config. To fix this, we just run it via `yarn test` (which includes the config). We already depend on Yarn for development anyway. --- packages/react/src/__tests__/ReactClassEquivalence-test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react/src/__tests__/ReactClassEquivalence-test.js b/packages/react/src/__tests__/ReactClassEquivalence-test.js index 0dcb608976e..81fd3c6bebe 100644 --- a/packages/react/src/__tests__/ReactClassEquivalence-test.js +++ b/packages/react/src/__tests__/ReactClassEquivalence-test.js @@ -10,7 +10,6 @@ 'use strict'; var spawnSync = require('child_process').spawnSync; -var path = require('path'); describe('ReactClassEquivalence', () => { it('tests the same thing for es6 classes and CoffeeScript', () => { @@ -28,9 +27,7 @@ describe('ReactClassEquivalence', () => { function runJest(testFile) { var cwd = process.cwd(); - var extension = process.platform === 'win32' ? '.cmd' : ''; - var jestBin = path.resolve('node_modules', '.bin', 'jest' + extension); - var result = spawnSync(jestBin, [testFile], { + var result = spawnSync('yarn', ['test', testFile], { cwd, env: Object.assign({}, process.env, { REACT_CLASS_EQUIVALENCE_TEST: 'true', From f43aaf593e0e804f9deeae2eab36aa88169b1ec4 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 16:18:27 +0000 Subject: [PATCH 06/35] Add yarn test-prod to run Jest with production environment --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index b6158b8179f..f723a6b6ebd 100644 --- a/package.json +++ b/package.json @@ -103,6 +103,7 @@ "lint": "node ./scripts/tasks/eslint.js", "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json", "test": "jest --config scripts/jest/dev/config.js", + "test-prod": "jest --config scripts/jest/prod/config.js", "flow": "node ./scripts/tasks/flow.js", "prettier": "node ./scripts/prettier/index.js write-changed", "prettier-all": "node ./scripts/prettier/index.js write", From 991e35989bf9473f2dff1e2eddfe465e202657bc Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 16:33:44 +0000 Subject: [PATCH 07/35] Actually flip the production tests to run in prod environment This produces a bunch of errors: Test Suites: 64 failed, 58 passed, 122 total Tests: 740 failed, 26 skipped, 1809 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total --- scripts/jest/prod/environment.js | 2 +- scripts/jest/prod/preprocessor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jest/prod/environment.js b/scripts/jest/prod/environment.js index ba3bbb81cf2..ea31c446418 100644 --- a/scripts/jest/prod/environment.js +++ b/scripts/jest/prod/environment.js @@ -1,5 +1,5 @@ /* eslint-disable */ -global.__DEV__ = true; +global.__DEV__ = false; // For testing DOM Fiber. global.requestAnimationFrame = function(callback) { diff --git a/scripts/jest/prod/preprocessor.js b/scripts/jest/prod/preprocessor.js index d181d3ac5c3..839ecd27492 100644 --- a/scripts/jest/prod/preprocessor.js +++ b/scripts/jest/prod/preprocessor.js @@ -1,6 +1,6 @@ 'use strict'; -process.env.NODE_ENV = 'development'; +process.env.NODE_ENV = 'production'; var path = require('path'); From ae39a60bec9f45db8a8d7aa3221a75e581d7a4ee Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 16:42:14 +0000 Subject: [PATCH 08/35] Ignore expectDev() calls in production Down from 740 to 175 failed. Test Suites: 44 failed, 78 passed, 122 total Tests: 175 failed, 26 skipped, 2374 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total --- scripts/jest/dev/test-framework-setup.js | 24 ++--------------------- scripts/jest/prod/test-framework-setup.js | 12 +++++------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/scripts/jest/dev/test-framework-setup.js b/scripts/jest/dev/test-framework-setup.js index 8d2974a4871..59d16ffed21 100644 --- a/scripts/jest/dev/test-framework-setup.js +++ b/scripts/jest/dev/test-framework-setup.js @@ -46,28 +46,8 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { }); }); - var wrapDevMatcher = function(obj, name) { - const original = obj[name]; - obj[name] = function devMatcher() { - try { - original.apply(this, arguments); - } catch (e) { - global.__hadDevFailures = e.stack; - } - }; - }; - - const expectDev = function expectDev(actual) { - const expectation = expect(actual); - if (global.__suppressDevFailures) { - Object.keys(expectation).forEach(name => { - wrapDevMatcher(expectation, name); - wrapDevMatcher(expectation.not, name); - }); - } - return expectation; - }; - global.expectDev = expectDev; + // In development environment, they're equivalent. + global.expectDev = expect; require('jasmine-check').install(); } diff --git a/scripts/jest/prod/test-framework-setup.js b/scripts/jest/prod/test-framework-setup.js index 8d2974a4871..1eaabdcd25d 100644 --- a/scripts/jest/prod/test-framework-setup.js +++ b/scripts/jest/prod/test-framework-setup.js @@ -52,19 +52,17 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { try { original.apply(this, arguments); } catch (e) { - global.__hadDevFailures = e.stack; + // Ignore DEV-only assertions in prod environment. } }; }; const expectDev = function expectDev(actual) { const expectation = expect(actual); - if (global.__suppressDevFailures) { - Object.keys(expectation).forEach(name => { - wrapDevMatcher(expectation, name); - wrapDevMatcher(expectation.not, name); - }); - } + Object.keys(expectation).forEach(name => { + wrapDevMatcher(expectation, name); + wrapDevMatcher(expectation.not, name); + }); return expectation; }; global.expectDev = expectDev; From 8325c6d0edf452e0f28b4bced30ba5840aa93993 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 17:16:28 +0000 Subject: [PATCH 09/35] Decode errors so tests can assert on their messages Down from 175 to 129. Test Suites: 33 failed, 89 passed, 122 total Tests: 129 failed, 1029 skipped, 1417 passed, 2575 total Snapshots: 16 failed, 4 passed, 20 total --- .../ReactDOMServerIntegration-test.js | 2 +- scripts/jest/prod/test-framework-setup.js | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js index d2be441442e..c23ad4830b9 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegration-test.js @@ -248,7 +248,7 @@ function itClientRenders(desc, testFn) { } function itThrows(desc, testFn, partialMessage) { - it(`throws ${desc}`, () => { + fit(`throws ${desc}`, () => { return testFn().then( () => expect(false).toBe('The promise resolved and should not have.'), err => { diff --git a/scripts/jest/prod/test-framework-setup.js b/scripts/jest/prod/test-framework-setup.js index 1eaabdcd25d..c7d96a76944 100644 --- a/scripts/jest/prod/test-framework-setup.js +++ b/scripts/jest/prod/test-framework-setup.js @@ -6,6 +6,7 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { require('../setupSpecEquivalenceReporter.js'); } else { var env = jasmine.getEnv(); + var errorMap = require('../../error-codes/codes.json'); // TODO: Stop using spyOn in all the test since that seem deprecated. // This is a legacy upgrade path strategy from: @@ -67,5 +68,34 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { }; global.expectDev = expectDev; + // In production, we strip error messages and turn them into codes. + // This decodes them back so that the test assertions on them work. + function decodeErrorMessage(message) { + if (!message) { + return message; + } + const re = /error-decoder.html\?invariant=(\d+)([^\s]*)/; + const matches = message.match(re); + if (!matches || matches.length !== 3) { + return message; + } + const code = parseInt(matches[1], 10); + const args = matches[2] + .split('&') + .filter(s => s.startsWith('args[]=')) + .map(s => s.substr('args[]='.length)) + .map(decodeURIComponent); + const format = errorMap[code]; + let argIndex = 0; + return format.replace(/%s/g, () => args[argIndex++]); + } + global.Error = new Proxy(global.Error, { + construct(target, argumentsList, newTarget) { + const error = Reflect.construct(target, argumentsList, newTarget); + error.message = decodeErrorMessage(error.message); + return error; + }, + }); + require('jasmine-check').install(); } From b8b136b0c4ee2b155101512858777e5328acea01 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 18:21:38 +0000 Subject: [PATCH 10/35] Remove ReactDOMProduction-test There is no need for it now. The only test that was special is moved into ReactDOM-test. --- .../react-dom/src/__tests__/ReactDOM-test.js | 22 ++ .../src/__tests__/ReactDOMProduction-test.js | 361 ------------------ 2 files changed, 22 insertions(+), 361 deletions(-) delete mode 100644 packages/react-dom/src/__tests__/ReactDOMProduction-test.js diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js index 9caa898ff1c..1833408e42c 100644 --- a/packages/react-dom/src/__tests__/ReactDOM-test.js +++ b/packages/react-dom/src/__tests__/ReactDOM-test.js @@ -338,4 +338,26 @@ describe('ReactDOM', () => { ]; expect(actual).toEqual(expected); }); + + it('should not crash with devtools installed', () => { + try { + global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { + inject: function() {}, + onCommitFiberRoot: function() {}, + onCommitFiberUnmount: function() {}, + supportsFiber: true, + }; + jest.resetModules(); + React = require('react'); + ReactDOM = require('react-dom'); + class Component extends React.Component { + render() { + return
; + } + } + ReactDOM.render(, document.createElement('container')); + } finally { + delete global.__REACT_DEVTOOLS_GLOBAL_HOOK__; + } + }); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMProduction-test.js b/packages/react-dom/src/__tests__/ReactDOMProduction-test.js deleted file mode 100644 index a3587be4a16..00000000000 --- a/packages/react-dom/src/__tests__/ReactDOMProduction-test.js +++ /dev/null @@ -1,361 +0,0 @@ -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ -'use strict'; - -describe('ReactDOMProduction', () => { - var React; - var ReactDOM; - var ReactDOMServer; - var oldProcess; - - beforeEach(() => { - __DEV__ = false; - - // Mutating process.env.NODE_ENV would cause our babel plugins to do the - // wrong thing. If you change this, make sure to test with jest --no-cache. - oldProcess = process; - global.process = { - ...process, - env: {...process.env, NODE_ENV: 'production'}, - }; - - jest.resetModules(); - React = require('react'); - ReactDOM = require('react-dom'); - ReactDOMServer = require('react-dom/server'); - }); - - afterEach(() => { - __DEV__ = true; - global.process = oldProcess; - }); - - it('should use prod fbjs', () => { - var warning = require('fbjs/lib/warning'); - - spyOn(console, 'error'); - warning(false, 'Do cows go moo?'); - expectDev(console.error.calls.count()).toBe(0); - }); - - it('should use prod React', () => { - spyOn(console, 'error'); - - // no key warning - void
{[]}
; - - expectDev(console.error.calls.count()).toBe(0); - }); - - it('should handle a simple flow', () => { - class Component extends React.Component { - render() { - return {this.props.children}; - } - } - - var container = document.createElement('div'); - var inst = ReactDOM.render( -
- A - B - C -
, - container, - ); - - expect(container.firstChild).toBe(inst); - expect(inst.className).toBe('blue'); - expect(inst.style.fontFamily).toBe('Helvetica'); - expect(inst.textContent).toBe('ABC'); - - ReactDOM.render( -
- B - A - C -
, - container, - ); - - expect(inst.className).toBe('red'); - expect(inst.style.fontFamily).toBe('Comic Sans MS'); - expect(inst.textContent).toBe('BAC'); - - ReactDOM.unmountComponentAtNode(container); - - expect(container.childNodes.length).toBe(0); - }); - - it('should support createFactory', () => { - var span = React.createFactory('span'); - class Component extends React.Component { - render() { - return span({children: this.props.children}); - } - } - - var ComponentFactory = React.createFactory(Component); - - var container = document.createElement('div'); - ReactDOM.render( - ComponentFactory(null, span(null, 'Hello'), span(null, 'world')), - container, - ); - expect(container.firstChild.tagName).toBe('SPAN'); - expect(container.firstChild.childNodes[0].tagName).toBe('SPAN'); - expect(container.firstChild.childNodes[0].textContent).toBe('Hello'); - expect(container.firstChild.childNodes[1].tagName).toBe('SPAN'); - expect(container.firstChild.childNodes[1].textContent).toBe('world'); - }); - - it('should support React public API methods', () => { - expect(React.isValidElement(42)).toBe(false); - expect(React.isValidElement(
)).toBe(true); - expect(React.cloneElement(
, {foo: 42})).toEqual(
); - - const mapped = React.Children.map(
, el => - React.cloneElement(el, {foo: 42}), - ); - expect(mapped.length).toBe(1); - expect(mapped[0].type).toBe('div'); - expect(mapped[0].props.foo).toBe(42); - - const arr = React.Children.toArray(
); - expect(arr.length).toBe(1); - expect(arr[0].type).toBe('div'); - - let called = 0; - React.Children.forEach(
, () => called++); - expect(called).toBe(1); - - expect(React.Children.count(
)).toBe(1); - expect(() => React.Children.only(42)).toThrowError(); - }); - - it('should handle a simple flow (ssr)', () => { - class Component extends React.Component { - render() { - return {this.props.children}; - } - } - - var container = document.createElement('div'); - var markup = ReactDOMServer.renderToString( -
- A - B - C -
, - container, - ); - container.innerHTML = markup; - var inst = container.firstChild; - - expect(inst.className).toBe('blue'); - expect(inst.style.fontFamily).toBe('Helvetica'); - expect(inst.textContent).toBe('ABC'); - }); - - it('should call lifecycle methods', () => { - var log = []; - class Component extends React.Component { - state = {y: 1}; - shouldComponentUpdate(nextProps, nextState) { - log.push(['shouldComponentUpdate', nextProps, nextState]); - return nextProps.x !== this.props.x || nextState.y !== this.state.y; - } - componentWillMount() { - log.push(['componentWillMount']); - } - componentDidMount() { - log.push(['componentDidMount']); - } - componentWillReceiveProps(nextProps) { - log.push(['componentWillReceiveProps', nextProps]); - } - componentWillUpdate(nextProps, nextState) { - log.push(['componentWillUpdate', nextProps, nextState]); - } - componentDidUpdate(prevProps, prevState) { - log.push(['componentDidUpdate', prevProps, prevState]); - } - componentWillUnmount() { - log.push(['componentWillUnmount']); - } - render() { - log.push(['render']); - return null; - } - } - - var container = document.createElement('div'); - var inst = ReactDOM.render(, container); - expect(log).toEqual([ - ['componentWillMount'], - ['render'], - ['componentDidMount'], - ]); - log = []; - - inst.setState({y: 2}); - expect(log).toEqual([ - ['shouldComponentUpdate', {x: 1}, {y: 2}], - ['componentWillUpdate', {x: 1}, {y: 2}], - ['render'], - ['componentDidUpdate', {x: 1}, {y: 1}], - ]); - log = []; - - inst.setState({y: 2}); - expect(log).toEqual([['shouldComponentUpdate', {x: 1}, {y: 2}]]); - log = []; - - ReactDOM.render(, container); - expect(log).toEqual([ - ['componentWillReceiveProps', {x: 2}], - ['shouldComponentUpdate', {x: 2}, {y: 2}], - ['componentWillUpdate', {x: 2}, {y: 2}], - ['render'], - ['componentDidUpdate', {x: 1}, {y: 2}], - ]); - log = []; - - ReactDOM.render(, container); - expect(log).toEqual([ - ['componentWillReceiveProps', {x: 2}], - ['shouldComponentUpdate', {x: 2}, {y: 2}], - ]); - log = []; - - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual([['componentWillUnmount']]); - }); - - it('should throw with an error code in production', () => { - const errorCode = 152; - expect(function() { - class Component extends React.Component { - render() { - return undefined; - } - } - - var container = document.createElement('div'); - ReactDOM.render(, container); - }).toThrowError( - `Minified React error #${errorCode}; visit ` + - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${ - errorCode - }&args[]=Component` + - ' for the full message or use the non-minified dev environment' + - ' for full errors and additional helpful warnings.', - ); - }); - - // Regression test verifying that trying to access (missing) component stack doesn't crash. - it('should throw on children for void elements', () => { - const errorCode = 137; - const container = document.createElement('div'); - expect(() => - ReactDOM.render(children, container), - ).toThrowError( - `Minified React error #${errorCode}; visit ` + - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${ - errorCode - }&args[]=input&args[]=` + - ' for the full message or use the non-minified dev environment' + - ' for full errors and additional helpful warnings.', - ); - expect(() => - ReactDOMServer.renderToString(children, container), - ).toThrowError( - `Minified React error #${errorCode}; visit ` + - `http://facebook.github.io/react/docs/error-decoder.html?invariant=${ - errorCode - }&args[]=input&args[]=` + - ' for the full message or use the non-minified dev environment' + - ' for full errors and additional helpful warnings.', - ); - }); - - it('should not crash with devtools installed', () => { - try { - global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { - inject: function() {}, - onCommitFiberRoot: function() {}, - onCommitFiberUnmount: function() {}, - supportsFiber: true, - }; - jest.resetModules(); - React = require('react'); - ReactDOM = require('react-dom'); - class Component extends React.Component { - render() { - return
; - } - } - ReactDOM.render(, document.createElement('container')); - } finally { - global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = undefined; - } - }); - - // This test is originally from ReactDOMFiber-test but we replicate it here - // to avoid production-only regressions because of host context differences - // in dev and prod. - it('should keep track of namespace across portals in production', () => { - var svgEls, htmlEls; - var expectSVG = {ref: el => svgEls.push(el)}; - var expectHTML = {ref: el => htmlEls.push(el)}; - var usePortal = function(tree) { - return ReactDOM.createPortal(tree, document.createElement('div')); - }; - var assertNamespacesMatch = function(tree) { - var container = document.createElement('div'); - svgEls = []; - htmlEls = []; - ReactDOM.render(tree, container); - svgEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg'); - }); - htmlEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml'); - }); - ReactDOM.unmountComponentAtNode(container); - expect(container.innerHTML).toBe(''); - }; - - assertNamespacesMatch( -
- - -

- {usePortal( - - - - - -

- - {usePortal(

)} - - - , - )} -

- - - -

- , - ); - }); -}); From cabddb7583d5f1e66d220e06ce84322070c0a6fb Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 18:24:10 +0000 Subject: [PATCH 11/35] Remove production switches from ReactErrorUtils The tests now run in production in a separate pass. --- .../shared/__tests__/ReactErrorUtils-test.js | 368 ++++++++---------- 1 file changed, 165 insertions(+), 203 deletions(-) diff --git a/packages/shared/__tests__/ReactErrorUtils-test.js b/packages/shared/__tests__/ReactErrorUtils-test.js index 3e6a9a98af3..30cb6d824aa 100644 --- a/packages/shared/__tests__/ReactErrorUtils-test.js +++ b/packages/shared/__tests__/ReactErrorUtils-test.js @@ -17,227 +17,189 @@ describe('ReactErrorUtils', () => { ReactErrorUtils = require('shared/ReactErrorUtils').default; }); - // Run tests in both DEV and production - describe( - 'invokeGuardedCallback (development)', - invokeGuardedCallbackTests.bind(null, 'development'), - ); - describe('invokeGuardedCallback (production)', () => { - let oldProcess; - beforeEach(() => { - __DEV__ = false; - - // Mutating process.env.NODE_ENV would cause our babel plugins to do the - // wrong thing. If you change this, make sure to test with jest --no-cache. - oldProcess = process; - global.process = { - ...process, - env: {...process.env, NODE_ENV: 'production'}, - }; - - jest.resetModules(); - ReactErrorUtils = require('shared/ReactErrorUtils').default; - }); - - afterEach(() => { - __DEV__ = true; - global.process = oldProcess; - }); - - invokeGuardedCallbackTests('production'); + it(`it should rethrow caught errors`, () => { + var err = new Error('foo'); + var callback = function() { + throw err; + }; + ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( + 'foo', + callback, + null, + ); + expect(ReactErrorUtils.hasCaughtError()).toBe(false); + expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err); }); - function invokeGuardedCallbackTests(environment) { - it(`it should rethrow caught errors (${environment})`, () => { - var err = new Error('foo'); - var callback = function() { - throw err; - }; - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( - 'foo', - callback, - null, - ); - expect(ReactErrorUtils.hasCaughtError()).toBe(false); - expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err); - }); + it(`should call the callback the passed arguments`, () => { + var callback = jest.fn(); + ReactErrorUtils.invokeGuardedCallback( + 'foo', + callback, + null, + 'arg1', + 'arg2', + ); + expect(callback).toBeCalledWith('arg1', 'arg2'); + }); - it(`should call the callback the passed arguments (${environment})`, () => { - var callback = jest.fn(); - ReactErrorUtils.invokeGuardedCallback( - 'foo', - callback, - null, - 'arg1', - 'arg2', - ); - expect(callback).toBeCalledWith('arg1', 'arg2'); - }); + it(`should call the callback with the provided context`, () => { + var context = {didCall: false}; + ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + this.didCall = true; + }, + context, + ); + expect(context.didCall).toBe(true); + }); - it(`should call the callback with the provided context (${ - environment - })`, () => { - var context = {didCall: false}; - ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - this.didCall = true; - }, - context, - ); - expect(context.didCall).toBe(true); - }); + it(`should catch errors`, () => { + const error = new Error(); + const returnValue = ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + throw error; + }, + null, + 'arg1', + 'arg2', + ); + expect(returnValue).toBe(undefined); + expect(ReactErrorUtils.hasCaughtError()).toBe(true); + expect(ReactErrorUtils.clearCaughtError()).toBe(error); + }); - it(`should catch errors (${environment})`, () => { - const error = new Error(); - const returnValue = ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - throw error; - }, - null, - 'arg1', - 'arg2', - ); - expect(returnValue).toBe(undefined); - expect(ReactErrorUtils.hasCaughtError()).toBe(true); - expect(ReactErrorUtils.clearCaughtError()).toBe(error); - }); + it(`should return false from clearCaughtError if no error was thrown`, () => { + var callback = jest.fn(); + ReactErrorUtils.invokeGuardedCallback('foo', callback, null); + expect(ReactErrorUtils.hasCaughtError()).toBe(false); + expect(ReactErrorUtils.clearCaughtError).toThrow('no error was captured'); + }); - it(`should return false from clearCaughtError if no error was thrown (${ - environment - })`, () => { - var callback = jest.fn(); - ReactErrorUtils.invokeGuardedCallback('foo', callback, null); - expect(ReactErrorUtils.hasCaughtError()).toBe(false); - expect(ReactErrorUtils.clearCaughtError).toThrow( - __DEV__ ? 'no error was captured' : 'Minified React error #198', - ); - }); + it(`can nest with same debug name`, () => { + const err1 = new Error(); + let err2; + const err3 = new Error(); + let err4; + ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + throw err1; + }, + null, + ); + err2 = ReactErrorUtils.clearCaughtError(); + throw err3; + }, + null, + ); + err4 = ReactErrorUtils.clearCaughtError(); + + expect(err2).toBe(err1); + expect(err4).toBe(err3); + }); - it(`can nest with same debug name (${environment})`, () => { - const err1 = new Error(); - let err2; - const err3 = new Error(); - let err4; - ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - throw err1; - }, - null, - ); - err2 = ReactErrorUtils.clearCaughtError(); - throw err3; - }, - null, - ); - err4 = ReactErrorUtils.clearCaughtError(); + it(`handles nested errors`, () => { + const err1 = new Error(); + let err2; + ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + ReactErrorUtils.invokeGuardedCallback( + 'foo', + function() { + throw err1; + }, + null, + ); + err2 = ReactErrorUtils.clearCaughtError(); + }, + null, + ); + // Returns null because inner error was already captured + expect(ReactErrorUtils.hasCaughtError()).toBe(false); + + expect(err2).toBe(err1); + }); - expect(err2).toBe(err1); - expect(err4).toBe(err3); - }); + it('handles nested errors in separate renderers', () => { + const ReactErrorUtils1 = require('shared/ReactErrorUtils').default; + jest.resetModules(); + const ReactErrorUtils2 = require('shared/ReactErrorUtils').default; + expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2); - it(`handles nested errors (${environment})`, () => { - const err1 = new Error(); - let err2; - ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - ReactErrorUtils.invokeGuardedCallback( - 'foo', - function() { - throw err1; - }, - null, - ); - err2 = ReactErrorUtils.clearCaughtError(); - }, - null, - ); - // Returns null because inner error was already captured - expect(ReactErrorUtils.hasCaughtError()).toBe(false); + let ops = []; - expect(err2).toBe(err1); - }); + ReactErrorUtils1.invokeGuardedCallback( + null, + () => { + ReactErrorUtils2.invokeGuardedCallback( + null, + () => { + throw new Error('nested error'); + }, + null, + ); + // ReactErrorUtils2 should catch the error + ops.push(ReactErrorUtils2.hasCaughtError()); + ops.push(ReactErrorUtils2.clearCaughtError().message); + }, + null, + ); - it('handles nested errors in separate renderers', () => { - const ReactErrorUtils1 = require('shared/ReactErrorUtils').default; - jest.resetModules(); - const ReactErrorUtils2 = require('shared/ReactErrorUtils').default; - expect(ReactErrorUtils1).not.toEqual(ReactErrorUtils2); + // ReactErrorUtils1 should not catch the error + ops.push(ReactErrorUtils1.hasCaughtError()); - let ops = []; + expect(ops).toEqual([true, 'nested error', false]); + }); - ReactErrorUtils1.invokeGuardedCallback( + if (!__DEV__) { + // jsdom doesn't handle this properly, but Chrome and Firefox should. Test + // this with a fixture. + it('catches null values', () => { + ReactErrorUtils.invokeGuardedCallback( null, - () => { - ReactErrorUtils2.invokeGuardedCallback( - null, - () => { - throw new Error('nested error'); - }, - null, - ); - // ReactErrorUtils2 should catch the error - ops.push(ReactErrorUtils2.hasCaughtError()); - ops.push(ReactErrorUtils2.clearCaughtError().message); + function() { + throw null; // eslint-disable-line no-throw-literal }, null, ); - - // ReactErrorUtils1 should not catch the error - ops.push(ReactErrorUtils1.hasCaughtError()); - - expect(ops).toEqual([true, 'nested error', false]); + expect(ReactErrorUtils.hasCaughtError()).toBe(true); + expect(ReactErrorUtils.clearCaughtError()).toBe(null); }); + } - if (environment === 'production') { - // jsdom doesn't handle this properly, but Chrome and Firefox should. Test - // this with a fixture. - it('catches null values', () => { - ReactErrorUtils.invokeGuardedCallback( - null, - function() { - throw null; // eslint-disable-line no-throw-literal - }, - null, - ); - expect(ReactErrorUtils.hasCaughtError()).toBe(true); - expect(ReactErrorUtils.clearCaughtError()).toBe(null); - }); - } - - it(`can be shimmed (${environment})`, () => { - const ops = []; - // Override the original invokeGuardedCallback - ReactErrorUtils.injection.injectErrorUtils({ - invokeGuardedCallback(name, func, context, a) { - ops.push(a); - try { - func.call(context, a); - } catch (error) { - this._hasCaughtError = true; - this._caughtError = error; - } - }, - }); - - var err = new Error('foo'); - var callback = function() { - throw err; - }; - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( - 'foo', - callback, - null, - 'somearg', - ); - expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err); - expect(ops).toEqual(['somearg']); + it(`can be shimmed`, () => { + const ops = []; + // Override the original invokeGuardedCallback + ReactErrorUtils.injection.injectErrorUtils({ + invokeGuardedCallback(name, func, context, a) { + ops.push(a); + try { + func.call(context, a); + } catch (error) { + this._hasCaughtError = true; + this._caughtError = error; + } + }, }); - } + + var err = new Error('foo'); + var callback = function() { + throw err; + }; + ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( + 'foo', + callback, + null, + 'somearg', + ); + expect(() => ReactErrorUtils.rethrowCaughtError()).toThrow(err); + expect(ops).toEqual(['somearg']); + }); }); From bc3027fa0f2451556dd457c6fc123fa7ff2a9185 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 21 Nov 2017 19:24:42 +0000 Subject: [PATCH 12/35] Add and use spyOnDev() for warnings This ensures that by default we expect no warnings in production bundles. If the warning *is* expected, use the regular spyOn() method. This currently breaks all expectDev() assertions without __DEV__ blocks so we go back to: Test Suites: 56 failed, 65 passed, 121 total Tests: 379 failed, 1029 skipped, 1148 passed, 2556 total Snapshots: 16 failed, 4 passed, 20 total --- .../__tests__/CSSPropertyOperations-test.js | 12 +- .../__tests__/DOMPropertyOperations-test.js | 2 +- .../src/__tests__/EventPluginHub-test.js | 2 +- .../ReactBrowserEventEmitter-test.js | 2 +- .../__tests__/ReactChildReconciler-test.js | 8 +- .../src/__tests__/ReactComponent-test.js | 18 +-- .../__tests__/ReactComponentLifeCycle-test.js | 10 +- .../__tests__/ReactCompositeComponent-test.js | 28 ++-- .../ReactCompositeComponentState-test.js | 4 +- .../react-dom/src/__tests__/ReactDOM-test.js | 4 +- .../src/__tests__/ReactDOMAttribute-test.js | 10 +- .../src/__tests__/ReactDOMComponent-test.js | 122 +++++++++--------- .../__tests__/ReactDOMComponentTree-test.js | 6 +- .../src/__tests__/ReactDOMFiber-test.js | 12 +- .../src/__tests__/ReactDOMInput-test.js | 2 +- .../__tests__/ReactDOMInvalidARIAHook-test.js | 12 +- .../src/__tests__/ReactDOMOption-test.js | 4 +- .../src/__tests__/ReactDOMRoot-test.js | 4 +- .../src/__tests__/ReactDOMSelect-test.js | 6 +- .../ReactDOMServerIntegration-test.js | 2 +- .../src/__tests__/ReactDOMTextarea-test.js | 16 +-- .../__tests__/ReactErrorBoundaries-test.js | 2 +- .../src/__tests__/ReactMount-test.js | 16 +-- .../__tests__/ReactMountDestruction-test.js | 4 +- .../src/__tests__/ReactMultiChild-test.js | 8 +- .../src/__tests__/ReactMultiChildText-test.js | 2 +- .../src/__tests__/ReactRenderDocument-test.js | 20 +-- .../__tests__/ReactServerRendering-test.js | 16 +-- .../__tests__/ReactStatelessComponent-test.js | 14 +- .../src/__tests__/ReactTestUtils-test.js | 2 +- .../src/__tests__/ReactUpdates-test.js | 6 +- .../events/__tests__/SyntheticEvent-test.js | 12 +- .../src/__tests__/ReactFragment-test.js | 4 +- .../ReactIncrementalErrorHandling-test.js | 16 +-- .../ReactIncrementalSideEffects-test.js | 2 +- .../__tests__/ReactIncrementalUpdates-test.js | 2 +- .../__tests__/ReactShallowRenderer-test.js | 6 +- .../src/__tests__/ReactTestRenderer-test.js | 2 +- .../react/src/__tests__/ReactChildren-test.js | 8 +- .../ReactCoffeeScriptClass-test.coffee | 12 +- .../__tests__/ReactContextValidator-test.js | 8 +- .../react/src/__tests__/ReactES6Class-test.js | 12 +- .../react/src/__tests__/ReactElement-test.js | 18 +-- .../src/__tests__/ReactElementClone-test.js | 10 +- .../__tests__/ReactElementValidator-test.js | 42 +++--- .../src/__tests__/ReactJSXElement-test.js | 10 +- .../ReactJSXElementValidator-test.js | 36 +++--- .../src/__tests__/ReactPureComponent-test.js | 4 +- .../__tests__/ReactTypeScriptClass-test.ts | 12 +- .../createReactClassIntegration-test.js | 16 +-- .../__tests__/ReactDOMFrameScheduling-test.js | 2 +- scripts/jest/dev/test-framework-setup.js | 3 + scripts/jest/jest.d.ts | 1 + scripts/jest/prod/test-framework-setup.js | 3 + scripts/jest/setupSpecEquivalenceReporter.js | 6 + 55 files changed, 318 insertions(+), 305 deletions(-) diff --git a/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js b/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js index 81b3efe48f7..2709bd7b28d 100644 --- a/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js +++ b/packages/react-dom/src/__tests__/CSSPropertyOperations-test.js @@ -91,7 +91,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var root = document.createElement('div'); ReactDOM.render(, root); expectDev(console.error.calls.count()).toBe(1); @@ -111,7 +111,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var styles = { '-ms-transform': 'translate3d(0, 0, 0)', '-webkit-transform': 'translate3d(0, 0, 0)', @@ -150,7 +150,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var root = document.createElement('div'); ReactDOM.render(, root); // msTransform is correct already and shouldn't warn @@ -187,7 +187,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var root = document.createElement('div'); ReactDOM.render(, root); expectDev(console.error.calls.count()).toBe(2); @@ -214,7 +214,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var root = document.createElement('div'); ReactDOM.render(, root); @@ -246,7 +246,7 @@ describe('CSSPropertyOperations', () => { } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var root = document.createElement('div'); ReactDOM.render(, root); diff --git a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js index 7f544506a5f..f814add877a 100644 --- a/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js +++ b/packages/react-dom/src/__tests__/DOMPropertyOperations-test.js @@ -151,7 +151,7 @@ describe('DOMPropertyOperations', () => { it('should not remove attributes for special properties', () => { var container = document.createElement('div'); - spyOn(console, 'error'); + spyOnDev(console, 'error'); ReactDOM.render( , container, diff --git a/packages/react-dom/src/__tests__/EventPluginHub-test.js b/packages/react-dom/src/__tests__/EventPluginHub-test.js index eaf40602ab6..e01c6baa302 100644 --- a/packages/react-dom/src/__tests__/EventPluginHub-test.js +++ b/packages/react-dom/src/__tests__/EventPluginHub-test.js @@ -22,7 +22,7 @@ describe('EventPluginHub', () => { }); it('should prevent non-function listeners, at dispatch', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var node = ReactTestUtils.renderIntoDocument(

, ); diff --git a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js index 647109a907b..27330f7053f 100644 --- a/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js +++ b/packages/react-dom/src/__tests__/ReactBrowserEventEmitter-test.js @@ -285,7 +285,7 @@ describe('ReactBrowserEventEmitter', () => { putListener(CHILD, ON_CLICK_KEY, recordIDAndReturnFalse.bind(null, CHILD)); putListener(PARENT, ON_CLICK_KEY, recordID.bind(null, PARENT)); putListener(GRANDPARENT, ON_CLICK_KEY, recordID.bind(null, GRANDPARENT)); - spyOn(console, 'error'); + spyOnDev(console, 'error'); ReactTestUtils.Simulate.click(CHILD); expect(idCallOrder.length).toBe(3); expect(idCallOrder[0]).toBe(CHILD); diff --git a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js index 09fa3d436e1..d7843ef5c83 100644 --- a/packages/react-dom/src/__tests__/ReactChildReconciler-test.js +++ b/packages/react-dom/src/__tests__/ReactChildReconciler-test.js @@ -46,7 +46,7 @@ describe('ReactChildReconciler', () => { } it('warns for duplicated array keys', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { render() { @@ -66,7 +66,7 @@ describe('ReactChildReconciler', () => { }); it('warns for duplicated array keys with component stack info', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { render() { @@ -105,7 +105,7 @@ describe('ReactChildReconciler', () => { }); it('warns for duplicated iterable keys', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { render() { @@ -125,7 +125,7 @@ describe('ReactChildReconciler', () => { }); it('warns for duplicated iterable keys with component stack info', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { render() { diff --git a/packages/react-dom/src/__tests__/ReactComponent-test.js b/packages/react-dom/src/__tests__/ReactComponent-test.js index 0a63915429c..0aa8482dd07 100644 --- a/packages/react-dom/src/__tests__/ReactComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactComponent-test.js @@ -46,7 +46,7 @@ describe('ReactComponent', () => { }); it('should warn when children are mutated during render', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); function Wrapper(props) { props.children[1] =

; // Mutation is illegal return

{props.children}
; @@ -63,7 +63,7 @@ describe('ReactComponent', () => { }); it('should warn when children are mutated during update', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Wrapper extends React.Component { componentDidMount() { @@ -335,7 +335,7 @@ describe('ReactComponent', () => { }); it('throws usefully when rendering badly-typed elements', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var X = undefined; expect(() => ReactTestUtils.renderIntoDocument()).toThrowError( @@ -356,7 +356,7 @@ describe('ReactComponent', () => { }); it('includes owner name in the error about badly-typed elements', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var X = undefined; @@ -494,7 +494,7 @@ describe('ReactComponent', () => { function Foo() { return Foo; } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(, container); expectDev(console.error.calls.count()).toBe(1); @@ -512,7 +512,7 @@ describe('ReactComponent', () => { return Foo; } } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(, container); expectDev(console.error.calls.count()).toBe(1); @@ -532,7 +532,7 @@ describe('ReactComponent', () => {
); } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(, container); expectDev(console.error.calls.count()).toBe(1); @@ -553,7 +553,7 @@ describe('ReactComponent', () => { function Foo() { return {() => 'Hello'}; } - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(, container); expect(container.innerHTML).toBe('Hello'); @@ -561,7 +561,7 @@ describe('ReactComponent', () => { }); it('deduplicates function type warnings based on component type', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Foo extends React.PureComponent { constructor() { super(); diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js index 66eaecbcf2e..2a032ac292d 100644 --- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js +++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js @@ -198,7 +198,7 @@ describe('ReactComponentLifeCycle', () => { }); it('should not allow update state inside of getInitialState', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class StatefulComponent extends React.Component { constructor(props, context) { @@ -228,7 +228,7 @@ describe('ReactComponentLifeCycle', () => { }); it('should correctly determine if a component is mounted', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { _isMounted() { // No longer a public API, but we can test that it works internally by @@ -259,7 +259,7 @@ describe('ReactComponentLifeCycle', () => { }); it('should correctly determine if a null component is mounted', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { _isMounted() { // No longer a public API, but we can test that it works internally by @@ -309,7 +309,7 @@ describe('ReactComponentLifeCycle', () => { }); it('warns if findDOMNode is used inside render', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { state = {isMounted: false}; componentDidMount() { @@ -331,7 +331,7 @@ describe('ReactComponentLifeCycle', () => { }); it('should carry through each of the phases of setup', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class LifeCycleComponent extends React.Component { constructor(props, context) { diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js index 17b54543fdf..8917ecf9030 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponent-test.js @@ -122,7 +122,7 @@ describe('ReactCompositeComponent', () => { } } - spyOn(console, 'warn'); + spyOnDev(console, 'warn'); var markup = ReactDOMServer.renderToString(); // Old API based on heuristic @@ -231,7 +231,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn about `forceUpdate` on unmounted components', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); document.body.appendChild(container); @@ -266,7 +266,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn about `setState` on unmounted components', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); document.body.appendChild(container); @@ -338,7 +338,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when rendering a class with a render method that does not extend React.Component', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); class ClassWithRenderNotExtended { render() { @@ -358,7 +358,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn about `setState` in render', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); @@ -410,7 +410,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn about `setState` in getChildContext', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); @@ -496,7 +496,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when shouldComponentUpdate() returns undefined', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { state = {bogus: false}; @@ -521,7 +521,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when componentDidUnmount method is defined', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { componentDidUnmount = () => {}; @@ -542,7 +542,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when componentDidReceiveProps method is defined', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { componentDidReceiveProps = () => {}; @@ -565,7 +565,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when defaultProps was defined as an instance property', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { constructor(props) { @@ -1056,7 +1056,7 @@ describe('ReactCompositeComponent', () => { }); it('should disallow nested render calls', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Inner extends React.Component { render() { @@ -1353,7 +1353,7 @@ describe('ReactCompositeComponent', () => { }); it('should warn when mutated props are passed', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); @@ -1592,7 +1592,7 @@ describe('ReactCompositeComponent', () => { }); it('should return a meaningful warning when constructor is returned', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class RenderTextInvalidConstructor extends React.Component { constructor(props) { super(props); @@ -1616,7 +1616,7 @@ describe('ReactCompositeComponent', () => { }); it('should return error if render is not defined', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class RenderTestUndefinedRender extends React.Component {} expect(function() { diff --git a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js index 93b4d9280e7..d3c7bd6da8a 100644 --- a/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js +++ b/packages/react-dom/src/__tests__/ReactCompositeComponentState-test.js @@ -381,7 +381,7 @@ describe('ReactCompositeComponent-state', () => { }); it('should treat assigning to this.state inside cWRP as a replaceState, with a warning', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); let ops = []; class Test extends React.Component { @@ -429,7 +429,7 @@ describe('ReactCompositeComponent-state', () => { }); it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); let ops = []; class Test extends React.Component { diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js index 1833408e42c..b2ae37811de 100644 --- a/packages/react-dom/src/__tests__/ReactDOM-test.js +++ b/packages/react-dom/src/__tests__/ReactDOM-test.js @@ -109,7 +109,7 @@ describe('ReactDOM', () => { }); it('throws in render() if the mount callback is not a function', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); function Foo() { this.a = 1; @@ -153,7 +153,7 @@ describe('ReactDOM', () => { }); it('throws in render() if the update callback is not a function', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); function Foo() { this.a = 1; diff --git a/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js b/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js index 67589233e96..4bc530c0202 100644 --- a/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMAttribute-test.js @@ -46,7 +46,7 @@ describe('ReactDOM unknown attribute', () => { }); it('changes values true, false to null, and also warns once', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); testUnknownAttributeAssignment(true, null); testUnknownAttributeAssignment(false, null); @@ -82,7 +82,7 @@ describe('ReactDOM unknown attribute', () => { }); it('coerces NaN to strings and warns', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); testUnknownAttributeAssignment(NaN, 'NaN'); expectDev( @@ -107,7 +107,7 @@ describe('ReactDOM unknown attribute', () => { }); it('removes symbols and warns', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); testUnknownAttributeRemoval(Symbol('foo')); expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( @@ -120,7 +120,7 @@ describe('ReactDOM unknown attribute', () => { }); it('removes functions and warns', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); testUnknownAttributeRemoval(function someFunction() {}); expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( @@ -134,7 +134,7 @@ describe('ReactDOM unknown attribute', () => { }); it('allows camelCase unknown attributes and warns', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var el = document.createElement('div'); ReactDOM.render(
, el); diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js index e9b19119524..3f620bf3c88 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js @@ -136,7 +136,7 @@ describe('ReactDOMComponent', () => { }); it('should warn for unknown prop', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
{}} />, container); expectDev(console.error.calls.count(0)).toBe(1); @@ -149,7 +149,7 @@ describe('ReactDOMComponent', () => { }); it('should group multiple unknown prop warnings together', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
{}} baz={() => {}} />, container); expectDev(console.error.calls.count(0)).toBe(1); @@ -162,7 +162,7 @@ describe('ReactDOMComponent', () => { }); it('should warn for onDblClick prop', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
{}} />, container); expectDev(console.error.calls.count(0)).toBe(1); @@ -172,7 +172,7 @@ describe('ReactDOMComponent', () => { }); it('should warn for unknown string event handlers', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
, container); expect(container.firstChild.hasAttribute('onUnknown')).toBe(false); @@ -196,7 +196,7 @@ describe('ReactDOMComponent', () => { }); it('should warn for unknown function event handlers', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
, container); expect(container.firstChild.hasAttribute('onUnknown')).toBe(false); @@ -220,7 +220,7 @@ describe('ReactDOMComponent', () => { }); it('should warn for badly cased React attributes', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(
, container); expect(container.firstChild.getAttribute('CHILDREN')).toBe('5'); @@ -231,7 +231,7 @@ describe('ReactDOMComponent', () => { }); it('should not warn for "0" as a unitless style value', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); class Component extends React.Component { render() { @@ -244,7 +244,7 @@ describe('ReactDOMComponent', () => { }); it('should warn nicely about NaN in style', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var style = {fontSize: NaN}; var div = document.createElement('div'); @@ -445,7 +445,7 @@ describe('ReactDOMComponent', () => { }); it('should reject attribute key injection attack on markup', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); for (var i = 0; i < 3; i++) { var container = document.createElement('div'); var element = React.createElement( @@ -462,7 +462,7 @@ describe('ReactDOMComponent', () => { }); it('should reject attribute key injection attack on update', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); for (var i = 0; i < 3; i++) { var container = document.createElement('div'); var beforeUpdate = React.createElement('x-foo-component', {}, null); @@ -723,7 +723,7 @@ describe('ReactDOMComponent', () => { }); it('should warn about non-string "is" attribute', () => { - spyOn(console, 'error'); + spyOnDev(console, 'error'); var container = document.createElement('div'); ReactDOM.render(