Skip to content

Commit e9fc5a7

Browse files
author
Artur Yorsh
committed
feat(components): modal - improve with meaningful tests
1 parent 205eb6d commit e9fc5a7

File tree

2 files changed

+121
-137
lines changed

2 files changed

+121
-137
lines changed

src/components/ui/modal/modal.component.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,16 @@ import {
1212
ViewProps,
1313
ViewStyle,
1414
} from 'react-native';
15-
import {
16-
ModalPresentingConfig,
17-
ModalService,
18-
} from '@kitten/theme';
1915
import {
2016
MeasureElement,
2117
MeasuringElement,
22-
} from '../measure/measure.component';
23-
import {
2418
Frame,
2519
Point,
26-
} from '../measure/type';
20+
} from '../../devsupport';
21+
import {
22+
ModalPresentingConfig,
23+
ModalService,
24+
} from '../../theme';
2725

2826
export interface ModalProps extends ViewProps, ModalPresentingConfig {
2927
visible: boolean;
@@ -37,8 +35,6 @@ interface State {
3735
forceMeasure: boolean;
3836
}
3937

40-
const POINT_OUTSCREEN: Point = new Point(-999, -999);
41-
4238
/**
4339
* `Modal` component is a wrapper than presents content above an enclosing view.
4440
*
@@ -71,7 +67,7 @@ export class Modal extends React.PureComponent<ModalProps, State> {
7167
};
7268

7369
private modalId: string;
74-
private contentPosition: Point = POINT_OUTSCREEN;
70+
private contentPosition: Point = Point.outscreen();
7571

7672
private get contentFlexPosition(): FlexStyle {
7773
const derivedStyle: ViewStyle = StyleSheet.flatten(this.props.style || {});
Lines changed: 115 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,159 @@
1+
/**
2+
* @license
3+
* Copyright Akveo. All Rights Reserved.
4+
* Licensed under the MIT License. See License.txt in the project root for license information.
5+
*/
6+
17
import React from 'react';
28
import {
3-
View,
4-
Text,
59
Button,
6-
ViewProps,
10+
StyleSheet,
11+
Text,
12+
TouchableOpacity,
713
} from 'react-native';
814
import {
915
fireEvent,
10-
render,
11-
RenderAPI,
16+
render, RenderAPI,
17+
waitForElement,
1218
} from 'react-native-testing-library';
19+
import {
20+
light,
21+
mapping,
22+
} from '@eva-design/eva';
23+
import { ApplicationProvider } from '../../theme';
1324
import {
1425
Modal,
15-
baseModalTestId,
26+
ModalProps,
1627
} from './modal.component';
17-
import {
18-
ModalPanel,
19-
StyleType,
20-
} from '@kitten/theme';
21-
import { Frame } from '../measure/type';
2228

23-
const buttonShowModalTestId: string = '@button-show-modal';
24-
const buttonHideModalTestId: string = '@button-hide-modal';
25-
const stringify = (obj: Object): string => JSON.stringify(obj);
29+
describe('@modal: component checks', () => {
2630

27-
interface TestScreenState {
28-
modalVisible: boolean;
29-
}
31+
const TestModal = (props: Partial<ModalProps>) => {
32+
const [visible, setVisible] = React.useState(props.visible);
33+
const [text, setText] = React.useState('I love Babel');
3034

31-
interface TestScreenProps {
32-
modalStyle?: StyleType;
33-
}
35+
const toggleVisible = (): void => {
36+
setVisible(!visible);
37+
};
3438

35-
class TestScreen extends React.Component<TestScreenProps & ViewProps, TestScreenState> {
39+
const changeText = (): void => {
40+
setText('I love Jest');
41+
};
3642

37-
public state: TestScreenState = {
38-
modalVisible: false,
43+
return (
44+
<ApplicationProvider mapping={mapping} theme={light}>
45+
<React.Fragment>
46+
<Modal {...props} visible={visible}>
47+
<Text>{text}</Text>
48+
<Button testID='@modal/change-text-button' title='' onPress={changeText}/>
49+
</Modal>
50+
<Button testID='@modal/toggle-button' title='' onPress={toggleVisible}/>
51+
</React.Fragment>
52+
</ApplicationProvider>
53+
);
3954
};
4055

41-
private setModalVisible(modalVisible: boolean): void {
42-
this.setState({ modalVisible });
43-
}
56+
/*
57+
* In this test:
58+
* [0] for @modal/toggle-button,
59+
* [1] for backdrop
60+
* [2] for @modal/change-text-button
61+
*/
62+
const touchables = {
63+
findBackdropTouchable: (api: RenderAPI) => api.getAllByType(TouchableOpacity)[1],
64+
};
4465

45-
public render(): React.ReactNode {
46-
return (
47-
<View>
48-
<Button
49-
title='Show Modal'
50-
testID={buttonShowModalTestId}
51-
onPress={() => this.setModalVisible(true)}
52-
/>
53-
<Button
54-
title='Show Modal'
55-
testID={buttonHideModalTestId}
56-
onPress={() => this.setModalVisible(false)}
57-
/>
58-
<Modal
59-
style={this.props.modalStyle}
60-
visible={this.state.modalVisible}>
61-
<Text>Test Modal</Text>
62-
</Modal>
63-
</View>
66+
it('should render nothing when invisible', async () => {
67+
const component = render(
68+
<TestModal/>,
6469
);
65-
}
66-
}
6770

68-
describe('@modal component checks', () => {
71+
const text = component.queryByText('I love Babel');
72+
expect(text).toBeFalsy();
73+
});
6974

70-
it('* modal shows/hides properly', () => {
71-
const component: RenderAPI = render(<TestScreen/>);
75+
// TODO
76+
// We need somehow to mock `measureInWindow` so that it returns a mocked x, y, width and height to make it work.
77+
//
78+
// it('should render element passed to children when initially visible', async () => {
79+
// const component = render(
80+
// <TestModal visible={true}/>,
81+
// );
82+
//
83+
// component.getByText('I love Babel');
84+
// expect(text).toBeTruthy();
85+
// });
86+
87+
it('should render element passed to children when becomes visible', async () => {
88+
const component = render(
89+
<TestModal/>,
90+
);
7291

73-
fireEvent.press(component.getByTestId(buttonShowModalTestId));
74-
expect(component.getByType(Modal).props.visible).toBe(true);
92+
const toggleButton = component.getByTestId('@modal/toggle-button');
93+
fireEvent.press(toggleButton);
7594

76-
fireEvent.press(component.getByTestId(buttonHideModalTestId));
77-
expect(component.getByType(Modal).props.visible).toBe(false);
95+
const text = await waitForElement(() => component.queryByText('I love Babel'));
96+
expect(text).toBeTruthy();
7897
});
7998

80-
});
99+
it('should render nothing when becomes invisible', async () => {
100+
const component = render(
101+
<TestModal/>,
102+
);
81103

82-
describe('@modal panel checks', () => {
104+
const toggleButton = component.getByTestId('@modal/toggle-button');
105+
fireEvent.press(toggleButton);
83106

84-
const showModalButtonTestId: string = '@modal/button-show';
85-
const changeModalTextButtonTestId: string = '@modal/button-change-text';
86-
const modalTextTestId: string = '@modal/text';
107+
await waitForElement(() => fireEvent.press(toggleButton));
87108

88-
interface Props {
89-
textValue: string;
90-
redTextValue: string;
91-
}
109+
const text = await waitForElement(() => component.queryByText('I love Babel'));
110+
expect(text).toBeFalsy();
111+
});
92112

93-
interface State {
94-
modalVisible: boolean;
95-
isModalTextRed: boolean;
96-
}
113+
it('should be able to interact with content element passed to children', async () => {
114+
const component = render(
115+
<TestModal/>,
116+
);
97117

98-
class TestApplication extends React.Component<Props, State> {
118+
const toggleButton = component.getByTestId('@modal/toggle-button');
119+
fireEvent.press(toggleButton);
99120

100-
public state: State = {
101-
modalVisible: false,
102-
isModalTextRed: false,
103-
};
121+
const changeTextButton = await waitForElement(() => component.getByTestId('@modal/change-text-button'));
122+
fireEvent.press(changeTextButton);
104123

105-
private setModalVisible = (): void => {
106-
const modalVisible: boolean = !this.state.modalVisible;
124+
const text = await waitForElement(() => component.queryByText('I love Jest'));
107125

108-
this.setState({ modalVisible });
109-
};
126+
expect(text).toBeTruthy();
127+
});
110128

111-
private setModalTextRed = (): void => {
112-
const isModalTextRed: boolean = !this.state.isModalTextRed;
129+
it('should call onBackdropPress', async () => {
130+
const onBackdropPress = jest.fn();
113131

114-
this.setState({ isModalTextRed });
115-
};
132+
const component = render(
133+
<TestModal onBackdropPress={onBackdropPress}/>,
134+
);
116135

136+
const toggleButton = component.getByTestId('@modal/toggle-button');
137+
fireEvent.press(toggleButton);
117138

118-
public render(): React.ReactNode {
119-
const { textValue, redTextValue } = this.props;
120-
const { modalVisible, isModalTextRed } = this.state;
121-
const modalTextStyle: StyleType = isModalTextRed ? { color: 'red' } : null;
122-
const modalTextValue: string = isModalTextRed ? redTextValue : textValue;
123-
124-
return (
125-
<ModalPanel>
126-
<Button
127-
testID={showModalButtonTestId}
128-
title='Show Modal'
129-
onPress={this.setModalVisible}
130-
/>
131-
<Modal
132-
visible={modalVisible}
133-
onBackdropPress={this.setModalVisible}>
134-
<Text
135-
style={modalTextStyle}
136-
testID={modalTextTestId}>
137-
{modalTextValue}
138-
</Text>
139-
<Button
140-
testID={changeModalTextButtonTestId}
141-
title='Set Text Red'
142-
onPress={this.setModalTextRed}
143-
/>
144-
</Modal>
145-
</ModalPanel>
146-
);
147-
}
148-
}
149-
150-
it('* modal "team" updates content properly', () => {
151-
const textValue: string = 'Text Value';
152-
const redTextValue: string = 'Red Text Value';
153-
const expectedTextStyle: StyleType = { color: 'red' };
154-
const application: any = render(
155-
<TestApplication
156-
textValue={textValue}
157-
redTextValue={redTextValue}
158-
/>,
139+
140+
const backdrop = await waitForElement(() => touchables.findBackdropTouchable(component));
141+
fireEvent.press(backdrop);
142+
143+
expect(onBackdropPress).toBeCalled();
144+
});
145+
146+
it('should style backdrop with backdropStyle prop', async () => {
147+
const component = render(
148+
<TestModal backdropStyle={{ backgroundColor: 'red' }}/>,
159149
);
160150

161-
fireEvent.press(application.getByTestId(showModalButtonTestId));
162-
fireEvent.press(application.getAllByTestId(changeModalTextButtonTestId)[0]);
151+
const toggleButton = component.getByTestId('@modal/toggle-button');
152+
fireEvent.press(toggleButton);
163153

164-
const { children, style } = application.getAllByTestId(modalTextTestId)[0].props;
154+
const backdrop = await waitForElement(() => touchables.findBackdropTouchable(component));
165155

166-
expect(stringify(style)).toBe(stringify(expectedTextStyle));
167-
expect(children).toBe(redTextValue);
156+
expect(StyleSheet.flatten(backdrop.props.style).backgroundColor).toEqual('red');
168157
});
169158

170159
});
171-

0 commit comments

Comments
 (0)