Skip to content

Commit

Permalink
Added comments & screenshots
Browse files Browse the repository at this point in the history
  • Loading branch information
support-fetchsky committed Nov 8, 2020
1 parent caea2c6 commit 5e78818
Show file tree
Hide file tree
Showing 32 changed files with 82 additions and 21 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# testing-app
# Testing App

Demo for react native testing library

Please don't judge me for coding practices using this code base. I am better than this; might be.
23 changes: 23 additions & 0 deletions app/screens/LoginScreen/EmailPasswordForm/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,53 @@ import {

describe('<EmailPasswordForm />', () => {
it('Expect to show password required', async () => {
// Dummy email value
const email = '[email protected]';

// Grabbing our parent component
const { getByTestId } = render(
<EmailPasswordForm
onSubmit={() => null}
onForgotPasswordPress={() => null}
/>,
);

// Grabbing our input & button components
const emailInput = getByTestId(TEST_ID_EMAIL_INPUT);
const button = getByTestId(TEST_ID_SUBMIT_BUTTON);

/**
* We are changing text in inputs here; that requires a state change
* Since setState is async in react we have to execute this tests
* in async way. RNTL give waitFor API for this.
*/
await waitFor(() => {
fireEvent.changeText(emailInput, email);
// Just making sure that value is updated in input
expect(emailInput.props.value).toBe(email);
fireEvent.press(button);

// We have passwordInput_ERROR component that only renders when error is there
expect(getByTestId('passwordInput_ERROR')).toBeDefined();
});
});

it('Expect to call handle submit with email & password', async () => {
// Dummy inputs
const email = '[email protected]';
const password = 'qwerty1234';

// Expected output
const expectedOutput = {
email,
password,
};
let output = {};

// Mock onSubmit method that we are expecting will be executed
const onSubmit = jest.fn((data) => (output = data));

// Rendering our component & grabbing required nodes.
const { getByTestId } = render(
<EmailPasswordForm
onSubmit={onSubmit}
Expand All @@ -51,13 +69,18 @@ describe('<EmailPasswordForm />', () => {
const emailInput = getByTestId(TEST_ID_EMAIL_INPUT);
const passwordInput = getByTestId(TEST_ID_PASSWORD_INPUT);

// Testing behaviors
await waitFor(() => {
fireEvent.changeText(emailInput, email);
expect(emailInput.props.value).toBe(email);

fireEvent.changeText(passwordInput, password);
expect(passwordInput.props.value).toBe(password);

/**
* Here we are asserting that onSubmit is not just called
* but it is called with expected output.
*/
fireEvent.press(button);
expect(onSubmit).toHaveBeenCalledTimes(1);
expect(output).toEqual(expectedOutput);
Expand Down
27 changes: 21 additions & 6 deletions app/screens/LoginScreen/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,32 @@ import {
} from '../constants';

describe('Login Screen', () => {
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});

it('Expect to save token in AsyncStorage & navigate to home screen on successful login', async () => {
// Setting up fetch mock before execution of any test
beforeAll(() => {
const endPoint = `${API_URL}${LOGIN_ENDPOINT}`;
fetchMock.post(endPoint, {
status: 200,
body: JSON.stringify(LOGIN_EXPECTED_RESPONSE),
});
});

// Testing complete flow
it('Expect to save token in AsyncStorage & navigate to home screen on successful login', async () => {
// Mocking navigate method
const navigate = jest.fn();
const endPoint = `${API_URL}${LOGIN_ENDPOINT}`;

// Dummy data to supply to form
const email = '[email protected]';
const password = 'password';

// Getting element
const screen = render(<LoginScreen navigation={{ navigate }} />);
const emailInput = screen.getByTestId(TEST_ID_EMAIL_INPUT);
const passwordInput = screen.getByTestId(TEST_ID_PASSWORD_INPUT);
const button = screen.getByTestId(TEST_ID_SUBMIT_BUTTON);

// Formik requires all state changes to be wrapper in async method.
await waitFor(() => {
fireEvent.changeText(emailInput, email);
fireEvent.changeText(passwordInput, password);
Expand All @@ -51,16 +55,27 @@ describe('Login Screen', () => {
fireEvent.press(button);
});

// Asserting that API has been called
expect(fetchMock).toHaveBeenCalledWith(endPoint, {
body: `{"email":"${email}","password":"${password}"}`,
headers: { 'Content-Type': 'application/json' },
method: 'POST',
});

// Asserting screen navigation
expect(navigate).toHaveBeenCalledTimes(1);
expect(navigate).toHaveBeenCalledWith(HOME, {});

// Asserting token storage in local storage.
expect(AsyncStorage.setItem).toHaveBeenCalledWith(
AUTH_TOKEN_KEY,
LOGIN_EXPECTED_RESPONSE.data.tokens.jwtToken,
);
});

// Cleaning up fetch mock
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
});
});
4 changes: 2 additions & 2 deletions app/theme/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, { useEffect, useRef } from 'react';
import { Animated } from 'react-native';

import Text from 'theme/Text';
import TouchFeedback from 'theme/TouchFeedback';
import TouchFeedback, { TouchFeedbackProps } from 'theme/TouchFeedback';

import style from './style';

Expand All @@ -22,7 +22,7 @@ const typeForeground = {
tertiary: style.tertiaryForeground,
};

interface ButtonProps {
interface ButtonProps extends TouchFeedbackProps {
onPress: (...args: any[]) => any;
label: string | React.ReactNode;
mini?: boolean;
Expand Down
16 changes: 16 additions & 0 deletions app/theme/Button/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,31 @@ import { fireEvent } from '@testing-library/react-native';
import { render } from 'utils/testWrapper';
import Button from '../index';

// Describing a test suite
describe('<Button />', () => {
// Describing our test
it('Calls onPress', async () => {
// Mocking onPress method so we can check if its called or not
const onPress = jest.fn();

// test id to be applied on our button component
const testID = 'button';

// Rendering Button component using react-native-test-renderer.
const { getByTestId } = await render(
<Button testID={testID} onPress={onPress} label="Button" />,
);

// Grabbing our button component to perform actions on it.
const button = getByTestId(testID);

/**
* RNTL gives us API to fire events on node
* Here we are firing on press event
*/
fireEvent.press(button);

// Asserting if given mock method is called or not.
expect(onPress).toHaveBeenCalledTimes(1);
});
});
4 changes: 2 additions & 2 deletions app/theme/TouchFeedback/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const AnimatedNative = Animated.createAnimatedComponent(
);
const RIPPLE = Platform.OS === 'android' && Platform.Version >= 21;

type TouchFeedbackProps = {
export interface TouchFeedbackProps {
style?: number | any[];
ripple?: boolean;
animated?: boolean;
onPress: (...args: any[]) => any;
testID?: string;
};
}

const TouchFeedback: React.SFC<TouchFeedbackProps> = ({
children,
Expand Down
5 changes: 0 additions & 5 deletions app/utils/delay.ts

This file was deleted.

Loading

0 comments on commit 5e78818

Please sign in to comment.