* Copyright (c) 2025 Huawei Technologies Co., Ltd.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React, {createRef, useState} from 'react';
import {Pressable, StyleSheet, View} from 'react-native';
import {TestSuite} from '@rnoh/testerino';
import {TestCase} from '../components';
import {useEnvironment} from '../contexts';
export function PressableTest() {
const {
env: {driver},
} = useEnvironment();
return (
<TestSuite name="Pressable">
<TestCase.Automated
tags={['sequential']}
itShould="handle short presses"
initialState={{
onPressIn: false,
onPress: false,
ref: createRef<React.ElementRef<typeof Pressable>>(),
}}
arrange={({setState, state, done}) => {
return (
<Pressable
ref={state.ref}
onPressIn={() => setState(prev => ({...prev, onPressIn: true}))}
onPress={() => {
setState(prev => ({...prev, onPress: true}));
done();
}}>
<View style={styles.unpressed} />
</Pressable>
);
}}
act={async ({state}) => {
await driver?.click({ref: state.ref});
}}
assert={({expect, state}) => {
expect(state).to.include({
onPressIn: true,
onPress: true,
});
}}
/>
<TestCase.Automated
itShould="handle long press"
tags={['sequential']}
initialState={{
onLongPress: false,
ref: createRef<React.ElementRef<typeof Pressable>>(),
}}
arrange={({setState, state, done}) => {
return (
<Pressable
ref={state.ref}
onLongPress={() => {
setState(prev => ({...prev, onLongPress: true}));
// The long click in the test kit is very long.
// If we don't wait for it to finish and proceed, it will cause the next test to fail.
setTimeout(done, 1000);
}}>
<View style={styles.unpressed} />
</Pressable>
);
}}
act={async ({state}) => {
await driver?.longClick({ref: state.ref});
}}
assert={({expect, state}) => {
expect(state).to.include({
onLongPress: true,
});
}}
/>
<TestCase.Automated
itShould="handle pressing out"
tags={['sequential']}
initialState={{
onPressOut: false,
ref: createRef<React.ElementRef<typeof Pressable>>(),
}}
arrange={({setState, state, done}) => {
return (
<Pressable
ref={state.ref}
onPressOut={() => {
setState(prev => ({...prev, onPressOut: true}));
done();
}}>
<View style={styles.unpressed} />
</Pressable>
);
}}
act={async ({state}) => {
await driver?.click({ref: state.ref});
}}
assert={({expect, state}) => {
expect(state).to.include({
onPressOut: true,
});
}}
/>
<TestCase.Automated
itShould="inner view should not react to presses"
tags={['sequential']}
initialState={{
tested: false,
pressed: false,
ref: createRef<React.ElementRef<typeof Pressable>>(),
}}
arrange={({setState, state, done}) => {
return (
<Pressable
onPress={() => {
setState(prev => ({...prev, tested: true}));
done();
}}
style={styles.pressed}>
<Pressable
ref={state.ref}
onPress={() => {
setState(prev => ({...prev, pressed: true}));
done();
}}
style={styles.unpressed}
disabled
/>
</Pressable>
);
}}
act={async ({state}) => {
await driver?.click({ref: state.ref});
}}
assert={({expect, state}) => {
expect(state).to.include({
tested: true,
pressed: false,
});
}}
/>
<TestCase.Example
itShould="change color to blue on hover"
skip={{android: false, harmony: true}} // https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/417
>
<HoverView />
</TestCase.Example>
<TestCase.Automated
itShould="pass when blue background is pressed"
tags={['sequential']}
initialState={{
pressed: false,
ref: createRef<View>(),
}}
arrange={({setState, state, done}) => (
<View
style={{
backgroundColor: 'blue',
alignSelf: 'center',
position: 'relative',
}}>
<Pressable
hitSlop={{top: 48, left: 48, bottom: 48, right: 48}}
style={{
width: 48,
height: 48,
backgroundColor: 'green',
margin: 48,
}}
onPress={() => {
setState(prev => ({...prev, pressed: true}));
done();
}}
/>
<View
ref={state.ref}
style={{
width: 48,
height: 48,
backgroundColor: 'red',
position: 'absolute',
top: 48,
left: 48,
}}
onTouchEnd={e => {
e.stopPropagation();
}}
/>
</View>
)}
act={async ({state}) => {
await driver?.click({ref: state.ref, offset: {x: 48, y: 48}});
}}
assert={({expect, state}) => {
expect(state.pressed).to.be.true;
}}
/>
<TestCase.Example
itShould="change color upon mouse click"
skip={{android: true, harmony: false}}>
<PressView />
</TestCase.Example>
</TestSuite>
);
}
const HoverView = () => {
const [hovered, setHovered] = useState(false);
return (
<Pressable
onHoverIn={() => {
setHovered(true);
console.log('onHoverIn');
}}
onHoverOut={() => {
setHovered(false);
console.log('onHoverOut');
}}
style={{...styles.unpressed, backgroundColor: hovered ? 'blue' : 'red'}}
/>
);
};
let pressed = true;
const PressView = () => {
const [count, setCount] = useState(0);
return (
<Pressable
onPress={() => {
pressed = !pressed;
setCount(count + 1);
}}
style={{...styles.unpressed, backgroundColor: pressed ? 'blue' : 'red'}}
/>
);
};
const styles = StyleSheet.create({
unpressed: {
width: 50,
height: 50,
backgroundColor: 'red',
},
pressed: {
width: 100,
height: 100,
backgroundColor: 'blue',
justifyContent: 'center',
alignItems: 'center',
},
longPressed: {
width: 300,
height: 100,
backgroundColor: 'green',
},
text: {
height: 20,
width: 200,
fontSize: 14,
},
});