Skip to content

Commit 72d5999

Browse files
authored
Merge pull request #107 from ctc-casa-ios/feature/106
jest configurations
2 parents db1ee75 + 41624f2 commit 72d5999

18 files changed

Lines changed: 2444 additions & 184 deletions

.eslintrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,11 @@ module.exports = {
33
rules: {
44
'function-paren-newline': 'off',
55
},
6+
overrides: [
7+
{
8+
// Test files only
9+
files: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'],
10+
extends: ['plugin:testing-library/react'],
11+
},
12+
],
613
};

App.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import { Provider } from 'react-redux';
3+
import { Provider as AuthProvider } from 'src/components/context/AuthContext';
4+
5+
import Main from './src/index';
6+
import { store } from './src/redux/store';
7+
8+
function App() {
9+
return (
10+
<AuthProvider>
11+
<Provider store={store}>
12+
<Main />
13+
</Provider>
14+
</AuthProvider>
15+
);
16+
}
17+
18+
//registerRootComponent(App);
19+
export default App;

jestSetupFile.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
jest.mock('@react-native-async-storage/async-storage', () =>
2+
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
3+
);
4+
5+
jest.mock('@expo/vector-icons', () => ({
6+
MaterialCommunityIcons: '',
7+
}));

package-lock.json

Lines changed: 2184 additions & 99 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,17 @@
1212
"web": "expo start --web"
1313
},
1414
"jest": {
15-
"preset": "react-native"
15+
"preset": "react-native",
16+
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"],
17+
"setupFiles": [
18+
"./jestSetupFile.js"
19+
],
20+
"transform": {
21+
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
22+
},
23+
"transformIgnorePatterns": [
24+
"./node_modules/(?!(jest-)?react-native|@react-native|@react-native-community|@react-navigation|react-redux|@reduxjs/toolkit|@react-native-async-storage/async-storage|@react-native-community/datetimepicker|@react-navigation/bottom-tabs|@react-navigation/native-stack|react-native-elements|react-native-reanimated|react-native-safe-area-context|expo-status-bar|expo|@expo|axios|twrnc)"
25+
]
1626
},
1727
"dependencies": {
1828
"@react-native-async-storage/async-storage": "^1.23.1",
@@ -32,22 +42,29 @@
3242
"twrnc": "^4.6.0"
3343
},
3444
"devDependencies": {
35-
"@babel/core": "^7.20.0",
45+
"@babel/core": "^7.26.9",
46+
"@babel/preset-env": "^7.26.9",
47+
"@testing-library/react-native": "^13.2.0",
48+
"@types/jest": "^29.5.14",
3649
"@types/react": "~18.3.12",
3750
"@typescript-eslint/eslint-plugin": "^7.7.0",
3851
"@typescript-eslint/parser": "^7.7.0",
52+
"babel-jest": "^29.7.0",
3953
"eslint": "^8.57.0",
4054
"eslint-config-universe": "^12.0.1",
55+
"eslint-plugin-testing-library": "^7.1.1",
4156
"jest": "^29.7.0",
57+
"jest-expo": "~52.0.5",
58+
"msw": "^2.7.3",
4259
"prettier": "^3.2.5",
4360
"prettier-plugin-tailwindcss": "^0.5.11",
61+
"react-test-renderer": "18.3.1",
4462
"tailwindcss": "^3.4.0",
4563
"typescript": "~5.3.3"
4664
},
4765
"eslintConfig": {
4866
"extends": "universe/native",
4967
"root": true
5068
},
51-
"main": "src/App.tsx",
5269
"private": true
5370
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { screen, userEvent } from '@testing-library/react-native';
2+
import { http, HttpResponse } from 'msw';
3+
import { setupServer } from 'msw/node';
4+
import React from 'react';
5+
6+
import { Provider as AuthProvider } from '../components/context/AuthContext';
7+
import Main from '../index';
8+
import AccountScreen from '../screens/AccountScreen';
9+
import { renderWithRedux } from '../test-utils/redux-test-utils';
10+
import { renderWithProviders } from '../test-utils/test-utils';
11+
12+
const handlers = [
13+
http.delete('https://casa-qa.herokuapp.com/api/v1/users/sign_out', () => {
14+
return HttpResponse.json({ message: 'Signed out successfully.' }, { status: 200 });
15+
}),
16+
http.post('https://casa-qa.herokuapp.com/api/v1/users/sign_in', () => {
17+
return HttpResponse.json(
18+
{
19+
id: 1,
20+
isSignedIn: true,
21+
api_token: 'aaaa',
22+
refresh_token: 'bbbb',
23+
user: {
24+
id: 1,
25+
display_name: 'Test User',
26+
email: 'test@test.com',
27+
refresh_token_expires_at: '2022-01-01T00:00:00.000Z',
28+
token_expires_at: '2022-01-01T00:00:00.000Z',
29+
},
30+
},
31+
{ status: 201 }
32+
);
33+
}),
34+
];
35+
36+
const server = setupServer(...handlers);
37+
38+
beforeAll(() => server.listen());
39+
afterEach(() => server.resetHandlers());
40+
afterAll(() => server.close());
41+
42+
describe('AccountScreen', () => {
43+
it('renders the users display name, email, and one button', () => {
44+
renderWithProviders(<AccountScreen />);
45+
46+
// Two Text elements for user display name and email should be rendered
47+
expect(screen.getByTestId('user:display_name')).toBeOnTheScreen();
48+
expect(screen.getByTestId('user:email')).toBeOnTheScreen();
49+
50+
// One Button element for sign out should be rendered
51+
expect(screen.getByRole('button', { name: 'Sign out' })).toBeOnTheScreen();
52+
});
53+
54+
it('navigates to the login screen when the sign out button is pressed', async () => {
55+
renderWithRedux(
56+
<AuthProvider>
57+
<Main />
58+
</AuthProvider>
59+
);
60+
61+
const user = userEvent.setup();
62+
// Fill in the email and password fields
63+
await user.type(screen.getByPlaceholderText('Email'), 'ash');
64+
await user.type(screen.getByPlaceholderText('Password'), 'ketchum');
65+
// Press the sign in button
66+
await user.press(screen.getByRole('button', { name: 'Sign In' }));
67+
68+
// check we are on the case contact list screen
69+
expect(await screen.findByText('MY CASES')).toBeOnTheScreen();
70+
71+
// go to account screen
72+
await user.press(screen.getByRole('button', { name: 'Account' }));
73+
expect(await screen.findByText('Test User')).toBeOnTheScreen();
74+
expect(await screen.findByText('test@test.com')).toBeOnTheScreen();
75+
76+
// return to login screen after signing out
77+
await user.press(screen.getByRole('button', { name: 'Sign out' }));
78+
expect(await screen.findByPlaceholderText('Email')).toBeOnTheScreen();
79+
expect(await screen.findByPlaceholderText('Password')).toBeOnTheScreen();
80+
});
81+
});

