Skip to content

POC Export of testkits #1925

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

Merged
merged 20 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,6 @@ index.ios.js
*.ts
*.tsx
# include the .d.ts files
!*.d.ts
!*.d.ts

!testkit/*.ts
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/react-native": "^7.2.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/jest": "^27.4.1",
"@types/lodash": "^4.0.0",
"@types/prop-types": "^15.5.3",
"@types/react-native": "0.66.4",
Expand Down
2 changes: 1 addition & 1 deletion src/.babelrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[
"module-resolver",
{
"root": ["./src"],
"root": ["./src", "./testkit"],
"alias": {
"commons": "./src/commons",
"helpers": "./src/helpers",
Expand Down
13 changes: 5 additions & 8 deletions src/components/button/Button.driver.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import _ from 'lodash';
import {fireEvent, RenderAPI} from '@testing-library/react-native';
import {ReactTestInstance} from 'react-test-renderer';
import TextDriver from '../text/Text.driver';
import ImageDriver from '../image/Image.driver';
import _ from 'lodash';
import {TextDriver} from '../text/Text.driver';
import {ImageDriver} from '../image/Image.driver';


const ButtonDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI, testID: string}) => {
export const ButtonDriver = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI; testID: string}) => {
const button: ReactTestInstance | null = await wrapperComponent.queryByTestId(testID);
const label = await TextDriver({wrapperComponent, testID: `${testID}.label`});
const icon = await ImageDriver({wrapperComponent, testID: `${testID}.icon`});
Expand All @@ -17,7 +16,7 @@ const ButtonDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent
if (button) {
fireEvent.press(button);
} else {
console.warn(`ButtonDriverFactory: cannot click because testID:${testID} were not found`);
console.warn(`ButtonDriver: cannot click because testID:${testID} were not found`);
}
},
// label
Expand All @@ -29,5 +28,3 @@ const ButtonDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent
isIconExists: () => icon.exists()
};
};

export default ButtonDriverFactory;
36 changes: 18 additions & 18 deletions src/components/button/__tests__/index.driver.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, {useState} from 'react';
import {render, waitFor} from '@testing-library/react-native';
import Button from '../index';
import View from '../../view';
import ButtonTestKit from '../Button.driver';
import Text from '../../text';
import TextTestKit from '../../text/Text.driver';
import Button from '../index';
import {ButtonDriver} from '../Button.driver';
import {TextDriver} from '../../text/Text.driver';

const BUTTON_ID = 'button_test_id';
const CHILDREN_TEXT_ID = 'children_test_id';
Expand All @@ -14,22 +14,22 @@ const CHILDREN_TEXT = 'custom button text';
describe.skip('Button', () => {
it('should render a button', async () => {
const wrapperComponent = renderWrapperScreenWithButton({});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.exists()).toBeTruthy();
});

describe('custom button', () => {
it('should render a custom button', async () => {
const wrapperComponent = renderWrapperScreenWithCustomButton({});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.exists()).toBeTruthy();
});

it('should render the children with correct text', async () => {
const wrapperComponent = renderWrapperScreenWithCustomButton({});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.exists()).toBeTruthy();
const childrenTextDriver = await TextTestKit({wrapperComponent, testID: CHILDREN_TEXT_ID});
const childrenTextDriver = await TextDriver({wrapperComponent, testID: CHILDREN_TEXT_ID});
expect(childrenTextDriver.getTextContent()).toEqual(CHILDREN_TEXT);
});
});
Expand All @@ -41,14 +41,14 @@ describe.skip('Button', () => {

it('should trigger onPress callback', async () => {
const wrapperComponent = renderWrapperScreenWithButton({onPress: onPressCallback});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
buttonDriver.click();
await waitFor(() => expect(onPressCallback).toHaveBeenCalledTimes(1));
});

it('should not trigger onPress callback if button disabled', async () => {
const wrapperComponent = renderWrapperScreenWithButton({onPress: onPressCallback, disabled: true});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
buttonDriver.click();
await waitFor(() => expect(onPressCallback).toHaveBeenCalledTimes(0));
});
Expand All @@ -58,35 +58,35 @@ describe.skip('Button', () => {
const LABEL = 'mock label';
it('should render a button with correct content', async () => {
const wrapperComponent = renderWrapperScreenWithButton({label: LABEL});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.getLabelContent()).toEqual(LABEL);
});

xit('should render a button with correct label content. ', async () => {
// todo import @testing-library/jest-native(/extend-expect)
const wrapperComponent = renderWrapperScreenWithButton({label: LABEL});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.getLabelRootElement()).toHaveTextContent(LABEL);
});

it('should render a button without label. ', async () => {
const wrapperComponent = renderWrapperScreenWithButton({});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.isLabelExists()).toBeFalsy();
});
});

describe('icon', () => {
it('should render a button without an icon. ', async () => {
const wrapperComponent = renderWrapperScreenWithButton({});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.isIconExists()).toBeFalsy();
});

it('should render a button with icon. ', async () => {
const ICON = 12;
const wrapperComponent = renderWrapperScreenWithButton({iconSource: ICON});
const buttonDriver = await ButtonTestKit({wrapperComponent, testID: BUTTON_ID});
const buttonDriver = await ButtonDriver({wrapperComponent, testID: BUTTON_ID});
expect(buttonDriver.isIconExists()).toBeTruthy();
});
});
Expand All @@ -95,10 +95,10 @@ describe.skip('Button', () => {
//todo take it out of this file. to the demo screens maybe
it('should change text values according to state changes from buttons pressing', async () => {
const wrapperComponent = renderMoreComplicatedScreen();
const text1Driver = await TextTestKit({wrapperComponent, testID: `text_1`});
const text2Driver = await TextTestKit({wrapperComponent, testID: `text_2`});
const button1Driver = await ButtonTestKit({wrapperComponent, testID: `${BUTTON_ID}1`});
const button2Driver = await ButtonTestKit({wrapperComponent, testID: `${BUTTON_ID}2`});
const text1Driver = await TextDriver({wrapperComponent, testID: `text_1`});
const text2Driver = await TextDriver({wrapperComponent, testID: `text_2`});
const button1Driver = await ButtonDriver({wrapperComponent, testID: `${BUTTON_ID}1`});
const button2Driver = await ButtonDriver({wrapperComponent, testID: `${BUTTON_ID}2`});
expect(text1Driver.getTextContent()).toBe('button 1 pressed 0 times');
expect(text2Driver.getTextContent()).toBe('button 2 pressed 0 times');
button1Driver.click();
Expand Down
6 changes: 1 addition & 5 deletions src/components/image/Image.driver.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import {RenderAPI} from '@testing-library/react-native';
import {ReactTestInstance} from 'react-test-renderer';


const ImageDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI, testID: string}) => {

export const ImageDriver = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI; testID: string}) => {
const text: ReactTestInstance | null = await wrapperComponent.queryByTestId(testID);
return {
//todo more research on this component
Expand All @@ -12,5 +10,3 @@ const ImageDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent:
// getImageContent: () => text.props.value
};
};

export default ImageDriverFactory;
113 changes: 113 additions & 0 deletions src/components/switch/__tests__/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from 'react';
import {render} from '@testing-library/react-native';
import Switch, {SwitchProps} from '../index';
import {SwitchDriver} from '../switch.driver';
import View from '../../view';

describe('Switch', () => {

const renderSwitch = (testID: string, props: Partial<SwitchProps>) => {
const defaultProps: SwitchProps = {
testID,
value: false,
onValueChange: jest.fn(),
disabled: false
};
const component = render(<View><Switch {...defaultProps} {...props}/></View>);
const driver = SwitchDriver({
wrappedComponent: component,
testID
});

return {driver};
};


it('Should fire onChange event', () => {
const testId = 'switch-comp';
const onChange = jest.fn();
const {driver} = renderSwitch(testId, {onValueChange: onChange});
driver.press();
expect(onChange).toHaveBeenCalled();
});

it('Should fire onChange event with false value when toggling off', () => {
const testId = 'switch-comp';
const onChange = jest.fn();
const {driver} = renderSwitch(testId, {onValueChange: onChange, value: true});
driver.press();
expect(onChange).toHaveBeenCalledWith(false);
});
it('Should fire onChange event with true value when toggling on', () => {
const testId = 'switch-comp';
const onChange = jest.fn();
const {driver} = renderSwitch(testId, {onValueChange: onChange, value: false});
driver.press();
expect(onChange).toHaveBeenCalledWith(true);
});

it('Should not fire onChange when disabled', () => {
const testId = 'switch-comp';
const onValueChange = jest.fn();
const {driver} = renderSwitch(testId, {disabled: true, onValueChange});

driver.press();
expect(onValueChange).not.toHaveBeenCalled();
});

it('Accessibility value should be true when checked', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: true});

expect(driver.getAccessibilityValue()).toBe(true);
});

it('Accessibility value should be false when not checked', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: false});

expect(driver.isChecked()).toBe(false);
});

it('Accessibility value should be checked when checked', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: true});

expect(driver.isChecked()).toBe(true);
});

it('Accessibility value should be false when not checked', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: false});

expect(driver.getAccessibilityValue()).toBe(false);
});

it('Should be disabled', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {disabled: true});

expect(driver.isDisabled()).toBe(true);
});

it('Should be disabled', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {disabled: false});

expect(driver.isDisabled()).toBe(false);
});

it('Should pass correct color when on', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: true, onColor: 'red'});

expect(driver.getColor()).toBe('red');
});

it('Should pass correct color when off', () => {
const testId = 'switch-comp';
const {driver} = renderSwitch(testId, {value: false, offColor: 'red'});

expect(driver.getColor()).toBe('red');
});
});
13 changes: 8 additions & 5 deletions src/components/switch/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export type SwitchProps = {
*/
thumbStyle?: StyleProp<ViewStyle>;
style?: StyleProp<ViewStyle>;
testID?: string;
testID?: string;
}

