I am trying to test my component. In my component, there is a function to measure from the reference of View. when I debug, the ref value exists. but the measure method is undefined.
This is the screenshot of the testing.

I need help how to mock the .measure or other function from the component reference
Component:
import React, {useState, useCallback, useMemo, useRef} from 'react';
import {
FlatListProps,
Animated,
StyleSheet,
View,
FlatList,
ViewStyle,
TextStyle,
ListRenderItemInfo,
} from 'react-native';
import {Layout} from '../../layout';
import {useDidUpdate} from '../../utilities';
import {Icon} from '../icon';
import {Touchable} from '../touchable';
import {Modal} from '../modal';
import {Text} from '../text';
import {PickerSelectionInfo} from '../../types';
export interface PickerProps<ItemT> extends FlatListProps<ItemT> {
containerStyle?: ViewStyle;
titleStyle?: TextStyle;
titleContainerStyle?: ViewStyle;
selected?: string;
selectedContainerStyle?: ViewStyle;
selectedTitleStyle?: TextStyle;
listContainerStyle?: ViewStyle;
listItemContainerStyle?: ViewStyle;
listItemSelectedContainerStyle?: ViewStyle;
fullWidth?: boolean;
animationDuration?: number;
placeholder?: string;
placeholderStyle?: TextStyle;
icon?: JSX.Element;
iconContainerStyle?: ViewStyle;
iconStartRotation?: string;
iconEndRotation?: string;
data: ReadonlyArray<ItemT>;
keyExtractor(item: ItemT, index: number): string;
titleExtractor?(item: ItemT, index: number): string;
onSelect(select: PickerSelectionInfo<ItemT>): void;
}
export default function Picker<ItemT>({
containerStyle,
titleStyle,
titleContainerStyle,
selected,
selectedContainerStyle,
selectedTitleStyle,
listContainerStyle,
listItemContainerStyle,
listItemSelectedContainerStyle,
fullWidth = false,
animationDuration = 250,
placeholder = 'Select Option',
placeholderStyle,
icon,
iconContainerStyle,
iconStartRotation = '-90deg',
iconEndRotation = '0deg',
data,
keyExtractor,
titleExtractor,
onSelect,
renderItem,
...props
}: PickerProps<ItemT>) {
const [selection, setSelection] = useState<
PickerSelectionInfo<ItemT> | undefined
>(getInfoFromKey(selected));
const [toggle, setToggle] = useState(false);
const layout = useRef<Layout>();
const refButton = useRef<View>();
const animation = useState(new Animated.Value(0))[0];
function getInfoFromKey(
key?: string,
): PickerSelectionInfo<ItemT> | undefined {
if (key !== undefined) {
for (let index = 0; index < data.length; index++) {
const item = data[index];
if (keyExtractor(item, index) === key) {
return {key, index, item};
}
}
}
return undefined;
}
const handleRefButton = useCallback((instance: View | null) => {
if (instance) {
refButton.current = instance;
}
}, []);
const handleRunAnimation = useCallback(
() =>Animated.timing(animation, {
toValue: toggle ? 1 : 0,
duration: animationDuration,
useNativeDriver: true,
}).start(),
[animation, toggle, animationDuration],
);
const handlePressMenuItem = useCallback(
(key: string, item: ItemT, index: number) => {
setSelection({key, item, index});
setToggle(false);
onSelect({key, item, index});
},
[],
);
const handlePressButton = useCallback(
() =>
refButton.current?.measure((x, y, width, height, pageX, pageY) => {
layout.current = {
x,
y,
width,
height,
pageX,
pageY,
};
setToggle(!toggle);
}),
[refButton.current, toggle],
);
const handleRenderMenuItem = useCallback(
(info: ListRenderItemInfo<ItemT>) => {
const {item, index} = info;
const key = keyExtractor(item, index);
return (
<Touchablestyle={StyleSheet.flatten([styles.listItemContainer,
listItemContainerStyle,
selection?.index === index &&
StyleSheet.flatten([
styles.listItemSelectedContainer,
listItemSelectedContainerStyle,
]),
])}
onPress={() => handlePressMenuItem(key, item, index)}>
{renderItem(info)}
</Touchable>
);
},
[
selection,
listItemContainerStyle,
listItemSelectedContainerStyle,
keyExtractor,
renderItem,
handlePressMenuItem,
],
);
const handleRenderIcon = useMemo(
() => (
<Animated.Viewstyle={StyleSheet.flatten([styles.iconContainer,
iconContainerStyle,
{
transform: [
{
rotateZ: animation.interpolate({
inputRange: [0, 1],
outputRange: [iconStartRotation, iconEndRotation],
}),
},
],
},
])}>
{icon || <Icon name="chevron-down" />}
</Animated.View>
),
[animation, icon, iconContainerStyle, iconStartRotation, iconEndRotation],
);
const handleRenderTitle = useMemo(() => {
const title = selection
? titleExtractor
? titleExtractor(selection.item, selection.index)
: keyExtractor(selection.item, selection.index)
: placeholder;
return (
<Viewstyle={StyleSheet.flatten([styles.titleContainer,
titleContainerStyle,
styles.fixedTitleContainer,
])}><Textstyle={StyleSheet.flatten([styles.title,
titleStyle,
selection
? selectedTitleStyle: StyleSheet.flatten<TextStyle>([
styles.placeholder,
placeholderStyle,
]),
])}>
{title}
</Text></View>
);
}, [
selection,
titleStyle,
placeholder,
placeholderStyle,
selectedTitleStyle,
titleExtractor,
keyExtractor,
]);
const handleRenderButton = useMemo(
() => (
<TouchabletestID="button"touchableType="normal"refView={handleRefButton}style={StyleSheet.flatten([styles.container,
containerStyle,
toggle
? StyleSheet.flatten([
styles.selectedContainer,
selectedContainerStyle,
])
: {},
styles.fixedContainer,
])}
onPress={handlePressButton}>
{handleRenderIcon}
<View style={fullWidth && styles.sectionTitle}>
{handleRenderTitle}
</View></Touchable>
),
[
toggle,
animation,
handleRenderTitle,
fullWidth,
containerStyle,
selectedContainerStyle,
iconContainerStyle,
iconStartRotation,
iconEndRotation,
icon,
handleRenderIcon,
handleRefButton,
],
);
const handleRenderMenu = useMemo(
() =>
layout.current && (
<Modaltransparentvisible={toggle}onPressBackdrop={() => setToggle(!toggle)}>
<Animated.Viewstyle={StyleSheet.flatten([styles.listContainer,
listContainerStyle,
styles.fixedListContainer,
{
width: layout.current.width,
left: layout.current.pageX,
top: layout.current.pageY + layout.current.height,
opacity: animation.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
},
])}><FlatList
{...props}
data={data}keyExtractor={keyExtractor}renderItem={handleRenderMenuItem}
/></Animated.View></Modal>
),
[
layout.current,
toggle,
props,
data,
listContainerStyle,
keyExtractor,
handleRenderMenuItem,
],
);
useDidUpdate(() => setSelection(getInfoFromKey(selected)), [selected]);
useDidUpdate(handleRunAnimation, [handleRunAnimation]);
return (
<>
{handleRenderButton}
{handleRenderMenu}
</>
);
}
const styles = StyleSheet.create({
container: {
borderWidth: 1,
borderRadius: 4,
borderColor: 'lightgray',
backgroundColor: 'whitesmoke',
alignItems: 'center',
justifyContent: 'space-between',
},
fixedContainer: {
flexDirection: 'row-reverse',
},
selectedContainer: {
borderColor: 'dodgerblue',
backgroundColor: 'white',
},
listContainer: {
borderWidth: 1,
borderRadius: 4,
borderColor: 'lightgray',
backgroundColor: '#fff',
maxHeight: 300,
},
fixedListContainer: {
position: 'absolute',
zIndex: 1,
},
listItemContainer: {
paddingHorizontal: 12,
paddingTop: 12,
paddingBottom: 8,
justifyContent: 'center',
},
listItemSelectedContainer: {
backgroundColor: 'whitesmoke',
},
titleContainer: {
paddingLeft: 12,
},
fixedTitleContainer: {
flex: 1,
},
iconContainer: {
padding: 12,
},
sectionTitle: {
flex: 1,
},
title: {
flex: 1,
fontSize: 15,
textAlignVertical: 'center',
},
placeholder: {
color: 'darkgray',
},
});
Testing:
import React from 'react';
import {fireEvent, render} from '@testing-library/react-native';
import {ObjectPartial} from '../../types';
import Picker, {PickerProps} from './Picker';
const defaultProps: PickerProps<string> = {
data: ['a', 'b', 'c', 'd'],
keyExtractor: item => item,
onSelect: jest.fn(),
renderItem: () => null,
};
function runtest(name: string, props?: ObjectPartial<PickerProps<string>>) {
test(name, async () => {
const {getByTestId} = render(<Picker {...defaultProps} {...props} />);
const button = getByTestId('button');
fireEvent.press(button);
});
}
describe('Picker', () => {
runtest('default');
runtest('selected', {selected: 'a'});
runtest('selected invalid', {selected: 'invalid'});
runtest('selected titleExtractor', {
selected: 'a',
titleExtractor: item => item,
});
});
To mock the measure method from the component's reference in your tests, you can use the jest.fn function to create a mock function and assign it to the measure property of the reference object.
For example, you can do something like this:
import {createRef} from 'react'; const ref = createRef<View>(); ref.current = { measure: jest.fn(), }; // Now you can use the mock `measure` function in your tests
Keep in mind that the measure method is part of the View component's ref object, so you will need to create a mock View object with a measure property that is set to the mock function.
You can then use the mock measure function in your tests to control the behavior of the component when the measure method is called. For example, you can specify the return value of the measure method using the mockReturnValue method:
ref.current.measure.mockReturnValue({ x: 0, y: 0, width: 100, height: 100, });