src/components/AuthForm.tsx

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import React, { useState, useContext } from 'react';
2-
import { TextInput, Text, StyleSheet, Modal, Alert, Pressable, View } from 'react-native';
3-
import { Context as AuthContext } from 'src/components/context/AuthContext';
2+
import { TextInput, Text, StyleSheet, Modal, Pressable, View } from 'react-native';
43
import tw from 'twrnc';
54

65
import Button from './Button';
76
import CheckBox from './CheckBox';
7+
import { Context as AuthContext } from '../components/context/AuthContext';
88

99
interface LoginFieldProps {
10-
errorMessage: string;
11-
submitButtonText: string;
12-
onSubmit: ({ email, password}) => void;
13-
style?: StyleSheet.NamedStyles<any>;
10+
errorMessage?: string;
1411
}
1512

1613
const AuthForm: React.FC<LoginFieldProps> = ({
1714
errorMessage,
18-
submitButtonText,
19-
onSubmit,
20-
style,
2115
}) => {
2216
const [email, setEmail] = useState<string>('');
2317
const [password, setPassword] = useState<string>('');
24-
const [staySignedIn, setStaySignedIn] = useState<boolean>(false)
18+
const [staySignedIn, setStaySignedIn] = useState<boolean>(false);
2519
const [modalVisible, setModalVisible] = useState<boolean>(false);
2620

2721
const { signin } = useContext(AuthContext);
@@ -34,9 +28,9 @@ const AuthForm: React.FC<LoginFieldProps> = ({
3428
}
3529
};
3630

37-
const handleStaySignInToggle = () => {
38-
setStaySignedIn(!staySignedIn);
39-
if (!staySignedIn) setModalVisible(true);
31+
const handleStaySignInToggle = () => {
32+
setStaySignedIn(!staySignedIn);
33+
if (!staySignedIn) setModalVisible(true);
4034
};
4135

4236
return (
@@ -58,30 +52,28 @@ const AuthForm: React.FC<LoginFieldProps> = ({
5852
autoCorrect={false}
5953
onChangeText={setPassword}
6054
/>
61-
<CheckBox
62-
onPress={handleStaySignInToggle}
63-
title="Stay Logged In"
64-
isChecked={staySignedIn}
65-
/>
66-
{errorMessage && <Text className="pl-4 text-red-500">{errorMessage}</Text>}
55+
<CheckBox onPress={handleStaySignInToggle} title="Stay Logged In" isChecked={staySignedIn} />
56+
{errorMessage && <Text style={tw`pl-4 text-red-500`}>{errorMessage}</Text>}
6757
<Button
6858
buttonStyle={tw`flex bg-[#ea5a4e] rounded-3xl w-40 h-10 flex justify-center items-center
69-
${!email || !password ? 'bg-gray-400' : 'bg-[#ea5a4e]'}` }
59+
${!email || !password ? 'bg-gray-400' : 'bg-[#ea5a4e]'}`}
7060
textStyle={tw`text-xl font-bold text-white`}
7161
title="Sign In"
7262
onPress={handleSignIn}
7363
disabled={!email || !password}
7464
/>
7565
<Modal
7666
animationType="slide"
77-
transparent={true}
67+
transparent
7868
visible={modalVisible}
7969
onRequestClose={() => {
8070
setModalVisible(!modalVisible);
8171
}}>
8272
<View style={tw`flex-1 justify-center items-center`}>
8373
<View style={tw`m-5 bg-white rounded-2xl p-9 items-center shadow-lg`}>
84-
<Text style={tw`mb-4 text-center`}>Your session will stay active until you sign out.</Text>
74+
<Text style={tw`mb-4 text-center`}>
75+
Your session will stay active until you sign out.
76+
</Text>
8577
<Pressable
8678
style={tw`rounded-2xl px-4 py-2 bg-[#345073]`}
8779
onPress={() => setModalVisible(!modalVisible)}>

src/components/Button.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ const Button: React.FC<Props> = ({
1919
disabled,
2020
}) => {
2121
return (
22-
<TouchableOpacity style={buttonStyle} onPress={onPress} disabled={disabled}>
22+
<TouchableOpacity
23+
style={buttonStyle}
24+
onPress={onPress}
25+
disabled={disabled}
26+
accessibilityRole="button">
2327
<Text style={textStyle} onPress={disabled ? undefined : onPress} disabled={disabled}>
2428
{title}
2529
</Text>

src/components/FormPages/Page3.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import DateTimePicker from '@react-native-community/datetimepicker';
22
import React from 'react';
33
import { View, Text, ScrollView, TextInput } from 'react-native';
44
import { CheckBox } from 'react-native-elements';
5-
import Button from 'src/components/Button';
5+
import Button from '../Button';
66
import tw from 'twrnc';
77

88
const Page3 = ({

src/components/FormPages/Page4.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { View, Text, TextInput, Alert } from 'react-native';
3-
import Button from 'src/components/Button';
3+
import Button from '../Button';
44
import tw from 'twrnc';
55

66
const submit = (miles, hours, minutes, date, allCheckboxes) => {

0 commit comments

Comments
 (0)