/**
Expand All @@ -65,13 +65,13 @@ export type SwitchProps = {
*/
class Switch extends Component<SwitchProps> {
static displayName = 'Switch';

state = {
thumbPosition: new Animated.Value(this.props.value ? 1 : 0)
};

styles = createStyles(this.props);

componentDidUpdate(prevProps: SwitchProps) {
const {value} = this.props;
if (prevProps.value !== value) {
Expand All @@ -82,11 +82,14 @@ class Switch extends Component<SwitchProps> {
getAccessibilityProps() {
const {disabled, value} = this.props;


return {
accessible: true,
accessibilityRole: 'switch',
accessibilityStates: disabled ? ['disabled'] : value ? ['checked'] : ['unchecked'],
accessibilityState: {
Copy link
Author

Choose a reason for hiding this comment

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

Changed accessibilityState to follow Accessibility docs of react-native
https://reactnative.dev/docs/accessibility#accessibilitystate

Copy link
Author

Choose a reason for hiding this comment

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

Double checked with building & running the app locally, I see no errors regarding this change so I guess it's ok

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍
I tried testing with Accessibility Inspector locally but it doesn't show the state value so I'll verify again on real device after we merge

disabled,
checked: value ? 'checked' : 'unchecked'
},
accessibilityValue: {text: value ? '1' : '0'}
};
}
Expand Down
15 changes: 15 additions & 0 deletions src/components/switch/switch.driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {RenderAPI, fireEvent} from '@testing-library/react-native';

export const SwitchDriver = ({wrappedComponent, testID}: {wrappedComponent: RenderAPI; testID: string}) => {
const switchComp = wrappedComponent.getByTestId(testID);

return {
exists: () => !!switchComp,
getRootElement: () => switchComp,
press: () => fireEvent(switchComp, 'press'),
getAccessibilityValue: () => switchComp.props.accessibilityValue.text === '1',
isDisabled: () => switchComp.props.accessibilityState.disabled === true,
isChecked: () => switchComp.props.accessibilityState.checked === 'checked',
getColor: () => switchComp.props.style.backgroundColor
};
};
8 changes: 2 additions & 6 deletions src/components/text/Text.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import {fireEvent, RenderAPI} from '@testing-library/react-native';
import {ReactTestInstance} from 'react-test-renderer';
import _ from 'lodash';

const TextDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI, testID: string}) => {

export const TextDriver = async ({wrapperComponent, testID}: {wrapperComponent: RenderAPI; testID: string}) => {
const text: ReactTestInstance | null = await wrapperComponent.queryByTestId(testID);
return {
exists: () => !!text,
Expand All @@ -21,11 +20,8 @@ const TextDriverFactory = async ({wrapperComponent, testID}: {wrapperComponent:
if (text) {
fireEvent.press(text);
} else {
console.warn(`TextDriverFactory: cannot click because testID:${testID} were not found`);
console.warn(`TextDriver: cannot click because testID:${testID} were not found`);
}
}

};
};

export default TextDriverFactory;
Loading