Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new driver for hint testing #2898

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions jestSetup/jest-setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {NativeModules, AccessibilityInfo, Animated} from 'react-native';
import React from 'react';
// ========= Mock Object.defineProperty to always allow overriding =========
const originalDefineProperty = Object.defineProperty;
Object.defineProperty = (obj, prop, desc) => {
Expand All @@ -15,6 +16,17 @@ Object.defineProperties = (obj, props) => {
return obj;
};
// =========================================================================
const OriginalReactComponent = React.Component;
Object.defineProperty(React, 'Component', {
value: class MockedReactComponent extends OriginalReactComponent {
M-i-k-e-l marked this conversation as resolved.
Show resolved Hide resolved
componentDidMount() {
super.componentDidMount?.();
this.props.onLayout?.({nativeEvent: {layout: {}}});
console.log(`MOCK MOCK MOCK! this.measureInWindow:`, this.measureInWindow);
this.measureInWindow?.({nativeEvent: {layout: {}}});
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
}
}
});

global._UILIB_TESTING = true;

Expand Down
62 changes: 62 additions & 0 deletions src/components/hint/Hint.driver.new.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {HintProps} from './index';
import {TouchableOpacityProps} from '../touchableOpacity';
import {
useComponentDriver,
ComponentProps,
usePressableDriver,
ViewDriver,
ImageDriver,
ModalDriver
} from '../../testkit';
import {ViewStyle, StyleSheet} from 'react-native';

export const HintDriver = (props: ComponentProps) => {
const driver = usePressableDriver<HintProps>(useComponentDriver(props));

const modalDriver = ModalDriver({
renderTree: props.renderTree,
testID: `${props.testID}.modal`
});

const contentDriver = ViewDriver({
renderTree: props.renderTree,
testID: `${props.testID}.message`
});

const hintTouchableDriver = usePressableDriver<TouchableOpacityProps>(useComponentDriver({
renderTree: props.renderTree,
testID: `${props.testID}`
}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just the Hint itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it is, do you think that the naming isn't good ?
It's the Touchable part of the component with out the View wrapper

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it should be exported as press and probably something like pressOnBackground: overlayDriver.press for the overlay (same as in Modal)


const overlayTouchableDriver = usePressableDriver<TouchableOpacityProps>(useComponentDriver({
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
renderTree: props.renderTree,
testID: `${props.testID}.overlay`
}));

const iconDriver = ImageDriver({
renderTree: props.renderTree,
testID: `${props.testID}.icon`
});

const getModal = () => {
return modalDriver;
};
M-i-k-e-l marked this conversation as resolved.
Show resolved Hide resolved

const getIcon = () => {
return iconDriver;
};

const getContentStyle = (): ViewStyle => {
return StyleSheet.flatten(contentDriver.getProps().style) as ViewStyle;
};
adids1221 marked this conversation as resolved.
Show resolved Hide resolved

const getHintTouchable = () => {
return hintTouchableDriver;
};

const getOverlayTouchable = () => {
return overlayTouchableDriver;
};

return {getContentStyle, getModal, getIcon, getHintTouchable, getOverlayTouchable, ...driver};
};
133 changes: 96 additions & 37 deletions src/components/hint/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,128 @@
import React from 'react';
import {Hint} from 'react-native-ui-lib';
import {Colors} from '../../../style';
import {findStyle} from '../../../uilib-test-renderer';
import {HintDriver} from '../Hint.driver';
import React, {useRef} from 'react';
import {waitFor, render} from '@testing-library/react-native';
import {Hint, Colors, Button} from 'react-native-ui-lib';
import {HintDriver} from '../Hint.driver.new';

const TEST_ID = 'Hint';

const settingsIcon = require('../../../assets/icons/check.png');

const HintTestComponent = ({
showHint,
color
color,
onPress,
onBackgroundPress,
useTargetFrame = true,
useModal,
useIcon
}: {
showHint: boolean;
color?: string;
onPress?: Function;
onBackgroundPress?: Function;
useTargetFrame?: boolean;
useModal?: boolean;
useIcon?: boolean;
}) => {
const ref = useRef();
return (
<Hint
visible={showHint}
message={'Hint message to hint things'}
position={Hint.positions.TOP}
useSideTip
key={'1'}
targetFrame={{x: 1, y: 1, height: 1, width: 1}}
onBackgroundPress={() => {}}
targetFrame={useTargetFrame && {x: 1, y: 1, height: 1, width: 1}}
onBackgroundPress={onBackgroundPress}
onPress={onPress}
color={color}
removePaddings
enableShadow
testID={'Hint'}
/>
testID={TEST_ID}
useModal={useModal}
icon={useIcon ? settingsIcon : undefined}
ref={ref}
>
<Button round $backgroundDefault/>
</Hint>
);
};

//TODO: Add test for onPress functionality
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
describe('Hint Screen component test', () => {
afterEach(() => {
HintDriver.clear();
});
describe('Test Hint style:', () => {
it('Test Hint component background color', async () => {
const expectedColor = Colors.$backgroundPrimaryHeavy;
const renderTree = render(<HintTestComponent showHint/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(await driver.getElement()).toBeTruthy();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'de change this to something else, what are we really testing here?


it('Test Hint component background color', async () => {
const expectedColor = Colors.$backgroundPrimaryHeavy;
const component = <HintTestComponent showHint/>;
let contentColor = await driver.getContentStyle().backgroundColor;
expect(contentColor).toBe(expectedColor);

const driver = new HintDriver({component, testID: 'Hint'});
expect(await driver.getElement()).toBeTruthy();
const whiteHintRenderTree = render(<HintTestComponent showHint color={Colors.white}/>);
const whiteHintDriver = HintDriver({renderTree: whiteHintRenderTree, testID: TEST_ID});

const hintContent = await driver.getHintContent();
let color = findStyle('backgroundColor', hintContent);

expect(color).toBe(expectedColor);
expect(hintContent).toBeTruthy();
contentColor = await whiteHintDriver.getContentStyle().backgroundColor;
expect(contentColor).toBe(Colors.white);
});
});

const WhiteHint = <HintTestComponent showHint color={Colors.white}/>;
const WhiteHintDriver = new HintDriver({component: WhiteHint, testID: 'Hint'});
describe('Test Hint icon', () => {
it('Hint should include icon', async () => {
const renderTree = render(<HintTestComponent showHint useIcon/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(driver.getIcon().exists()).toBeTruthy();
});

const WhiteHintContent = await WhiteHintDriver.getHintContent();
color = findStyle('backgroundColor', WhiteHintContent);
expect(color).toBe(Colors.white);
it('Hint shouldn\'t include icon', async () => {
const renderTree = render(<HintTestComponent showHint/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(driver.getIcon().exists()).toBeFalsy();
});
});

it('Test Hint modal is not visible when showHint is false', async () => {
const component = <HintTestComponent showHint={false}/>;
const driver = new HintDriver({component, testID: 'Hint'});
expect(await driver.getHintModal()).toBeFalsy();
describe('Test Hint modal visibility:', () => {
M-i-k-e-l marked this conversation as resolved.
Show resolved Hide resolved
it('Test Hint modal is not visible when showHint is false', async () => {
const renderTree = render(<HintTestComponent showHint={false}/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(await driver.getModal().exists()).toBeFalsy();
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
});

it('Test Hint modal is visible when showHint is true', async () => {
const renderTree = render(<HintTestComponent showHint onBackgroundPress={() => {}}/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(await driver.exists()).toBeTruthy();
expect(await driver.getModal().exists()).toBeTruthy();
});
});

it('Test Hint modal is visible when showHint is true', async () => {
const component = <HintTestComponent showHint/>;
const driver = new HintDriver({component, testID: 'Hint'});
expect(await driver.getElement()).toBeTruthy();
expect(await driver.getHintModal()).toBeTruthy();
describe('Test Hint onPress & onBackgroundPress', () => {
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
//TODO - add test for onBackgroundTest, should mock measureInWindow functionally to test it
M-i-k-e-l marked this conversation as resolved.
Show resolved Hide resolved
let onPressCallback: jest.Mock;
beforeEach(() => (onPressCallback = jest.fn()));
afterEach(() => onPressCallback.mockClear());

it('should trigger onPress callback', async () => {
const renderTree = render(<HintTestComponent showHint onPress={onPressCallback}/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
driver.getHintTouchable().press();
await waitFor(() => expect(onPressCallback).toHaveBeenCalledTimes(1));
});

it('should not trigger onPress callback when onPress isn\'t passed', async () => {
const renderTree = render(<HintTestComponent showHint/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
driver.getHintTouchable().press();
await waitFor(() => expect(onPressCallback).toHaveBeenCalledTimes(0));
});

it('should not create touchable overlay driver when onBackgroundPress isn\'t passed', async () => {
const renderTree = render(<HintTestComponent showHint/>);
const driver = HintDriver({renderTree, testID: TEST_ID});
expect(driver.getOverlayTouchable().exists()).toBeFalsy();
});
});

describe('Test Hint tip', () => {});
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
});
20 changes: 13 additions & 7 deletions src/components/hint/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ class Hint extends Component<HintProps, HintState> {
renderOverlay() {
const {targetLayoutInWindow} = this.state;
const {onBackgroundPress, backdropColor, testID} = this.props;
console.log(`inside renderOverlay, targetLayoutInWindow:`, targetLayoutInWindow);
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
if (targetLayoutInWindow) {
const containerPosition = this.getContainerPosition() as {top: number; left: number};
return (
Expand All @@ -425,10 +426,13 @@ class Hint extends Component<HintProps, HintState> {
}
]}
pointerEvents="box-none"
testID={`${testID}.overlay`}
>
{onBackgroundPress && (
<TouchableWithoutFeedback style={StyleSheet.absoluteFillObject} onPress={onBackgroundPress}>
<TouchableWithoutFeedback
style={StyleSheet.absoluteFillObject}
onPress={onBackgroundPress}
testID={`${testID}.overlay`}
>
<View flex/>
</TouchableWithoutFeedback>
)}
Expand Down Expand Up @@ -479,8 +483,12 @@ class Hint extends Component<HintProps, HintState> {
ref={this.hintRef}
>
{customContent}
{!customContent && icon && <Image source={icon} style={[styles.icon, iconStyle]}/>}
{!customContent && <Text recorderTag={'unmask'} style={[styles.hintMessage, messageStyle]}>{message}</Text>}
{!customContent && icon && <Image source={icon} style={[styles.icon, iconStyle]} testID={`${testID}.icon`}/>}
{!customContent && (
<Text recorderTag={'unmask'} style={[styles.hintMessage, messageStyle]}>
adids1221 marked this conversation as resolved.
Show resolved Hide resolved
{message}
</Text>
)}
</View>
);
}
Expand All @@ -501,9 +509,8 @@ class Hint extends Component<HintProps, HintState> {
this.getHintAnimatedStyle()
]}
pointerEvents="box-none"
testID={testID}
>
<TouchableOpacity activeOpacity={opacity} onPress={onPress}>
<TouchableOpacity activeOpacity={opacity} onPress={onPress} testID={`${testID}`}>
{this.renderContent()}
</TouchableOpacity>
{this.renderHintTip()}
Expand Down Expand Up @@ -552,7 +559,6 @@ class Hint extends Component<HintProps, HintState> {

renderChildren() {
const {targetFrame} = this.props;

if (!targetFrame && isValidElement(this.props.children)) {
return React.cloneElement<any>(this.props.children, {
key: 'clone',
Expand Down
1 change: 1 addition & 0 deletions src/testkit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {TextFieldDriver} from '../components/textField/TextField.driver.new';
export {ViewDriver} from '../components/view/View.driver.new';
export {ModalDriver} from '../components/modal/Modal.driver.new';
export {DialogDriver} from '../incubator/Dialog/Dialog.driver.new';
export {HintDriver} from '../components/hint/Hint.driver.new';
export {ButtonDriver} from '../components/button/Button.driver.new';
export {ImageDriver} from '../components/image/Image.driver.new';
export {SwitchDriver} from '../components/switch/switch.driver';