Files
AntoineDoubovetzky ec614c16b3 Update Modal's mock to not render its children when it is not visible (#32346)
Summary:
The Modal's mock always render its children (whether it is visible or not), whereas in reality the Modal renders `null` when the Modal is not visible.
This causes troubles when trying to test whether the Modal is visible or not. Instead of testing if the children are rendered (using getByText from React Native Testing Library for instance), we are forced to test the value of the visible prop directly (see https://github.com/callstack/react-native-testing-library/issues/508 and https://github.com/callstack/react-native-testing-library/issues/659).
This is not ideal because we are forced to test implementation detail and can't test from the user perspective. I also believe the mock should be closest as possible from reality.

I had 2 options:
  1. Rendering the Modal without its children
  2. Not rendering the Modal at all

The latter has the advantage of being closer to the reality, but I chose the former to still be able to test the Modal through the visible prop, so there is no breaking change (only snapshots update will be required).

## Changelog

[General] [Changed] - Update Modal's mock to not render its children when it is not visible

Pull Request resolved: https://github.com/facebook/react-native/pull/32346

Test Plan:
I added a test case when visible is false, then updated the mock so the children are not rendered. The before / after is here:
![image](https://user-images.githubusercontent.com/17070498/136256142-a351d002-8b77-490a-ba65-1e8ad0d6eb55.png)

Reviewed By: yungsters

Differential Revision: D31445964

Pulled By: lunaleaps

fbshipit-source-id: 08501921455728cde6befd0103016c95074cc1df
2021-10-07 22:44:19 -07:00

372 lines
10 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const MockNativeMethods = jest.requireActual('./MockNativeMethods');
const mockComponent = jest.requireActual('./mockComponent');
jest.requireActual('@react-native/polyfills/Object.es8');
jest.requireActual('@react-native/polyfills/error-guard');
global.__DEV__ = true;
global.performance = {
now: jest.fn(Date.now),
};
global.Promise = jest.requireActual('promise');
global.regeneratorRuntime = jest.requireActual('regenerator-runtime/runtime');
global.window = global;
global.requestAnimationFrame = function(callback) {
return setTimeout(callback, 0);
};
global.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
// there's a __mock__ for it.
jest.setMock(
'../Libraries/vendor/core/ErrorUtils',
require('../Libraries/vendor/core/ErrorUtils'),
);
jest
.mock('../Libraries/Core/InitializeCore', () => {})
.mock('../Libraries/Core/NativeExceptionsManager', () => ({
__esModule: true,
default: {
reportException: jest.fn(),
},
}))
.mock('../Libraries/ReactNative/UIManager', () => ({
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,
},
},
};
}
}),
hasViewManagerConfig: jest.fn(name => {
return name === 'AndroidDrawerLayout';
}),
measure: jest.fn(),
manageChildren: jest.fn(),
removeSubviewsFromContainerWithID: jest.fn(),
replaceExistingNonRootView: jest.fn(),
setChildren: jest.fn(),
updateView: jest.fn(),
AndroidDrawerLayout: {
Constants: {
DrawerPosition: {
Left: 10,
},
},
},
AndroidTextInput: {
Commands: {},
},
ScrollView: {
Constants: {},
},
View: {
Constants: {},
},
}))
.mock('../Libraries/Image/Image', () =>
mockComponent('../Libraries/Image/Image'),
)
.mock('../Libraries/Text/Text', () =>
mockComponent('../Libraries/Text/Text', MockNativeMethods),
)
.mock('../Libraries/Components/TextInput/TextInput', () =>
mockComponent('../Libraries/Components/TextInput/TextInput', {
...MockNativeMethods,
isFocused: jest.fn(),
clear: jest.fn(),
getNativeRef: jest.fn(),
}),
)
.mock('../Libraries/Modal/Modal', () => {
const baseComponent = mockComponent('../Libraries/Modal/Modal');
const mockModal = jest.requireActual('./mockModal');
return mockModal(baseComponent);
})
.mock('../Libraries/Components/View/View', () =>
mockComponent('../Libraries/Components/View/View', MockNativeMethods),
)
.mock('../Libraries/Components/AccessibilityInfo/AccessibilityInfo', () => ({
__esModule: true,
default: {
addEventListener: jest.fn(),
announceForAccessibility: jest.fn(),
isBoldTextEnabled: jest.fn(),
isGrayscaleEnabled: jest.fn(),
isInvertColorsEnabled: jest.fn(),
isReduceMotionEnabled: jest.fn(),
isReduceTransparencyEnabled: jest.fn(),
isScreenReaderEnabled: jest.fn(() => Promise.resolve(false)),
removeEventListener: jest.fn(),
setAccessibilityFocus: jest.fn(),
sendAccessibilityEvent_unstable: jest.fn(),
getRecommendedTimeoutMillis: jest.fn(),
},
}))
.mock('../Libraries/Components/RefreshControl/RefreshControl', () =>
jest.requireActual(
'../Libraries/Components/RefreshControl/__mocks__/RefreshControlMock',
),
)
.mock('../Libraries/Components/ScrollView/ScrollView', () => {
const baseComponent = mockComponent(
'../Libraries/Components/ScrollView/ScrollView',
{
...MockNativeMethods,
getScrollResponder: jest.fn(),
getScrollableNode: jest.fn(),
getInnerViewNode: jest.fn(),
getInnerViewRef: jest.fn(),
getNativeScrollRef: jest.fn(),
scrollTo: jest.fn(),
scrollToEnd: jest.fn(),
flashScrollIndicators: jest.fn(),
scrollResponderZoomTo: jest.fn(),
scrollResponderScrollNativeHandleToKeyboard: jest.fn(),
},
);
const mockScrollView = jest.requireActual('./mockScrollView');
return mockScrollView(baseComponent);
})
.mock('../Libraries/Components/ActivityIndicator/ActivityIndicator', () =>
mockComponent(
'../Libraries/Components/ActivityIndicator/ActivityIndicator',
),
)
.mock('../Libraries/AppState/AppState', () => ({
addEventListener: jest.fn(() => ({
remove: jest.fn(),
})),
}))
.mock('../Libraries/Linking/Linking', () => ({
openURL: jest.fn(),
canOpenURL: jest.fn(() => Promise.resolve(true)),
openSettings: jest.fn(),
addEventListener: jest.fn(),
getInitialURL: jest.fn(() => Promise.resolve()),
removeEventListener: jest.fn(),
sendIntent: jest.fn(),
}))
// Mock modules defined by the native layer (ex: Objective-C, Java)
.mock('../Libraries/BatchedBridge/NativeModules', () => ({
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, [])),
),
},
Clipboard: {
getString: jest.fn(() => ''),
setString: jest.fn(),
},
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({width: 320, height: 240})),
prefetchImage: jest.fn(),
},
ImageViewManager: {
getSize: jest.fn((uri, success) =>
process.nextTick(() => success(320, 240)),
),
prefetchImage: jest.fn(),
},
KeyboardObserver: {
addListener: jest.fn(),
removeListeners: jest.fn(),
},
Networking: {
sendRequest: jest.fn(),
abortRequest: jest.fn(),
addListener: jest.fn(),
removeListeners: jest.fn(),
},
PlatformConstants: {
getConstants() {
return {};
},
},
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}),
),
),
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,
}),
},
}))
.mock('../Libraries/NativeComponent/NativeComponentRegistry', () => {
return {
get: jest.fn((name, viewConfigProvider) => {
return jest.requireActual('./mockNativeComponent')(name);
}),
getWithFallback_DEPRECATED: jest.fn((name, viewConfigProvider) => {
return jest.requireActual('./mockNativeComponent')(name);
}),
setRuntimeConfigProvider: jest.fn(),
};
})
.mock('../Libraries/ReactNative/requireNativeComponent', () => {
return jest.requireActual('./mockNativeComponent');
})
.mock(
'../Libraries/Utilities/verifyComponentAttributeEquivalence',
() => function() {},
)
.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);
}
};
Component.displayName = 'View';
return {
__esModule: true,
default: Component,
};
});