diff --git a/flow-typed/npm/jest.js b/flow-typed/npm/jest.js index 3a3c56d5550503..4b4bd7d5dfa1e7 100644 --- a/flow-typed/npm/jest.js +++ b/flow-typed/npm/jest.js @@ -853,6 +853,10 @@ type JestObjectType = { * Returns the number of fake timers still left to run. */ getTimerCount(): number, + /** + * Returns the time in ms of the current clock. + */ + now(): number, /** * Set the current system time used by fake timers. * Simulates a user changing the system clock while your program is running. diff --git a/packages/react-native/jest/MockNativeMethods.js b/packages/react-native/jest/MockNativeMethods.js index c0917b7f3a74e1..036d529c0b5ee8 100644 --- a/packages/react-native/jest/MockNativeMethods.js +++ b/packages/react-native/jest/MockNativeMethods.js @@ -4,11 +4,10 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict * @format */ -'use strict'; - const MockNativeMethods = { measure: jest.fn(), measureInWindow: jest.fn(), @@ -16,6 +15,13 @@ const MockNativeMethods = { setNativeProps: jest.fn(), focus: jest.fn(), blur: jest.fn(), +} as { + measure: () => void, + measureInWindow: () => void, + measureLayout: () => void, + setNativeProps: () => void, + focus: () => void, + blur: () => void, }; -module.exports = MockNativeMethods; +export default MockNativeMethods; diff --git a/packages/react-native/jest/RefreshControlMock.js b/packages/react-native/jest/RefreshControlMock.js index dd86060e7f0364..42111b9cbf4c46 100644 --- a/packages/react-native/jest/RefreshControlMock.js +++ b/packages/react-native/jest/RefreshControlMock.js @@ -21,10 +21,12 @@ const RCTRefreshControl: HostComponent<{}> = requireNativeComponent<{}>( export default class RefreshControlMock extends React.Component<{...}> { static latestRef: ?RefreshControlMock; + + render(): React.Node { + return ; + } + componentDidMount() { RefreshControlMock.latestRef = this; } - render(): React.MixedElement { - return ; - } } diff --git a/packages/react-native/jest/assetFileTransformer.js b/packages/react-native/jest/assetFileTransformer.js index c230a46e64c3be..13ec3e3c528111 100644 --- a/packages/react-native/jest/assetFileTransformer.js +++ b/packages/react-native/jest/assetFileTransformer.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @noflow * @format */ diff --git a/packages/react-native/jest/local-setup.js b/packages/react-native/jest/local-setup.js index dc728555fbc9cd..2d33843fe7b3a1 100644 --- a/packages/react-native/jest/local-setup.js +++ b/packages/react-native/jest/local-setup.js @@ -4,24 +4,25 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ // Global setup for tests local to the react-native repo. This setup is not // included in the react-native Jest preset. -'use strict'; - -require('./setup'); +import './setup'; const consoleError = console.error; const consoleWarn = console.warn; +// $FlowIgnore[cannot-write] console.error = (...args) => { consoleError(...args); throw new Error('console.error() was called (see error above)'); }; +// $FlowIgnore[cannot-write] console.warn = (...args) => { consoleWarn(...args); throw new Error('console.warn() was called (see warning above)'); diff --git a/packages/react-native/jest/mockComponent.js b/packages/react-native/jest/mockComponent.js index db79932447df27..4b63f8c4786796 100644 --- a/packages/react-native/jest/mockComponent.js +++ b/packages/react-native/jest/mockComponent.js @@ -4,37 +4,58 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict * @format */ -'use strict'; +import * as React from 'react'; +import {createElement} from 'react'; -module.exports = (moduleName, instanceMethods, isESModule) => { - const RealComponent = isESModule - ? jest.requireActual(moduleName).default - : jest.requireActual(moduleName); - const React = require('react'); +type Modulish = T | $ReadOnly<{default: T}>; +type ModuleDefault = T['default']; - const SuperClass = +type TComponentType = React.ComponentType<$ReadOnly<{children?: React.Node}>>; + +export default function mockComponent< + TComponentModule: Modulish, +>( + moduleName: string, + instanceMethods: ?interface {}, + isESModule: boolean, +): typeof isESModule extends true + ? ModuleDefault + : TComponentModule & typeof instanceMethods { + const RealComponent: TComponentType = isESModule + ? // $FlowIgnore[prop-missing] + jest.requireActual(moduleName).default + : // $FlowIgnore[incompatible-type] + jest.requireActual(moduleName); + + const SuperClass: typeof React.Component< + React.ElementProps, + > = typeof RealComponent === 'function' && RealComponent.prototype.constructor instanceof React.Component ? RealComponent : React.Component; const name = - RealComponent.displayName || - RealComponent.name || - (RealComponent.render // handle React.forwardRef - ? RealComponent.render.displayName || RealComponent.render.name - : 'Unknown'); + RealComponent.displayName ?? + RealComponent.name ?? + // $FlowFixMe[prop-missing] - Checking for `forwardRef` values. + (RealComponent.render == null + ? 'Unknown' + : // $FlowFixMe[incompatible-use] + RealComponent.render.displayName ?? RealComponent.render.name); const nameWithoutPrefix = name.replace(/^(RCT|RK)/, ''); const Component = class extends SuperClass { - static displayName = 'Component'; + static displayName: ?string = 'Component'; - render() { - const props = Object.assign({}, RealComponent.defaultProps); + render(): React.Node { + // $FlowIgnore[prop-missing] + const props = {...RealComponent.defaultProps}; if (this.props) { Object.keys(this.props).forEach(prop => { @@ -49,7 +70,8 @@ module.exports = (moduleName, instanceMethods, isESModule) => { }); } - return React.createElement(nameWithoutPrefix, props, this.props.children); + // $FlowIgnore[not-a-function] + return createElement(nameWithoutPrefix, props, this.props.children); } }; @@ -62,13 +84,16 @@ module.exports = (moduleName, instanceMethods, isESModule) => { Component.displayName = nameWithoutPrefix; + // $FlowIgnore[not-an-object] Object.keys(RealComponent).forEach(classStatic => { Component[classStatic] = RealComponent[classStatic]; }); if (instanceMethods != null) { + // $FlowIgnore[unsafe-object-assign] Object.assign(Component.prototype, instanceMethods); } + // $FlowIgnore[incompatible-return] return Component; -}; +} diff --git a/packages/react-native/jest/mockModal.js b/packages/react-native/jest/mockModal.js index 7bdd85932492d0..1b683831e33d8a 100644 --- a/packages/react-native/jest/mockModal.js +++ b/packages/react-native/jest/mockModal.js @@ -4,19 +4,21 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow strict-local + * @flow strict * @format */ -/* eslint-env jest */ +import * as React from 'react'; -'use strict'; - -const React = require('react'); - -function mockModal(BaseComponent: $FlowFixMe) { - class ModalMock extends BaseComponent { - render(): React.MixedElement | null { +export default function mockModal( + BaseComponent: React.ComponentType<{children?: React.Node}>, +): React.ComponentType<{ + ...React.ElementConfig, + visible?: ?boolean, +}> { + // $FlowIgnore[incompatible-use] + return class ModalMock extends BaseComponent { + render(): React.Node { if (this.props.visible === false) { return null; } @@ -25,8 +27,5 @@ function mockModal(BaseComponent: $FlowFixMe) { {this.props.children} ); } - } - return ModalMock; + }; } - -module.exports = (mockModal: $FlowFixMe); diff --git a/packages/react-native/jest/mockNativeComponent.js b/packages/react-native/jest/mockNativeComponent.js index ea5dd406f42db6..0e1d5cdbd90c70 100644 --- a/packages/react-native/jest/mockNativeComponent.js +++ b/packages/react-native/jest/mockNativeComponent.js @@ -4,31 +4,38 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict * @format */ -'use strict'; +import type {HostInstance} from '../src/private/types/HostInstance'; -const React = require('react'); -const {createElement} = require('react'); +import * as React from 'react'; +import {createElement} from 'react'; let nativeTag = 1; -export default viewName => { - const Component = class extends React.Component { - _nativeTag = nativeTag++; +type MockNativeComponent> = + component(ref?: ?React.RefSetter, ...props: TProps); - render() { +export default function mockNativeComponent< + TProps: $ReadOnly<{children?: React.Node}>, +>(viewName: string): MockNativeComponent { + const Component = class extends React.Component { + _nativeTag: number = nativeTag++; + + render(): React.Node { + // $FlowIgnore[not-a-function] return createElement(viewName, this.props, this.props.children); } // The methods that exist on host components - blur = jest.fn(); - focus = jest.fn(); - measure = jest.fn(); - measureInWindow = jest.fn(); - measureLayout = jest.fn(); - setNativeProps = jest.fn(); + blur: () => void = jest.fn(); + focus: () => void = jest.fn(); + measure: () => void = jest.fn(); + measureInWindow: () => void = jest.fn(); + measureLayout: () => void = jest.fn(); + setNativeProps: () => void = jest.fn(); }; if (viewName === 'RCTView') { @@ -38,4 +45,4 @@ export default viewName => { } return Component; -}; +} diff --git a/packages/react-native/jest/mockScrollView.js b/packages/react-native/jest/mockScrollView.js index 027f60eaafc1eb..6b7fbc0da273cd 100644 --- a/packages/react-native/jest/mockScrollView.js +++ b/packages/react-native/jest/mockScrollView.js @@ -8,19 +8,24 @@ * @format */ -/* eslint-env jest */ +import type {ScrollViewNativeProps} from '../Libraries/Components/ScrollView/ScrollViewNativeComponentType'; -'use strict'; +import View from '../Libraries/Components/View/View'; +import requireNativeComponent from '../Libraries/ReactNative/requireNativeComponent'; +import * as React from 'react'; -const View = require('../Libraries/Components/View/View').default; -const requireNativeComponent = - require('../Libraries/ReactNative/requireNativeComponent').default; -const React = require('react'); -const RCTScrollView: $FlowFixMe = requireNativeComponent('RCTScrollView'); +const RCTScrollView = + requireNativeComponent('RCTScrollView'); -function mockScrollView(BaseComponent: $FlowFixMe) { - class ScrollViewMock extends BaseComponent { - render(): React.MixedElement { +export default function mockScrollView( + BaseComponent: React.ComponentType<{children?: React.Node}>, +): React.ComponentType<{ + ...React.ElementConfig, + refreshControl?: ?React.MixedElement, +}> { + // $FlowIgnore[incompatible-use] + return class ScrollViewMock extends BaseComponent { + render(): React.Node { return ( {this.props.refreshControl} @@ -28,8 +33,5 @@ function mockScrollView(BaseComponent: $FlowFixMe) { ); } - } - return ScrollViewMock; + }; } - -module.exports = (mockScrollView: $FlowFixMe); diff --git a/packages/react-native/jest/react-native-env.js b/packages/react-native/jest/react-native-env.js index 494bb884ef4582..f4a3a2ad1c9c26 100644 --- a/packages/react-native/jest/react-native-env.js +++ b/packages/react-native/jest/react-native-env.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @noflow * @format */ diff --git a/packages/react-native/jest/renderer.js b/packages/react-native/jest/renderer.js index 4fb889da3f8c77..7098815ea79b64 100644 --- a/packages/react-native/jest/renderer.js +++ b/packages/react-native/jest/renderer.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict * @format */ diff --git a/packages/react-native/jest/resolver.js b/packages/react-native/jest/resolver.js index 31e3a438764a3f..9148a91fa64f9c 100644 --- a/packages/react-native/jest/resolver.js +++ b/packages/react-native/jest/resolver.js @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @noflow * @format */ diff --git a/packages/react-native/jest/setup.js b/packages/react-native/jest/setup.js index 5b3bab6d161741..b70fee91eb0a80 100644 --- a/packages/react-native/jest/setup.js +++ b/packages/react-native/jest/setup.js @@ -4,21 +4,39 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * + * @flow strict-local * @format */ -'use strict'; +// NOTE: Ideally, these would use `$Exports`, but Flow is struggling to resolve +// these module specifiers. Also, these are prefixed with `mock_` to workaround +// Jest's `babel-plugin-jest-hoist` plugin which validates that mock factories +// only reference local variables. (It is unaware of generic type annotations.) +import typeof * as mock_TScrollView from '../Libraries/Components/ScrollView/ScrollView'; +import type {ViewProps as mock_ViewProps} from '../Libraries/Components/View/ViewPropTypes'; +import typeof * as mock_TModal from '../Libraries/Modal/Modal'; +import typeof * as mock_TMockComponent from './mockComponent'; +import typeof * as mock_TMockModal from './mockModal'; +import typeof * as mock_TMockNativeComponent from './mockNativeComponent'; +import typeof * as mock_TMockNativeMethods from './MockNativeMethods'; +import typeof * as mock_TMockScrollView from './mockScrollView'; +import typeof * as mock_TRefreshControlMock from './RefreshControlMock'; global.IS_REACT_ACT_ENVIRONMENT = true; // Suppress the `react-test-renderer` warnings until New Architecture and legacy // mode are no longer supported by React Native. global.IS_REACT_NATIVE_TEST_ENVIRONMENT = true; -const MockNativeMethods = jest.requireActual('./MockNativeMethods'); -const mockComponent = jest.requireActual('./mockComponent'); +jest.requireActual('@react-native/js-polyfills/error-guard'); -jest.requireActual('@react-native/js-polyfills/error-guard'); +/** + * @see https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options + */ +function mockESModule(exports: T): {__esModule: true, ...T} { + return {__esModule: true, ...exports}; +} +// $FlowIgnore[cannot-write] Object.defineProperties(global, { __DEV__: { configurable: true, @@ -29,7 +47,9 @@ Object.defineProperties(global, { cancelAnimationFrame: { configurable: true, enumerable: true, - value: id => clearTimeout(id), + value(id: TimeoutID): void { + return clearTimeout(id); + }, writable: true, }, nativeFabricUIManager: { @@ -42,6 +62,7 @@ Object.defineProperties(global, { configurable: true, enumerable: true, value: { + // $FlowIgnore[method-unbinding] now: jest.fn(Date.now), }, writable: true, @@ -49,13 +70,16 @@ Object.defineProperties(global, { regeneratorRuntime: { configurable: true, enumerable: true, - value: jest.requireActual('regenerator-runtime/runtime'), + value: jest.requireActual('regenerator-runtime/runtime'), writable: true, }, requestAnimationFrame: { configurable: true, enumerable: true, - value: callback => setTimeout(() => callback(jest.now()), 0), + value(callback: number => void): TimeoutID { + // $FlowFixMe[prop-missing] - JestObjectType is incomplete. + return setTimeout(() => callback(jest.now()), 0); + }, writable: true, }, window: { @@ -69,142 +93,184 @@ Object.defineProperties(global, { jest .mock('../Libraries/Core/InitializeCore', () => {}) .mock('../Libraries/Core/NativeExceptionsManager') - .mock('../Libraries/ReactNative/UIManager', () => ({ - __esModule: true, - default: { - AndroidViewPager: { - Commands: { - setPage: jest.fn(), - setPageWithoutAnimation: jest.fn(), + .mock('../Libraries/ReactNative/UIManager', () => + mockESModule({ + default: { + AndroidViewPager: { + Commands: { + setPage: jest.fn(), + setPageWithoutAnimation: jest.fn(), + }, }, - }, - blur: jest.fn(), - createView: jest.fn(), - customBubblingEventTypes: {}, - customDirectEventTypes: {}, - dispatchViewManagerCommand: jest.fn(), - focus: jest.fn(), - getViewManagerConfig: jest.fn(name => { - if (name === 'AndroidDrawerLayout') { - return { - Constants: { - DrawerPosition: { - Left: 10, + blur: jest.fn(), + createView: jest.fn(), + customBubblingEventTypes: {}, + customDirectEventTypes: {}, + dispatchViewManagerCommand: jest.fn(), + focus: jest.fn(), + getViewManagerConfig: jest.fn(name => { + if (name === 'AndroidDrawerLayout') { + return { + Constants: { + DrawerPosition: { + Left: 10, + }, }, + }; + } + }), + hasViewManagerConfig: jest.fn(name => { + return name === 'AndroidDrawerLayout'; + }), + measure: jest.fn(), + manageChildren: jest.fn(), + setChildren: jest.fn(), + updateView: jest.fn(), + AndroidDrawerLayout: { + Constants: { + DrawerPosition: { + Left: 10, }, - }; - } - }), - hasViewManagerConfig: jest.fn(name => { - return name === 'AndroidDrawerLayout'; - }), - measure: jest.fn(), - manageChildren: jest.fn(), - setChildren: jest.fn(), - updateView: jest.fn(), - AndroidDrawerLayout: { - Constants: { - DrawerPosition: { - Left: 10, }, }, + AndroidTextInput: { + Commands: {}, + }, + ScrollView: { + Constants: {}, + }, + View: { + Constants: {}, + }, }, - AndroidTextInput: { - Commands: {}, - }, - ScrollView: { - Constants: {}, - }, - View: { - Constants: {}, - }, - }, - })) - .mock('../Libraries/Image/Image', () => ({ - __esModule: true, - default: mockComponent( - '../Libraries/Image/Image', - /* instanceMethods */ null, - /* isESModule */ true, - ), - })) - .mock('../Libraries/Text/Text', () => ({ - __esModule: true, - default: mockComponent( - '../Libraries/Text/Text', - MockNativeMethods, - /* isESModule */ true, - ), - })) - .mock('../Libraries/Components/TextInput/TextInput', () => ({ - __esModule: true, - default: mockComponent( - '../Libraries/Components/TextInput/TextInput', - /* instanceMethods */ { - ...MockNativeMethods, - isFocused: jest.fn(), - clear: jest.fn(), - getNativeRef: jest.fn(), - }, - /* isESModule */ true, - ), - })) + }), + ) + .mock('../Libraries/Image/Image', () => { + const mockComponent = + jest.requireActual('./mockComponent').default; + return mockESModule({ + default: mockComponent( + '../Libraries/Image/Image', + /* instanceMethods */ null, + /* isESModule */ true, + ), + }); + }) + .mock('../Libraries/Text/Text', () => { + const MockNativeMethods = jest.requireActual( + './MockNativeMethods', + ).default; + const mockComponent = + jest.requireActual('./mockComponent').default; + + return mockESModule({ + default: mockComponent( + '../Libraries/Text/Text', + MockNativeMethods, + /* isESModule */ true, + ), + }); + }) + .mock('../Libraries/Components/TextInput/TextInput', () => { + const MockNativeMethods = jest.requireActual( + './MockNativeMethods', + ).default; + const mockComponent = + jest.requireActual('./mockComponent').default; + + return mockESModule({ + default: mockComponent( + '../Libraries/Components/TextInput/TextInput', + /* instanceMethods */ { + ...MockNativeMethods, + isFocused: jest.fn(), + clear: jest.fn(), + getNativeRef: jest.fn(), + }, + /* isESModule */ true, + ), + }); + }) .mock('../Libraries/Modal/Modal', () => { - const baseComponent = mockComponent( + const mockComponent = + jest.requireActual('./mockComponent').default; + const mockModal = + jest.requireActual('./mockModal').default; + + const baseComponent = mockComponent( '../Libraries/Modal/Modal', /* instanceMethods */ null, /* isESModule */ true, ); - const mockModal = jest.requireActual('./mockModal'); - return { - __esModule: true, + + return mockESModule({ default: mockModal(baseComponent), - }; + }); }) - .mock('../Libraries/Components/View/View', () => ({ - __esModule: true, - default: mockComponent( - '../Libraries/Components/View/View', - /* instanceMethods */ MockNativeMethods, - /* isESModule */ true, - ), - })) - .mock('../Libraries/Components/AccessibilityInfo/AccessibilityInfo', () => ({ - __esModule: true, - default: { - addEventListener: jest.fn(() => ({ - remove: jest.fn(), - })), - announceForAccessibility: jest.fn(), - announceForAccessibilityWithOptions: jest.fn(), - isAccessibilityServiceEnabled: jest.fn(() => Promise.resolve(false)), - isBoldTextEnabled: jest.fn(() => Promise.resolve(false)), - isGrayscaleEnabled: jest.fn(() => Promise.resolve(false)), - isInvertColorsEnabled: jest.fn(() => Promise.resolve(false)), - isReduceMotionEnabled: jest.fn(() => Promise.resolve(false)), - isHighTextContrastEnabled: jest.fn(() => Promise.resolve(false)), - isDarkerSystemColorsEnabled: jest.fn(() => Promise.resolve(false)), - prefersCrossFadeTransitions: jest.fn(() => Promise.resolve(false)), - isReduceTransparencyEnabled: jest.fn(() => Promise.resolve(false)), - isScreenReaderEnabled: jest.fn(() => Promise.resolve(false)), - setAccessibilityFocus: jest.fn(), - sendAccessibilityEvent: jest.fn(), - getRecommendedTimeoutMillis: jest.fn(() => Promise.resolve(false)), - }, - })) - .mock('../Libraries/Components/Clipboard/Clipboard', () => ({ - __esModule: true, - default: { - getString: jest.fn(() => ''), - setString: jest.fn(), - }, - })) - .mock('../Libraries/Components/RefreshControl/RefreshControl', () => ({ - __esModule: true, - default: jest.requireActual('./RefreshControlMock').default, - })) + .mock('../Libraries/Components/View/View', () => { + const MockNativeMethods = jest.requireActual( + './MockNativeMethods', + ).default; + const mockComponent = + jest.requireActual('./mockComponent').default; + + return mockESModule({ + default: mockComponent( + '../Libraries/Components/View/View', + /* instanceMethods */ MockNativeMethods, + /* isESModule */ true, + ), + }); + }) + .mock('../Libraries/Components/AccessibilityInfo/AccessibilityInfo', () => + mockESModule({ + default: { + addEventListener: jest.fn(() => ({ + remove: jest.fn(), + })), + announceForAccessibility: jest.fn(), + announceForAccessibilityWithOptions: jest.fn(), + isAccessibilityServiceEnabled: jest.fn(() => Promise.resolve(false)), + isBoldTextEnabled: jest.fn(() => Promise.resolve(false)), + isGrayscaleEnabled: jest.fn(() => Promise.resolve(false)), + isInvertColorsEnabled: jest.fn(() => Promise.resolve(false)), + isReduceMotionEnabled: jest.fn(() => Promise.resolve(false)), + isHighTextContrastEnabled: jest.fn(() => Promise.resolve(false)), + isDarkerSystemColorsEnabled: jest.fn(() => Promise.resolve(false)), + prefersCrossFadeTransitions: jest.fn(() => Promise.resolve(false)), + isReduceTransparencyEnabled: jest.fn(() => Promise.resolve(false)), + isScreenReaderEnabled: jest.fn(() => Promise.resolve(false)), + setAccessibilityFocus: jest.fn(), + sendAccessibilityEvent: jest.fn(), + getRecommendedTimeoutMillis: jest.fn(() => Promise.resolve(false)), + }, + }), + ) + .mock('../Libraries/Components/Clipboard/Clipboard', () => + mockESModule({ + default: { + getString: jest.fn(() => ''), + setString: jest.fn(), + }, + }), + ) + .mock('../Libraries/Components/RefreshControl/RefreshControl', () => + mockESModule({ + default: jest.requireActual( + './RefreshControlMock', + ).default, + }), + ) .mock('../Libraries/Components/ScrollView/ScrollView', () => { - const baseComponent = mockComponent( + const MockNativeMethods = jest.requireActual( + './MockNativeMethods', + ).default; + const mockComponent = + jest.requireActual('./mockComponent').default; + const mockScrollView = + jest.requireActual('./mockScrollView').default; + + const baseComponent = mockComponent( '../Libraries/Components/ScrollView/ScrollView', { ...MockNativeMethods, @@ -221,274 +287,298 @@ jest }, true, // isESModule ); - const mockScrollView = jest.requireActual('./mockScrollView'); - return { - __esModule: true, + + return mockESModule({ default: mockScrollView(baseComponent), - }; + }); }) - .mock('../Libraries/Components/ActivityIndicator/ActivityIndicator', () => ({ - __esModule: true, - default: mockComponent( - '../Libraries/Components/ActivityIndicator/ActivityIndicator', - null, // instanceMethods - true, // isESModule - ), - })) - .mock('../Libraries/AppState/AppState', () => ({ - __esModule: true, - default: { - addEventListener: jest.fn(() => ({ - remove: jest.fn(), - })), - removeEventListener: jest.fn(), - currentState: jest.fn(), - }, - })) - .mock('../Libraries/Linking/Linking', () => ({ - __esModule: true, - default: { - openURL: jest.fn(), - canOpenURL: jest.fn(() => Promise.resolve(true)), - openSettings: jest.fn(), - addEventListener: jest.fn(() => ({ - remove: jest.fn(), - })), - getInitialURL: jest.fn(() => Promise.resolve()), - sendIntent: jest.fn(), - }, - })) - // Mock modules defined by the native layer (ex: Objective-C, Java) - .mock('../Libraries/BatchedBridge/NativeModules', () => ({ - __esModule: true, - default: { - AlertManager: { - alertWithArgs: jest.fn(), + .mock('../Libraries/Components/ActivityIndicator/ActivityIndicator', () => { + const mockComponent = + jest.requireActual('./mockComponent').default; + return mockESModule({ + default: mockComponent( + '../Libraries/Components/ActivityIndicator/ActivityIndicator', + null, // instanceMethods + true, // isESModule + ), + }); + }) + .mock('../Libraries/AppState/AppState', () => + mockESModule({ + default: { + addEventListener: jest.fn(() => ({ + remove: jest.fn(), + })), + removeEventListener: jest.fn(), + currentState: jest.fn(), }, - AsyncLocalStorage: { - multiGet: jest.fn((keys, callback) => - process.nextTick(() => callback(null, [])), - ), - multiSet: jest.fn((entries, callback) => - process.nextTick(() => callback(null)), - ), - multiRemove: jest.fn((keys, callback) => - process.nextTick(() => callback(null)), - ), - multiMerge: jest.fn((entries, callback) => - process.nextTick(() => callback(null)), - ), - clear: jest.fn(callback => process.nextTick(() => callback(null))), - getAllKeys: jest.fn(callback => - process.nextTick(() => callback(null, [])), - ), + }), + ) + .mock('../Libraries/Linking/Linking', () => + mockESModule({ + default: { + openURL: jest.fn(), + canOpenURL: jest.fn(() => Promise.resolve(true)), + openSettings: jest.fn(), + addEventListener: jest.fn(() => ({ + remove: jest.fn(), + })), + getInitialURL: jest.fn(() => Promise.resolve()), + sendIntent: jest.fn(), }, - DeviceInfo: { - getConstants() { - return { - Dimensions: { - window: { - fontScale: 2, - height: 1334, - scale: 2, - width: 750, - }, - screen: { - fontScale: 2, - height: 1334, - scale: 2, - width: 750, + }), + ) + // Mock modules defined by the native layer (ex: Objective-C, Java) + .mock('../Libraries/BatchedBridge/NativeModules', () => + mockESModule({ + default: { + AlertManager: { + alertWithArgs: jest.fn(), + }, + AsyncLocalStorage: { + multiGet: jest.fn((keys, callback) => + process.nextTick(() => callback(null, [])), + ), + multiSet: jest.fn((entries, callback) => + process.nextTick(() => callback(null)), + ), + multiRemove: jest.fn((keys, callback) => + process.nextTick(() => callback(null)), + ), + multiMerge: jest.fn((entries, callback) => + process.nextTick(() => callback(null)), + ), + clear: jest.fn(callback => process.nextTick(() => callback(null))), + getAllKeys: jest.fn(callback => + process.nextTick(() => callback(null, [])), + ), + }, + DeviceInfo: { + getConstants() { + return { + Dimensions: { + window: { + fontScale: 2, + height: 1334, + scale: 2, + width: 750, + }, + screen: { + fontScale: 2, + height: 1334, + scale: 2, + width: 750, + }, }, + }; + }, + }, + DevSettings: { + addMenuItem: jest.fn(), + reload: jest.fn(), + }, + ImageLoader: { + getSize: jest.fn(url => Promise.resolve([320, 240])), + getSizeWithHeaders: jest.fn((url, headers) => + Promise.resolve({height: 222, width: 333}), + ), + prefetchImage: jest.fn(), + prefetchImageWithMetadata: jest.fn(), + queryCache: jest.fn(), + }, + ImageViewManager: { + getSize: jest.fn((uri, success) => + process.nextTick(() => success(320, 240)), + ), + prefetchImage: jest.fn(), + }, + KeyboardObserver: { + addListener: jest.fn(), + removeListeners: jest.fn(), + }, + NativeAnimatedModule: { + createAnimatedNode: jest.fn(), + updateAnimatedNodeConfig: jest.fn(), + getValue: jest.fn(), + startListeningToAnimatedNodeValue: jest.fn(), + stopListeningToAnimatedNodeValue: jest.fn(), + connectAnimatedNodes: jest.fn(), + disconnectAnimatedNodes: jest.fn(), + startAnimatingNode: jest.fn( + (animationId, nodeTag, config, endCallback) => { + setTimeout(() => endCallback({finished: true}), 16); }, - }; + ), + stopAnimation: jest.fn(), + setAnimatedNodeValue: jest.fn(), + setAnimatedNodeOffset: jest.fn(), + flattenAnimatedNodeOffset: jest.fn(), + extractAnimatedNodeOffset: jest.fn(), + connectAnimatedNodeToView: jest.fn(), + disconnectAnimatedNodeFromView: jest.fn(), + restoreDefaultValues: jest.fn(), + dropAnimatedNode: jest.fn(), + addAnimatedEventToView: jest.fn(), + removeAnimatedEventFromView: jest.fn(), + addListener: jest.fn(), + removeListener: jest.fn(), + removeListeners: jest.fn(), }, - }, - DevSettings: { - addMenuItem: jest.fn(), - reload: jest.fn(), - }, - ImageLoader: { - getSize: jest.fn(url => Promise.resolve([320, 240])), - getSizeWithHeaders: jest.fn((url, headers) => - Promise.resolve({height: 222, width: 333}), - ), - prefetchImage: jest.fn(), - prefetchImageWithMetadata: jest.fn(), - queryCache: jest.fn(), - }, - ImageViewManager: { - getSize: jest.fn((uri, success) => - process.nextTick(() => success(320, 240)), - ), - prefetchImage: jest.fn(), - }, - KeyboardObserver: { - addListener: jest.fn(), - removeListeners: jest.fn(), - }, - NativeAnimatedModule: { - createAnimatedNode: jest.fn(), - updateAnimatedNodeConfig: jest.fn(), - getValue: jest.fn(), - startListeningToAnimatedNodeValue: jest.fn(), - stopListeningToAnimatedNodeValue: jest.fn(), - connectAnimatedNodes: jest.fn(), - disconnectAnimatedNodes: jest.fn(), - startAnimatingNode: jest.fn( - (animationId, nodeTag, config, endCallback) => { - setTimeout(() => endCallback({finished: true}), 16); + Networking: { + sendRequest: jest.fn(), + abortRequest: jest.fn(), + addListener: jest.fn(), + removeListeners: jest.fn(), + }, + PlatformConstants: { + getConstants() { + return { + reactNativeVersion: { + major: 1000, + minor: 0, + patch: 0, + prerelease: undefined, + }, + }; }, - ), - stopAnimation: jest.fn(), - setAnimatedNodeValue: jest.fn(), - setAnimatedNodeOffset: jest.fn(), - flattenAnimatedNodeOffset: jest.fn(), - extractAnimatedNodeOffset: jest.fn(), - connectAnimatedNodeToView: jest.fn(), - disconnectAnimatedNodeFromView: jest.fn(), - restoreDefaultValues: jest.fn(), - dropAnimatedNode: jest.fn(), - addAnimatedEventToView: jest.fn(), - removeAnimatedEventFromView: jest.fn(), - addListener: jest.fn(), - removeListener: jest.fn(), - removeListeners: jest.fn(), - }, - Networking: { - sendRequest: jest.fn(), - abortRequest: jest.fn(), - addListener: jest.fn(), - removeListeners: jest.fn(), - }, - PlatformConstants: { - getConstants() { - return { - reactNativeVersion: { - major: 1000, - minor: 0, - patch: 0, - prerelease: undefined, - }, - }; }, - }, - PushNotificationManager: { - presentLocalNotification: jest.fn(), - scheduleLocalNotification: jest.fn(), - cancelAllLocalNotifications: jest.fn(), - removeAllDeliveredNotifications: jest.fn(), - getDeliveredNotifications: jest.fn(callback => - process.nextTick(() => []), - ), - removeDeliveredNotifications: jest.fn(), - setApplicationIconBadgeNumber: jest.fn(), - getApplicationIconBadgeNumber: jest.fn(callback => - process.nextTick(() => callback(0)), - ), - cancelLocalNotifications: jest.fn(), - getScheduledLocalNotifications: jest.fn(callback => - process.nextTick(() => callback()), - ), - requestPermissions: jest.fn(() => - Promise.resolve({alert: true, badge: true, sound: true}), - ), - abandonPermissions: jest.fn(), - checkPermissions: jest.fn(callback => - process.nextTick(() => - callback({alert: true, badge: true, sound: true}), + PushNotificationManager: { + presentLocalNotification: jest.fn(), + scheduleLocalNotification: jest.fn(), + cancelAllLocalNotifications: jest.fn(), + removeAllDeliveredNotifications: jest.fn(), + getDeliveredNotifications: jest.fn(callback => + process.nextTick(() => []), ), - ), - getInitialNotification: jest.fn(() => Promise.resolve(null)), - addListener: jest.fn(), - removeListeners: jest.fn(), - }, - SourceCode: { - getConstants() { - return { - scriptURL: null, - }; + removeDeliveredNotifications: jest.fn(), + setApplicationIconBadgeNumber: jest.fn(), + getApplicationIconBadgeNumber: jest.fn(callback => + process.nextTick(() => callback(0)), + ), + cancelLocalNotifications: jest.fn(), + getScheduledLocalNotifications: jest.fn(callback => + process.nextTick(() => callback()), + ), + requestPermissions: jest.fn(() => + Promise.resolve({alert: true, badge: true, sound: true}), + ), + abandonPermissions: jest.fn(), + checkPermissions: jest.fn(callback => + process.nextTick(() => + callback({alert: true, badge: true, sound: true}), + ), + ), + getInitialNotification: jest.fn(() => Promise.resolve(null)), + addListener: jest.fn(), + removeListeners: jest.fn(), + }, + SourceCode: { + getConstants() { + return { + scriptURL: null, + }; + }, + }, + StatusBarManager: { + setColor: jest.fn(), + setStyle: jest.fn(), + setHidden: jest.fn(), + setNetworkActivityIndicatorVisible: jest.fn(), + setBackgroundColor: jest.fn(), + setTranslucent: jest.fn(), + getConstants: () => ({ + HEIGHT: 42, + }), + }, + Timing: { + createTimer: jest.fn(), + deleteTimer: jest.fn(), + }, + UIManager: {}, + BlobModule: { + getConstants: () => ({ + BLOB_URI_SCHEME: 'content', + BLOB_URI_HOST: null, + }), + addNetworkingHandler: jest.fn(), + enableBlobSupport: jest.fn(), + disableBlobSupport: jest.fn(), + createFromParts: jest.fn(), + sendBlob: jest.fn(), + release: jest.fn(), + }, + WebSocketModule: { + connect: jest.fn(), + send: jest.fn(), + sendBinary: jest.fn(), + ping: jest.fn(), + close: jest.fn(), + addListener: jest.fn(), + removeListeners: jest.fn(), + }, + I18nManager: { + allowRTL: jest.fn(), + forceRTL: jest.fn(), + swapLeftAndRightInRTL: jest.fn(), + getConstants: () => ({ + isRTL: false, + doLeftAndRightSwapInRTL: true, + }), }, }, - StatusBarManager: { - setColor: jest.fn(), - setStyle: jest.fn(), - setHidden: jest.fn(), - setNetworkActivityIndicatorVisible: jest.fn(), - setBackgroundColor: jest.fn(), - setTranslucent: jest.fn(), - getConstants: () => ({ - HEIGHT: 42, - }), - }, - Timing: { - createTimer: jest.fn(), - deleteTimer: jest.fn(), - }, - UIManager: {}, - BlobModule: { - getConstants: () => ({BLOB_URI_SCHEME: 'content', BLOB_URI_HOST: null}), - addNetworkingHandler: jest.fn(), - enableBlobSupport: jest.fn(), - disableBlobSupport: jest.fn(), - createFromParts: jest.fn(), - sendBlob: jest.fn(), - release: jest.fn(), - }, - WebSocketModule: { - connect: jest.fn(), - send: jest.fn(), - sendBinary: jest.fn(), - ping: jest.fn(), - close: jest.fn(), - addListener: jest.fn(), - removeListeners: jest.fn(), - }, - I18nManager: { - allowRTL: jest.fn(), - forceRTL: jest.fn(), - swapLeftAndRightInRTL: jest.fn(), - getConstants: () => ({ - isRTL: false, - doLeftAndRightSwapInRTL: true, - }), - }, - }, - })) + }), + ) .mock('../Libraries/NativeComponent/NativeComponentRegistry', () => { return { get: jest.fn((name, viewConfigProvider) => { - return jest.requireActual('./mockNativeComponent').default(name); + const mockNativeComponent = + jest.requireActual( + './mockNativeComponent', + ).default; + return mockNativeComponent(name); }), getWithFallback_DEPRECATED: jest.fn((name, viewConfigProvider) => { - return jest.requireActual('./mockNativeComponent').default(name); + const mockNativeComponent = + jest.requireActual( + './mockNativeComponent', + ).default; + return mockNativeComponent(name); }), setRuntimeConfigProvider: jest.fn(), }; }) .mock('../Libraries/ReactNative/requireNativeComponent', () => { - return jest.requireActual('./mockNativeComponent'); + const mockNativeComponent = jest.requireActual( + './mockNativeComponent', + ).default; + return mockESModule({ + default: mockNativeComponent, + }); }) - .mock('../Libraries/Vibration/Vibration', () => ({ - __esModule: true, - default: { - vibrate: jest.fn(), - cancel: jest.fn(), - }, - })) + .mock('../Libraries/Vibration/Vibration', () => + mockESModule({ + default: { + vibrate: jest.fn(), + cancel: jest.fn(), + }, + }), + ) .mock('../Libraries/Components/View/ViewNativeComponent', () => { const React = require('react'); - const Component = class extends React.Component { - render() { - return React.createElement('View', this.props, this.props.children); + const {createElement} = React; + + const Component = class extends React.Component { + render(): React.Node { + // $FlowIgnore[not-a-function] + return createElement('View', this.props, this.props.children); } }; Component.displayName = 'View'; - return { - __esModule: true, + return mockESModule({ default: Component, - }; + }); }) // In tests, we can use the default version instead of the one using // dependency injection. @@ -497,7 +587,8 @@ jest '../Libraries/ReactNative/RendererImplementation', ); }) - .mock('../Libraries/Utilities/useColorScheme', () => ({ - __esModule: true, - default: jest.fn().mockReturnValue('light'), - })); + .mock('../Libraries/Utilities/useColorScheme', () => + mockESModule({ + default: jest.fn().mockReturnValue('light'), + }), + );