diff --git a/.eslintrc.js b/.eslintrc.js index 2aa48458b8bff..ccd162593f5ca 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -303,7 +303,6 @@ module.exports = { ERROR, {isProductionUserAppCode: true}, ], - 'react-internal/no-to-warn-dev-within-to-throw': ERROR, 'react-internal/warning-args': ERROR, 'react-internal/no-production-logging': ERROR, }, diff --git a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js index befbdc617d361..02ff7c50f0f4f 100644 --- a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js +++ b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js @@ -13,7 +13,6 @@ const React = require('react'); const stripAnsi = require('strip-ansi'); const {startTransition, useDeferredValue} = React; -const chalk = require('chalk'); const ReactNoop = require('react-noop-renderer'); const { waitFor, @@ -25,7 +24,7 @@ const { const act = require('internal-test-utils').act; const Scheduler = require('scheduler/unstable_mock'); const { - flushAllUnexpectedConsoleCalls, + assertConsoleLogsCleared, resetAllUnexpectedConsoleCalls, patchConsoleMethods, } = require('../consoleMock'); @@ -205,16 +204,17 @@ describe('ReactInternalTestUtils console mocks', () => { it('should fail if not asserted', () => { expect(() => { console.log('hit'); - flushAllUnexpectedConsoleCalls(); - }).toThrow(`Expected test not to call ${chalk.bold('console.log()')}.`); + assertConsoleLogsCleared(); + }).toThrow(`console.log was called without assertConsoleLogDev`); }); - // @gate __DEV__ it('should not fail if mocked with spyOnDev', () => { spyOnDev(console, 'log').mockImplementation(() => {}); expect(() => { - console.log('hit'); - flushAllUnexpectedConsoleCalls(); + if (__DEV__) { + console.log('hit'); + } + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -223,7 +223,7 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnProd(console, 'log').mockImplementation(() => {}); expect(() => { console.log('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -231,33 +231,26 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnDevAndProd(console, 'log').mockImplementation(() => {}); expect(() => { console.log('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); - - // @gate __DEV__ - it('should not fail with toLogDev', () => { - expect(() => { - console.log('hit'); - flushAllUnexpectedConsoleCalls(); - }).toLogDev(['hit']); - }); }); describe('console.warn', () => { it('should fail if not asserted', () => { expect(() => { console.warn('hit'); - flushAllUnexpectedConsoleCalls(); - }).toThrow(`Expected test not to call ${chalk.bold('console.warn()')}.`); + assertConsoleLogsCleared(); + }).toThrow('console.warn was called without assertConsoleWarnDev'); }); - // @gate __DEV__ it('should not fail if mocked with spyOnDev', () => { spyOnDev(console, 'warn').mockImplementation(() => {}); expect(() => { - console.warn('hit'); - flushAllUnexpectedConsoleCalls(); + if (__DEV__) { + console.warn('hit'); + } + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -266,7 +259,7 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnProd(console, 'warn').mockImplementation(() => {}); expect(() => { console.warn('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -274,33 +267,26 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnDevAndProd(console, 'warn').mockImplementation(() => {}); expect(() => { console.warn('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); - - // @gate __DEV__ - it('should not fail with toWarnDev', () => { - expect(() => { - console.warn('hit'); - flushAllUnexpectedConsoleCalls(); - }).toWarnDev(['hit'], {withoutStack: true}); - }); }); describe('console.error', () => { it('should fail if console.error is not asserted', () => { expect(() => { console.error('hit'); - flushAllUnexpectedConsoleCalls(); - }).toThrow(`Expected test not to call ${chalk.bold('console.error()')}.`); + assertConsoleLogsCleared(); + }).toThrow('console.error was called without assertConsoleErrorDev'); }); - // @gate __DEV__ it('should not fail if mocked with spyOnDev', () => { spyOnDev(console, 'error').mockImplementation(() => {}); expect(() => { - console.error('hit'); - flushAllUnexpectedConsoleCalls(); + if (__DEV__) { + console.error('hit'); + } + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -309,7 +295,7 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnProd(console, 'error').mockImplementation(() => {}); expect(() => { console.error('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); @@ -317,17 +303,9 @@ describe('ReactInternalTestUtils console mocks', () => { spyOnDevAndProd(console, 'error').mockImplementation(() => {}); expect(() => { console.error('hit'); - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); }).not.toThrow(); }); - - // @gate __DEV__ - it('should not fail with toErrorDev', () => { - expect(() => { - console.error('hit'); - flushAllUnexpectedConsoleCalls(); - }).toErrorDev(['hit'], {withoutStack: true}); - }); }); }); @@ -361,17 +339,19 @@ describe('ReactInternalTestUtils console assertions', () => { }); describe('assertConsoleLogDev', () => { - // @gate __DEV__ it('passes for a single log', () => { - console.log('Hello'); + if (__DEV__) { + console.log('Hello'); + } assertConsoleLogDev(['Hello']); }); - // @gate __DEV__ it('passes for multiple logs', () => { - console.log('Hello'); - console.log('Good day'); - console.log('Bye'); + if (__DEV__) { + console.log('Hello'); + console.log('Good day'); + console.log('Bye'); + } assertConsoleLogDev(['Hello', 'Good day', 'Bye']); }); @@ -906,17 +886,19 @@ describe('ReactInternalTestUtils console assertions', () => { }); describe('assertConsoleWarnDev', () => { - // @gate __DEV__ it('passes if an warning contains a stack', () => { - console.warn('Hello\n in div'); + if (__DEV__) { + console.warn('Hello\n in div'); + } assertConsoleWarnDev(['Hello']); }); - // @gate __DEV__ it('passes if all warnings contain a stack', () => { - console.warn('Hello\n in div'); - console.warn('Good day\n in div'); - console.warn('Bye\n in div'); + if (__DEV__) { + console.warn('Hello\n in div'); + console.warn('Good day\n in div'); + console.warn('Bye\n in div'); + } assertConsoleWarnDev(['Hello', 'Good day', 'Bye']); }); @@ -1353,14 +1335,17 @@ describe('ReactInternalTestUtils console assertions', () => { }); describe('global withoutStack', () => { - // @gate __DEV__ it('passes if warnings without stack explicitly opt out', () => { - console.warn('Hello'); + if (__DEV__) { + console.warn('Hello'); + } assertConsoleWarnDev(['Hello'], {withoutStack: true}); - console.warn('Hello'); - console.warn('Good day'); - console.warn('Bye'); + if (__DEV__) { + console.warn('Hello'); + console.warn('Good day'); + console.warn('Bye'); + } assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], { withoutStack: true, @@ -1460,11 +1445,12 @@ describe('ReactInternalTestUtils console assertions', () => { }); }); describe('local withoutStack', () => { - // @gate __DEV__ it('passes when expected withoutStack logs matches the actual logs', () => { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); + if (__DEV__) { + console.warn('Hello\n in div'); + console.warn('Good day'); + console.warn('Bye\n in div'); + } assertConsoleWarnDev([ 'Hello', ['Good day', {withoutStack: true}], @@ -1981,17 +1967,19 @@ describe('ReactInternalTestUtils console assertions', () => { }); describe('assertConsoleErrorDev', () => { - // @gate __DEV__ it('passes if an error contains a stack', () => { - console.error('Hello\n in div'); + if (__DEV__) { + console.error('Hello\n in div'); + } assertConsoleErrorDev(['Hello']); }); - // @gate __DEV__ it('passes if all errors contain a stack', () => { - console.error('Hello\n in div'); - console.error('Good day\n in div'); - console.error('Bye\n in div'); + if (__DEV__) { + console.error('Hello\n in div'); + console.error('Good day\n in div'); + console.error('Bye\n in div'); + } assertConsoleErrorDev(['Hello', 'Good day', 'Bye']); }); @@ -2446,14 +2434,17 @@ describe('ReactInternalTestUtils console assertions', () => { }); describe('global withoutStack', () => { - // @gate __DEV__ it('passes if errors without stack explicitly opt out', () => { - console.error('Hello'); + if (__DEV__) { + console.error('Hello'); + } assertConsoleErrorDev(['Hello'], {withoutStack: true}); - console.error('Hello'); - console.error('Good day'); - console.error('Bye'); + if (__DEV__) { + console.error('Hello'); + console.error('Good day'); + console.error('Bye'); + } assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], { withoutStack: true, @@ -2553,11 +2544,12 @@ describe('ReactInternalTestUtils console assertions', () => { }); }); describe('local withoutStack', () => { - // @gate __DEV__ it('passes when expected withoutStack logs matches the actual logs', () => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); + if (__DEV__) { + console.error('Hello\n in div'); + console.error('Good day'); + console.error('Bye\n in div'); + } assertConsoleErrorDev([ 'Hello', ['Good day', {withoutStack: true}], diff --git a/packages/internal-test-utils/consoleMock.js b/packages/internal-test-utils/consoleMock.js index 561004a4dea76..0ab69bfa3a922 100644 --- a/packages/internal-test-utils/consoleMock.js +++ b/packages/internal-test-utils/consoleMock.js @@ -19,19 +19,7 @@ const loggedErrors = (global.__loggedErrors = global.__loggedErrors || []); const loggedWarns = (global.__loggedWarns = global.__loggedWarns || []); const loggedLogs = (global.__loggedLogs = global.__loggedLogs || []); -// TODO: delete these after code modding away from toWarnDev. -const unexpectedErrorCallStacks = (global.__unexpectedErrorCallStacks = - global.__unexpectedErrorCallStacks || []); -const unexpectedWarnCallStacks = (global.__unexpectedWarnCallStacks = - global.__unexpectedWarnCallStacks || []); -const unexpectedLogCallStacks = (global.__unexpectedLogCallStacks = - global.__unexpectedLogCallStacks || []); - -const patchConsoleMethod = ( - methodName, - unexpectedConsoleCallStacks, - logged, -) => { +const patchConsoleMethod = (methodName, logged) => { const newMethod = function (format, ...args) { // Ignore uncaught errors reported by jsdom // and React addendums because they're too noisy. @@ -72,14 +60,6 @@ const patchConsoleMethod = ( } } - // Capture the call stack now so we can warn about it later. - // The call stack has helpful information for the test author. - // Don't throw yet though b'c it might be accidentally caught and suppressed. - const stack = new Error().stack; - unexpectedConsoleCallStacks.push([ - stack.slice(stack.indexOf('\n') + 1), - util.format(format, ...args), - ]); logged.push([format, ...args]); }; @@ -88,123 +68,40 @@ const patchConsoleMethod = ( return newMethod; }; -const flushUnexpectedConsoleCalls = ( - mockMethod, - methodName, - expectedMatcher, - unexpectedConsoleCallStacks, -) => { - if ( - console[methodName] !== mockMethod && - !jest.isMockFunction(console[methodName]) - ) { - // throw new Error( - // `Test did not tear down console.${methodName} mock properly.` - // ); - } - if (unexpectedConsoleCallStacks.length > 0) { - const messages = unexpectedConsoleCallStacks.map( - ([stack, message]) => - `${chalk.red(message)}\n` + - `${stack - .split('\n') - .map(line => chalk.gray(line)) - .join('\n')}`, - ); - - const type = methodName === 'log' ? 'log' : 'warning'; - const message = - `Expected test not to call ${chalk.bold( - `console.${methodName}()`, - )}.\n\n` + - `If the ${type} is expected, test for it explicitly by:\n` + - `1. Using ${chalk.bold(expectedMatcher + '()')} or...\n` + - `2. Mock it out using ${chalk.bold( - 'spyOnDev', - )}(console, '${methodName}') or ${chalk.bold( - 'spyOnProd', - )}(console, '${methodName}'), and test that the ${type} occurs.`; - - throw new Error(`${message}\n\n${messages.join('\n\n')}`); - } -}; - -let errorMethod; -let warnMethod; let logMethod; export function patchConsoleMethods({includeLog} = {includeLog: false}) { - errorMethod = patchConsoleMethod( - 'error', - unexpectedErrorCallStacks, - loggedErrors, - ); - warnMethod = patchConsoleMethod( - 'warn', - unexpectedWarnCallStacks, - loggedWarns, - ); + patchConsoleMethod('error', loggedErrors); + patchConsoleMethod('warn', loggedWarns); // Only assert console.log isn't called in CI so you can debug tests in DEV. // The matchers will still work in DEV, so you can assert locally. if (includeLog) { - logMethod = patchConsoleMethod('log', unexpectedLogCallStacks, loggedLogs); - } -} - -export function flushAllUnexpectedConsoleCalls() { - flushUnexpectedConsoleCalls( - errorMethod, - 'error', - 'assertConsoleErrorDev', - unexpectedErrorCallStacks, - ); - flushUnexpectedConsoleCalls( - warnMethod, - 'warn', - 'assertConsoleWarnDev', - unexpectedWarnCallStacks, - ); - if (logMethod) { - flushUnexpectedConsoleCalls( - logMethod, - 'log', - 'assertConsoleLogDev', - unexpectedLogCallStacks, - ); - unexpectedLogCallStacks.length = 0; + logMethod = patchConsoleMethod('log', loggedLogs); } - unexpectedErrorCallStacks.length = 0; - unexpectedWarnCallStacks.length = 0; } export function resetAllUnexpectedConsoleCalls() { loggedErrors.length = 0; loggedWarns.length = 0; - unexpectedErrorCallStacks.length = 0; - unexpectedWarnCallStacks.length = 0; if (logMethod) { loggedLogs.length = 0; - unexpectedLogCallStacks.length = 0; } } export function clearLogs() { const logs = Array.from(loggedLogs); - unexpectedLogCallStacks.length = 0; loggedLogs.length = 0; return logs; } export function clearWarnings() { const warnings = Array.from(loggedWarns); - unexpectedWarnCallStacks.length = 0; loggedWarns.length = 0; return warnings; } export function clearErrors() { const errors = Array.from(loggedErrors); - unexpectedErrorCallStacks.length = 0; loggedErrors.length = 0; return errors; } diff --git a/scripts/eslint-rules/__tests__/no-to-warn-dev-within-to-throw-test.internal.js b/scripts/eslint-rules/__tests__/no-to-warn-dev-within-to-throw-test.internal.js deleted file mode 100644 index b3c0d114cc7c7..0000000000000 --- a/scripts/eslint-rules/__tests__/no-to-warn-dev-within-to-throw-test.internal.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * 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'; - -const rule = require('../no-to-warn-dev-within-to-throw'); -const {RuleTester} = require('eslint'); -const ruleTester = new RuleTester(); - -ruleTester.run('eslint-rules/no-to-warn-dev-within-to-throw', rule, { - valid: [ - 'expect(callback).toWarnDev("warning");', - 'expect(function() { expect(callback).toThrow("error") }).toWarnDev("warning");', - ], - invalid: [ - { - code: 'expect(function() { expect(callback).toWarnDev("warning") }).toThrow("error");', - errors: [ - { - message: 'toWarnDev() matcher should not be nested', - }, - ], - }, - ], -}); diff --git a/scripts/eslint-rules/index.js b/scripts/eslint-rules/index.js index f66c49a80e40c..23af2e471a019 100644 --- a/scripts/eslint-rules/index.js +++ b/scripts/eslint-rules/index.js @@ -3,7 +3,6 @@ module.exports = { rules: { 'no-primitive-constructors': require('./no-primitive-constructors'), - 'no-to-warn-dev-within-to-throw': require('./no-to-warn-dev-within-to-throw'), 'warning-args': require('./warning-args'), 'prod-error-codes': require('./prod-error-codes'), 'no-production-logging': require('./no-production-logging'), diff --git a/scripts/eslint-rules/no-to-warn-dev-within-to-throw.js b/scripts/eslint-rules/no-to-warn-dev-within-to-throw.js deleted file mode 100644 index 3b22b34fd6960..0000000000000 --- a/scripts/eslint-rules/no-to-warn-dev-within-to-throw.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * 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'; - -module.exports = { - meta: { - schema: [], - }, - create(context) { - return { - Identifier(node) { - if (node.name === 'toWarnDev' || node.name === 'toErrorDev') { - let current = node; - while (current.parent) { - if (current.type === 'CallExpression') { - if ( - current && - current.callee && - current.callee.property && - current.callee.property.name === 'toThrow' - ) { - context.report( - node, - node.name + '() matcher should not be nested' - ); - } - } - current = current.parent; - } - } - }, - }; - }, -}; diff --git a/scripts/jest/matchers/__tests__/toWarnDev-test.js b/scripts/jest/matchers/__tests__/toWarnDev-test.js deleted file mode 100644 index 90b78d2cd4729..0000000000000 --- a/scripts/jest/matchers/__tests__/toWarnDev-test.js +++ /dev/null @@ -1,435 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * 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('toErrorDev', () => { - it('does not fail if a warning contains a stack', () => { - expect(() => { - if (__DEV__) { - console.error('Hello\n in div'); - } - }).toErrorDev('Hello'); - }); - - it('does not fail if all warnings contain a stack', () => { - expect(() => { - if (__DEV__) { - console.error('Hello\n in div'); - console.error('Good day\n in div'); - console.error('Bye\n in div'); - } - }).toErrorDev(['Hello', 'Good day', 'Bye']); - }); - - it('does not fail if warnings without stack explicitly opt out', () => { - expect(() => { - if (__DEV__) { - console.error('Hello'); - } - }).toErrorDev('Hello', {withoutStack: true}); - expect(() => { - if (__DEV__) { - console.error('Hello'); - console.error('Good day'); - console.error('Bye'); - } - }).toErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: true}); - }); - - it('does not fail when expected stack-less warning number matches the actual one', () => { - expect(() => { - if (__DEV__) { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - } - }).toErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: 1}); - }); - - if (__DEV__) { - // Helper methods avoids invalid toWarn().toThrow() nesting - // See no-to-warn-dev-within-to-throw - const expectToWarnAndToThrow = (expectBlock, expectedErrorMessage) => { - let caughtError; - try { - expectBlock(); - } catch (error) { - caughtError = error; - } - expect(caughtError).toBeDefined(); - expect(caughtError.message).toContain(expectedErrorMessage); - }; - - it('fails if a warning does not contain a stack', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello'); - }).toErrorDev('Hello'); - }, 'Received warning unexpectedly does not include a component stack'); - }); - - it('fails if some warnings do not contain a stack', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello\n in div'); - console.error('Good day\n in div'); - console.error('Bye'); - }).toErrorDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello'); - console.error('Good day\n in div'); - console.error('Bye\n in div'); - }).toErrorDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - }).toErrorDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello'); - console.error('Good day'); - console.error('Bye'); - }).toErrorDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - }); - - it('fails if warning is expected to not have a stack, but does', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello\n in div'); - }).toErrorDev('Hello', {withoutStack: true}); - }, 'Received warning unexpectedly includes a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - }).toErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: true}); - }, 'Received warning unexpectedly includes a component stack'); - }); - - it('fails if expected stack-less warning number does not match the actual one', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - }).toErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: 4}); - }, 'Expected 4 warnings without a component stack but received 1'); - }); - - it('fails if withoutStack is invalid', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi'); - }).toErrorDev('Hi', {withoutStack: null}); - }, 'Instead received object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi'); - }).toErrorDev('Hi', {withoutStack: {}}); - }, 'Instead received object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi'); - }).toErrorDev('Hi', {withoutStack: 'haha'}); - }, 'Instead received string'); - }); - - it('fails if the argument number does not match', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi %s', 'Sara', 'extra'); - }).toErrorDev('Hi', {withoutStack: true}); - }, 'Received 2 arguments for a message with 1 placeholders'); - - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi %s'); - }).toErrorDev('Hi', {withoutStack: true}); - }, 'Received 0 arguments for a message with 1 placeholders'); - }); - - it('fails if stack is passed twice', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi %s%s', '\n in div', '\n in div'); - }).toErrorDev('Hi'); - }, 'Received more than one component stack for a warning'); - }); - - it('fails if multiple strings are passed without an array wrapper', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi \n in div'); - }).toErrorDev('Hi', 'Bye'); - }, 'toErrorDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi \n in div'); - console.error('Bye \n in div'); - }).toErrorDev('Hi', 'Bye'); - }, 'toErrorDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi \n in div'); - console.error('Wow \n in div'); - console.error('Bye \n in div'); - }).toErrorDev('Hi', 'Bye'); - }, 'toErrorDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi \n in div'); - console.error('Wow \n in div'); - console.error('Bye \n in div'); - }).toErrorDev('Hi', 'Wow', 'Bye'); - }, 'toErrorDev() second argument, when present, should be an object'); - }); - - it('fails on more than two arguments', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.error('Hi \n in div'); - console.error('Wow \n in div'); - console.error('Bye \n in div'); - }).toErrorDev('Hi', undefined, 'Bye'); - }, 'toErrorDev() received more than two arguments.'); - }); - } -}); - -describe('toWarnDev', () => { - it('does not fail if a warning contains a stack', () => { - expect(() => { - if (__DEV__) { - console.warn('Hello\n in div'); - } - }).toWarnDev('Hello'); - }); - - it('does not fail if all warnings contain a stack', () => { - expect(() => { - if (__DEV__) { - console.warn('Hello\n in div'); - console.warn('Good day\n in div'); - console.warn('Bye\n in div'); - } - }).toWarnDev(['Hello', 'Good day', 'Bye']); - }); - - it('does not fail if warnings without stack explicitly opt out', () => { - expect(() => { - if (__DEV__) { - console.warn('Hello'); - } - }).toWarnDev('Hello', {withoutStack: true}); - expect(() => { - if (__DEV__) { - console.warn('Hello'); - console.warn('Good day'); - console.warn('Bye'); - } - }).toWarnDev(['Hello', 'Good day', 'Bye'], {withoutStack: true}); - }); - - it('does not fail when expected stack-less warning number matches the actual one', () => { - expect(() => { - if (__DEV__) { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); - } - }).toWarnDev(['Hello', 'Good day', 'Bye'], {withoutStack: 1}); - }); - - if (__DEV__) { - // Helper methods avoids invalid toWarn().toThrow() nesting - // See no-to-warn-dev-within-to-throw - const expectToWarnAndToThrow = (expectBlock, expectedErrorMessage) => { - let caughtError; - try { - expectBlock(); - } catch (error) { - caughtError = error; - } - expect(caughtError).toBeDefined(); - expect(caughtError.message).toContain(expectedErrorMessage); - }; - - it('fails if a warning does not contain a stack', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello'); - }).toWarnDev('Hello'); - }, 'Received warning unexpectedly does not include a component stack'); - }); - - it('fails if some warnings do not contain a stack', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello\n in div'); - console.warn('Good day\n in div'); - console.warn('Bye'); - }).toWarnDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello'); - console.warn('Good day\n in div'); - console.warn('Bye\n in div'); - }).toWarnDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); - }).toWarnDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello'); - console.warn('Good day'); - console.warn('Bye'); - }).toWarnDev(['Hello', 'Good day', 'Bye']); - }, 'Received warning unexpectedly does not include a component stack'); - }); - - it('fails if warning is expected to not have a stack, but does', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello\n in div'); - }).toWarnDev('Hello', {withoutStack: true}); - }, 'Received warning unexpectedly includes a component stack'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); - }).toWarnDev(['Hello', 'Good day', 'Bye'], { - withoutStack: true, - }); - }, 'Received warning unexpectedly includes a component stack'); - }); - - it('fails if expected stack-less warning number does not match the actual one', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); - }).toWarnDev(['Hello', 'Good day', 'Bye'], { - withoutStack: 4, - }); - }, 'Expected 4 warnings without a component stack but received 1'); - }); - - it('fails if withoutStack is invalid', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi'); - }).toWarnDev('Hi', {withoutStack: null}); - }, 'Instead received object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi'); - }).toWarnDev('Hi', {withoutStack: {}}); - }, 'Instead received object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi'); - }).toWarnDev('Hi', {withoutStack: 'haha'}); - }, 'Instead received string'); - }); - - it('fails if the argument number does not match', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi %s', 'Sara', 'extra'); - }).toWarnDev('Hi', {withoutStack: true}); - }, 'Received 2 arguments for a message with 1 placeholders'); - - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi %s'); - }).toWarnDev('Hi', {withoutStack: true}); - }, 'Received 0 arguments for a message with 1 placeholders'); - }); - - it('fails if stack is passed twice', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi %s%s', '\n in div', '\n in div'); - }).toWarnDev('Hi'); - }, 'Received more than one component stack for a warning'); - }); - - it('fails if multiple strings are passed without an array wrapper', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi \n in div'); - }).toWarnDev('Hi', 'Bye'); - }, 'toWarnDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi \n in div'); - console.warn('Bye \n in div'); - }).toWarnDev('Hi', 'Bye'); - }, 'toWarnDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi \n in div'); - console.warn('Wow \n in div'); - console.warn('Bye \n in div'); - }).toWarnDev('Hi', 'Bye'); - }, 'toWarnDev() second argument, when present, should be an object'); - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi \n in div'); - console.warn('Wow \n in div'); - console.warn('Bye \n in div'); - }).toWarnDev('Hi', 'Wow', 'Bye'); - }, 'toWarnDev() second argument, when present, should be an object'); - }); - - it('fails on more than two arguments', () => { - expectToWarnAndToThrow(() => { - expect(() => { - console.warn('Hi \n in div'); - console.warn('Wow \n in div'); - console.warn('Bye \n in div'); - }).toWarnDev('Hi', undefined, 'Bye'); - }, 'toWarnDev() received more than two arguments.'); - }); - } -}); - -describe('toLogDev', () => { - it('does not fail if warnings do not include a stack', () => { - expect(() => { - if (__DEV__) { - console.log('Hello'); - } - }).toLogDev('Hello'); - expect(() => { - if (__DEV__) { - console.log('Hello'); - console.log('Good day'); - console.log('Bye'); - } - }).toLogDev(['Hello', 'Good day', 'Bye']); - }); -}); diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js deleted file mode 100644 index b93a4daf75da5..0000000000000 --- a/scripts/jest/matchers/toWarnDev.js +++ /dev/null @@ -1,348 +0,0 @@ -'use strict'; - -const {diff: jestDiff} = require('jest-diff'); -const util = require('util'); -const shouldIgnoreConsoleError = require('internal-test-utils/shouldIgnoreConsoleError'); - -function normalizeCodeLocInfo(str) { - if (typeof str !== 'string') { - return str; - } - // This special case exists only for the special source location in - // ReactElementValidator. That will go away if we remove source locations. - str = str.replace(/Check your code at .+?:\d+/g, 'Check your code at **'); - // V8 format: - // at Component (/path/filename.js:123:45) - // React format: - // in Component (at filename.js:123) - return str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) { - if (name.endsWith('.render')) { - // Class components will have the `render` method as part of their stack trace. - // We strip that out in our normalization to make it look more like component stacks. - name = name.slice(0, name.length - 7); - } - return '\n in ' + name + ' (at **)'; - }); -} - -const createMatcherFor = (consoleMethod, matcherName) => - function matcher(callback, expectedMessages, options = {}) { - if (__DEV__) { - // Warn about incorrect usage of matcher. - if (typeof expectedMessages === 'string') { - expectedMessages = [expectedMessages]; - } else if (!Array.isArray(expectedMessages)) { - throw Error( - `${matcherName}() requires a parameter of type string or an array of strings ` + - `but was given ${typeof expectedMessages}.` - ); - } - if ( - options != null && - (typeof options !== 'object' || Array.isArray(options)) - ) { - throw new Error( - `${matcherName}() second argument, when present, should be an object. ` + - 'Did you forget to wrap the messages into an array?' - ); - } - if (arguments.length > 3) { - // `matcher` comes from Jest, so it's more than 2 in practice - throw new Error( - `${matcherName}() received more than two arguments. ` + - 'Did you forget to wrap the messages into an array?' - ); - } - - const withoutStack = options.withoutStack; - const logAllErrors = options.logAllErrors; - const warningsWithoutComponentStack = []; - const warningsWithComponentStack = []; - const unexpectedWarnings = []; - - let lastWarningWithMismatchingFormat = null; - let lastWarningWithExtraComponentStack = null; - - // Catch errors thrown by the callback, - // But only rethrow them if all test expectations have been satisfied. - // Otherwise an Error in the callback can mask a failed expectation, - // and result in a test that passes when it shouldn't. - let caughtError; - - const isLikelyAComponentStack = message => - typeof message === 'string' && - (message.includes('\n in ') || message.includes('\n at ')); - - const consoleSpy = (format, ...args) => { - // Ignore uncaught errors reported by jsdom - // and React addendums because they're too noisy. - if (!logAllErrors && shouldIgnoreConsoleError(format, args)) { - return; - } - - // Append Component Stacks. Simulates a framework or DevTools appending them. - if ( - typeof format === 'string' && - (consoleMethod === 'error' || consoleMethod === 'warn') - ) { - const React = require('react'); - if (React.captureOwnerStack) { - // enableOwnerStacks enabled. When it's always on, we can assume this case. - const stack = React.captureOwnerStack(); - if (stack) { - format += '%s'; - args.push(stack); - } - } else { - // Otherwise we have to use internals to emulate parent stacks. - const ReactSharedInternals = - React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE || - React.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; - if (ReactSharedInternals && ReactSharedInternals.getCurrentStack) { - const stack = ReactSharedInternals.getCurrentStack(); - if (stack !== '') { - format += '%s'; - args.push(stack); - } - } - } - } - - const message = util.format(format, ...args); - const normalizedMessage = normalizeCodeLocInfo(message); - - // Remember if the number of %s interpolations - // doesn't match the number of arguments. - // We'll fail the test if it happens. - let argIndex = 0; - // console.* could have been called with a non-string e.g. `console.error(new Error())` - String(format).replace(/%s/g, () => argIndex++); - if (argIndex !== args.length) { - lastWarningWithMismatchingFormat = { - format, - args, - expectedArgCount: argIndex, - }; - } - - // Protect against accidentally passing a component stack - // to warning() which already injects the component stack. - if ( - args.length >= 2 && - isLikelyAComponentStack(args[args.length - 1]) && - isLikelyAComponentStack(args[args.length - 2]) - ) { - lastWarningWithExtraComponentStack = { - format, - }; - } - - for (let index = 0; index < expectedMessages.length; index++) { - const expectedMessage = expectedMessages[index]; - if ( - normalizedMessage === expectedMessage || - normalizedMessage.includes(expectedMessage) - ) { - if (isLikelyAComponentStack(normalizedMessage)) { - warningsWithComponentStack.push(normalizedMessage); - } else { - warningsWithoutComponentStack.push(normalizedMessage); - } - expectedMessages.splice(index, 1); - return; - } - } - - let errorMessage; - if (expectedMessages.length === 0) { - errorMessage = - 'Unexpected warning recorded: ' + - this.utils.printReceived(normalizedMessage); - } else if (expectedMessages.length === 1) { - errorMessage = - 'Unexpected warning recorded: ' + - jestDiff(expectedMessages[0], normalizedMessage); - } else { - errorMessage = - 'Unexpected warning recorded: ' + - jestDiff(expectedMessages, [normalizedMessage]); - } - - // Record the call stack for unexpected warnings. - // We don't throw an Error here though, - // Because it might be suppressed by ReactFiberScheduler. - unexpectedWarnings.push(new Error(errorMessage)); - }; - - // TODO Decide whether we need to support nested toWarn* expectations. - // If we don't need it, add a check here to see if this is already our spy, - // And throw an error. - const originalMethod = console[consoleMethod]; - - // Avoid using Jest's built-in spy since it can't be removed. - console[consoleMethod] = consoleSpy; - - const onFinally = () => { - // Restore the unspied method so that unexpected errors fail tests. - console[consoleMethod] = originalMethod; - - // Any unexpected Errors thrown by the callback should fail the test. - // This should take precedence since unexpected errors could block warnings. - if (caughtError) { - throw caughtError; - } - - // Any unexpected warnings should be treated as a failure. - if (unexpectedWarnings.length > 0) { - return { - message: () => unexpectedWarnings[0].stack, - pass: false, - }; - } - - // Any remaining messages indicate a failed expectations. - if (expectedMessages.length > 0) { - return { - message: () => - `Expected warning was not recorded:\n ${this.utils.printReceived( - expectedMessages[0] - )}`, - pass: false, - }; - } - - if (consoleMethod === 'log') { - // We don't expect any console.log calls to have a stack. - } else if (typeof withoutStack === 'number') { - // We're expecting a particular number of warnings without stacks. - if (withoutStack !== warningsWithoutComponentStack.length) { - return { - message: () => - `Expected ${withoutStack} warnings without a component stack but received ${warningsWithoutComponentStack.length}:\n` + - warningsWithoutComponentStack.map(warning => - this.utils.printReceived(warning) - ), - pass: false, - }; - } - } else if (withoutStack === true) { - // We're expecting that all warnings won't have the stack. - // If some warnings have it, it's an error. - if (warningsWithComponentStack.length > 0) { - return { - message: () => - `Received warning unexpectedly includes a component stack:\n ${this.utils.printReceived( - warningsWithComponentStack[0] - )}\nIf this warning intentionally includes the component stack, remove ` + - `{withoutStack: true} from the ${matcherName}() call. If you have a mix of ` + - `warnings with and without stack in one ${matcherName}() call, pass ` + - `{withoutStack: N} where N is the number of warnings without stacks.`, - pass: false, - }; - } - } else if (withoutStack === false || withoutStack === undefined) { - // We're expecting that all warnings *do* have the stack (default). - // If some warnings don't have it, it's an error. - if (warningsWithoutComponentStack.length > 0) { - return { - message: () => - `Received warning unexpectedly does not include a component stack:\n ${this.utils.printReceived( - warningsWithoutComponentStack[0] - )}\nIf this warning intentionally omits the component stack, add ` + - `{withoutStack: true} to the ${matcherName} call.`, - pass: false, - }; - } - } else { - throw Error( - `The second argument for ${matcherName}(), when specified, must be an object. It may have a ` + - `property called "withoutStack" whose value may be undefined, boolean, or a number. ` + - `Instead received ${typeof withoutStack}.` - ); - } - - if (lastWarningWithMismatchingFormat !== null) { - return { - message: () => - `Received ${ - lastWarningWithMismatchingFormat.args.length - } arguments for a message with ${ - lastWarningWithMismatchingFormat.expectedArgCount - } placeholders:\n ${this.utils.printReceived( - lastWarningWithMismatchingFormat.format - )}`, - pass: false, - }; - } - - if (lastWarningWithExtraComponentStack !== null) { - return { - message: () => - `Received more than one component stack for a warning:\n ${this.utils.printReceived( - lastWarningWithExtraComponentStack.format - )}\nDid you accidentally pass a stack to warning() as the last argument? ` + - `Don't forget warning() already injects the component stack automatically.`, - pass: false, - }; - } - - return {pass: true}; - }; - - let returnPromise = null; - try { - const result = callback(); - - if ( - typeof result === 'object' && - result !== null && - typeof result.then === 'function' - ) { - // `act` returns a thenable that can't be chained. - // Once `act(async () => {}).then(() => {}).then(() => {})` works - // we can just return `result.then(onFinally, error => ...)` - returnPromise = new Promise((resolve, reject) => { - result - .then( - () => { - resolve(onFinally()); - }, - error => { - caughtError = error; - return resolve(onFinally()); - } - ) - // In case onFinally throws we need to reject from this matcher - .catch(error => { - reject(error); - }); - }); - } - } catch (error) { - caughtError = error; - } finally { - return returnPromise === null ? onFinally() : returnPromise; - } - } else { - // Any uncaught errors or warnings should fail tests in production mode. - const result = callback(); - - if ( - typeof result === 'object' && - result !== null && - typeof result.then === 'function' - ) { - return result.then(() => { - return {pass: true}; - }); - } else { - return {pass: true}; - } - } - }; - -module.exports = { - toWarnDev: createMatcherFor('warn', 'toWarnDev'), - toErrorDev: createMatcherFor('error', 'toErrorDev'), - toLogDev: createMatcherFor('log', 'toLogDev'), -}; diff --git a/scripts/jest/setupTests.js b/scripts/jest/setupTests.js index d642e77a2a09e..01433ea583079 100644 --- a/scripts/jest/setupTests.js +++ b/scripts/jest/setupTests.js @@ -2,7 +2,7 @@ const {getTestFlags} = require('./TestFlags'); const { - flushAllUnexpectedConsoleCalls, + assertConsoleLogsCleared, resetAllUnexpectedConsoleCalls, patchConsoleMethods, } = require('internal-test-utils/consoleMock'); @@ -44,7 +44,6 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { expect.extend({ ...require('./matchers/reactTestMatchers'), ...require('./matchers/toThrow'), - ...require('./matchers/toWarnDev'), }); // We have a Babel transform that inserts guards against infinite loops. @@ -66,7 +65,19 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { // Patch the console to assert that all console error/warn/log calls assert. patchConsoleMethods({includeLog: !!process.env.CI}); beforeEach(resetAllUnexpectedConsoleCalls); - afterEach(flushAllUnexpectedConsoleCalls); + afterEach(assertConsoleLogsCleared); + + // TODO: enable this check so we don't forget to reset spyOnX mocks. + // afterEach(() => { + // if ( + // console[methodName] !== mockMethod && + // !jest.isMockFunction(console[methodName]) + // ) { + // throw new Error( + // `Test did not tear down console.${methodName} mock properly.` + // ); + // } + // }); if (process.env.NODE_ENV === 'production') { // In production, we strip error messages and turn them into codes. @@ -187,7 +198,7 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { // Flush unexpected console calls inside the test itself, instead of in // `afterEach` like we normally do. `afterEach` is too late because if it // throws, we won't have captured it. - flushAllUnexpectedConsoleCalls(); + assertConsoleLogsCleared(); } catch (testError) { didError = true; } diff --git a/scripts/jest/spec-equivalence-reporter/setupTests.js b/scripts/jest/spec-equivalence-reporter/setupTests.js index c3eaf5690afcb..a561fb4b4d1d4 100644 --- a/scripts/jest/spec-equivalence-reporter/setupTests.js +++ b/scripts/jest/spec-equivalence-reporter/setupTests.js @@ -10,7 +10,7 @@ const { patchConsoleMethods, resetAllUnexpectedConsoleCalls, - flushAllUnexpectedConsoleCalls, + assertConsoleLogsCleared, } = require('internal-test-utils/consoleMock'); const spyOn = jest.spyOn; @@ -44,10 +44,21 @@ global.spyOnProd = function (...args) { // Patch the console to assert that all console error/warn/log calls assert. patchConsoleMethods({includeLog: !!process.env.CI}); beforeEach(resetAllUnexpectedConsoleCalls); -afterEach(flushAllUnexpectedConsoleCalls); +afterEach(assertConsoleLogsCleared); + +// TODO: enable this check so we don't forget to reset spyOnX mocks. +// afterEach(() => { +// if ( +// console[methodName] !== mockMethod && +// !jest.isMockFunction(console[methodName]) +// ) { +// throw new Error( +// `Test did not tear down console.${methodName} mock properly.` +// ); +// } +// }); expect.extend({ ...require('../matchers/reactTestMatchers'), ...require('../matchers/toThrow'), - ...require('../matchers/toWarnDev'), }); diff --git a/scripts/jest/typescript/jest.d.ts b/scripts/jest/typescript/jest.d.ts index 57e93783665a7..ff450acf53395 100644 --- a/scripts/jest/typescript/jest.d.ts +++ b/scripts/jest/typescript/jest.d.ts @@ -8,7 +8,7 @@ declare function describe(name: string, fn: any): void; declare const it: { (name: string, fn: any): void; only: (name: string, fn: any) => void; -} +}; declare function expect(val: any): Expect; declare const jest: Jest; declare function pit(name: string, fn: any): void; @@ -19,55 +19,53 @@ 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 - toErrorDev(message?: string | Array, options?: any): void - toWarnDev(message?: string | Array, options?: any): 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 + 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 + 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 + (...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 + calls: Array>; + instances: Array; + }; + mockClear(): void; + mockImplementation(fn: Function): MockFunction; + mockImpl(fn: Function): MockFunction; + mockReturnThis(): MockFunction; + mockReturnValue(value: any): MockFunction; + mockReturnValueOnce(value: any): MockFunction; } declare const check: any;