Skip to content

Commit ae9b5f1

Browse files
committed
Incubator.Dialog - allow headless testing mocking (#2443)
* Incubator.Dialog - allow headless testing mocking * Rename
1 parent b07eb56 commit ae9b5f1

File tree

6 files changed

+107
-10
lines changed

6 files changed

+107
-10
lines changed

jest-setup.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {NativeModules, AccessibilityInfo} from 'react-native';
2+
global._UILIB_TESTING = true;
23

34
NativeModules.StatusBarManager = {getHeight: jest.fn()};
45
jest.spyOn(AccessibilityInfo, 'isScreenReaderEnabled').mockImplementation(() => new Promise.resolve(false));
@@ -17,9 +18,30 @@ jest.mock('react-native-reanimated', () => {
1718
});
1819
global.__reanimatedWorkletInit = jest.fn();
1920
jest.mock('react-native-gesture-handler',
20-
() => ({
21-
FlatList: require('react-native').FlatList
22-
}),
21+
() => {
22+
jest.requireActual('react-native-gesture-handler/jestSetup');
23+
const GestureHandler = jest.requireActual('react-native-gesture-handler');
24+
GestureHandler.Gesture.Pan = () => {
25+
const PanMock = {
26+
_handlers: {}
27+
};
28+
29+
const getDefaultMockedHandler = handlerName => handler => {
30+
if (typeof handler === 'function') {
31+
PanMock._handlers[handlerName] = handler;
32+
}
33+
return PanMock;
34+
};
35+
36+
PanMock.onStart = getDefaultMockedHandler('onStart');
37+
PanMock.onUpdate = getDefaultMockedHandler('onUpdate');
38+
PanMock.onEnd = getDefaultMockedHandler('onEnd');
39+
PanMock.prepare = jest.fn();
40+
return PanMock;
41+
};
42+
43+
return GestureHandler;
44+
},
2345
{virtual: true});
2446
jest.mock('@react-native-picker/picker', () => ({Picker: {Item: {}}}));
2547
jest.mock('react-native', () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, {useCallback, useEffect, useState} from 'react';
2+
import Button from '../../../components/button';
3+
import View from '../../../components/view';
4+
import Dialog from '../index';
5+
import {ButtonDriver, ComponentDriver} from '../../../testkit';
6+
7+
const onDismiss = () => {};
8+
9+
const defaultProps = {
10+
testID: 'dialog',
11+
useSafeArea: true,
12+
onDismiss,
13+
bottom: true,
14+
centerH: true
15+
};
16+
17+
const TestCase = props => {
18+
const [visible, setVisible] = useState(props.visible);
19+
20+
useEffect(() => {
21+
setVisible(props.visible);
22+
}, [props.visible]);
23+
24+
const openDialog = useCallback(() => {
25+
setVisible(true);
26+
}, []);
27+
28+
const closeDialog = useCallback(() => {
29+
setVisible(false);
30+
}, []);
31+
32+
return (
33+
<View>
34+
<Dialog {...defaultProps} {...props} visible={visible}>
35+
<View height={300}>
36+
<Button testID={'closeButton'} flex onPress={closeDialog}/>
37+
</View>
38+
</Dialog>
39+
<Button testID={'openButton'} flex onPress={openDialog}/>
40+
</View>
41+
);
42+
};
43+
44+
const _TestCase = props => <TestCase {...props}/>;
45+
46+
describe('Incubator.Dialog', () => {
47+
it('Incubator.Dialog should exist only if visible', async () => {
48+
const onDismiss = jest.fn();
49+
const component = _TestCase({onDismiss});
50+
const dialogDriver = new ComponentDriver({component, testID: 'dialog'});
51+
expect(await dialogDriver.exists()).toBeFalsy();
52+
const openButtonDriver = new ButtonDriver({component, testID: 'openButton'});
53+
await openButtonDriver.press();
54+
expect(await dialogDriver.exists()).toBeTruthy();
55+
expect(onDismiss).toHaveBeenCalledTimes(0);
56+
const closeButtonDriver = new ButtonDriver({component, testID: 'closeButton'});
57+
await closeButtonDriver.press();
58+
expect(await dialogDriver.exists()).toBeFalsy();
59+
expect(onDismiss).toHaveBeenCalledTimes(1);
60+
});
61+
});

src/incubator/Dialog/index.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
PanGestureHandlerEventPayload
1818
} from 'react-native-gesture-handler';
1919
import {Spacings, Colors, BorderRadiuses} from '../../style';
20+
import {useDidUpdate} from '../../hooks';
2021
import {asBaseComponent} from '../../commons/new';
2122
import View from '../../components/view';
2223
import Modal from '../../components/modal';
@@ -108,7 +109,7 @@ const Dialog = (props: DialogProps, ref: ForwardedRef<DialogImperativeMethods>)
108109
// eslint-disable-next-line react-hooks/exhaustive-deps
109110
}, [visible, wasMeasured]);
110111

111-
useEffect(() => {
112+
useDidUpdate(() => {
112113
if (wasMeasured) {
113114
if (modalVisibility) {
114115
open();
@@ -216,6 +217,10 @@ const Dialog = (props: DialogProps, ref: ForwardedRef<DialogImperativeMethods>)
216217
<View testID={`${testID}.overlayFadingBackground`} absF reanimated style={overlayStyle} pointerEvents="none"/>
217218
);
218219

220+
if (!modalVisibility) {
221+
return null;
222+
}
223+
219224
return (
220225
<Modal
221226
transparent

src/incubator/hooks/useHiddenLocation.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ export interface HiddenLocation extends HiddenLocationRecord {
1010
wasMeasured: boolean;
1111
}
1212

13+
// Adding this for headless tests that are not triggering onLayout
14+
const wasMeasuredDefaultValue = global._UILIB_TESTING ?? false;
15+
1316
export default function useHiddenLocation<T extends View>() {
1417
const getHiddenLocation = ({
1518
x = 0,
1619
y = 0,
1720
width = Constants.screenWidth,
1821
height = Constants.windowHeight,
19-
wasMeasured = false
22+
wasMeasured = wasMeasuredDefaultValue
2023
}): HiddenLocation => {
2124
return {
2225
up: -y - height,
@@ -30,7 +33,7 @@ export default function useHiddenLocation<T extends View>() {
3033
const [hiddenLocation, setHiddenLocation] = useState<HiddenLocation>(getHiddenLocation({}));
3134
const ref = useRef<T>();
3235
const layoutData = useRef<LayoutRectangle>();
33-
const wasMeasured = useRef(false);
36+
const wasMeasured = useRef(wasMeasuredDefaultValue);
3437

3538
const measure = useCallback(() => {
3639
if (ref.current && layoutData.current && layoutData.current.width > 0 && layoutData.current.height > 0) {

src/testkit/drivers/TestingLibraryDriver.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ export class TestingLibraryDriver implements UniDriver {
3333
if (!this.renderAPI) {
3434
throw new SelectorChainingException();
3535
}
36-
const instances = await this.renderAPI
37-
.findAllByTestId(testId)
38-
.catch(() => []);
39-
return new TestingLibraryDriver(instances);
36+
const instances = await this.renderAPI.queryAllByTestId(testId);
37+
if (instances) {
38+
return Promise.resolve(new TestingLibraryDriver(instances));
39+
} else {
40+
return Promise.reject(new NoSelectorException());
41+
}
4042
};
4143

4244
selectorByText = async (text: string): Promise<UniDriver> => {

typings/global.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
declare namespace globalThis {
2+
// eslint-disable-next-line no-var
3+
var _UILIB_TESTING: boolean;
4+
}

0 commit comments

Comments
 (0)