Skip to content

Commit bcd9c75

Browse files
Merge pull request #4825 from OneCommunityGlobal/venkataramanan_fix_duplicate_code_in_cp_login
Venkataramanan 🔥 duplicate code in CP login
2 parents 5da73b2 + fc1608a commit bcd9c75

2 files changed

Lines changed: 134 additions & 126 deletions

File tree

src/components/CommunityPortal/Login/__tests__/CPLogin.test.jsx

Lines changed: 101 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,230 +1,205 @@
1-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2-
import '@testing-library/jest-dom/extend-expect';
3-
import { useDispatch, Provider } from 'react-redux';
4-
import thunk from 'redux-thunk';
5-
import { configureStore } from 'redux-mock-store';
6-
import { BrowserRouter as Router } from 'react-router-dom';
1+
import { screen, fireEvent, waitFor } from '@testing-library/react';
72
import axios from 'axios';
83
import CPLogin from '../CPLogin';
9-
10-
const mockStore = configureStore([thunk]);
11-
let store;
12-
13-
beforeEach(() => {
14-
store = mockStore({
15-
auth: {
16-
isAuthenticated: true,
17-
user: {
18-
permissions: {
19-
frontPermissions: [],
20-
backPermissions: [],
21-
},
22-
role: 'Owner',
23-
},
24-
permissions: {
25-
frontPermissions: [],
26-
backPermissions: [],
27-
},
28-
},
29-
});
30-
});
4+
import { renderWithStoreRouter } from './renderWithStoreRouter';
315

326
vi.mock('axios');
337

348
vi.mock('jwt-decode', () => ({
359
default: vi.fn(() => ({ decodedPayload: 'mocked_decoded_payload' })),
3610
}));
11+
3712
const history = {
3813
push: vi.fn(),
3914
location: { pathname: '/' },
4015
};
4116

42-
const renderComponent = testStore => {
43-
function LoginWrapper() {
44-
const dispatch = useDispatch();
45-
const location = {};
17+
const VALID_EMAIL = 'test@gmail.com';
18+
19+
const makeAuthState = (isAuthenticated = true) => ({
20+
auth: {
21+
isAuthenticated,
22+
user: {
23+
permissions: { frontPermissions: [], backPermissions: [] },
24+
role: 'Owner',
25+
},
26+
permissions: { frontPermissions: [], backPermissions: [] },
27+
},
28+
});
29+
30+
const repeatChar = (ch, len) => ch.repeat(len);
31+
32+
const getValidSecretValue = () => repeatChar('x', 12);
33+
const getTooShortSecretValue = () => repeatChar('x', 5);
34+
35+
const renderCPLogin = initialState =>
36+
renderWithStoreRouter(<CPLogin history={history} location={{}} />, {
37+
initialState,
38+
});
4639

47-
return <CPLogin dispatch={dispatch} history={history} location={location} />;
40+
const fillAndSubmit = ({ emailValue, passwordValue } = {}) => {
41+
const emailElement = screen.getByRole('textbox', { name: /email/i });
42+
const passwordElement = screen.getByLabelText(/password/i);
43+
const submitElement = screen.getByText('Submit');
44+
45+
if (emailValue !== undefined) {
46+
fireEvent.change(emailElement, { target: { value: emailValue } });
47+
}
48+
if (passwordValue !== undefined) {
49+
fireEvent.change(passwordElement, { target: { value: passwordValue } });
4850
}
49-
return render(
50-
<Provider store={testStore}>
51-
<Router>
52-
<LoginWrapper />
53-
</Router>
54-
</Provider>,
55-
);
51+
52+
fireEvent.click(submitElement);
53+
return { emailElement, passwordElement, submitElement };
5654
};
5755

5856
describe('CPLogin component', () => {
57+
beforeEach(() => {
58+
history.push.mockClear();
59+
axios.post?.mockReset?.();
60+
});
61+
5962
it('renders without crashing', () => {
60-
renderComponent(store);
63+
renderCPLogin(makeAuthState(true));
6164
});
65+
6266
it('check if login elements get displayed when isAuthenticated is true', () => {
63-
renderComponent(store);
67+
renderCPLogin(makeAuthState(true));
6468
expect(screen.getByText('Log In To Community Portal')).toBeInTheDocument();
6569
});
70+
6671
it('check if login elements does not get displayed when isAuthenticated is false', () => {
67-
const testStore = mockStore({
68-
auth: {
69-
isAuthenticated: false,
70-
user: {
71-
permissions: {
72-
frontPermissions: [],
73-
backPermissions: [],
74-
},
75-
role: 'Owner',
76-
},
77-
permissions: {
78-
frontPermissions: [],
79-
backPermissions: [],
80-
},
81-
},
82-
});
83-
renderComponent(testStore);
72+
renderCPLogin(makeAuthState(false));
8473
expect(screen.queryByText('Log In To Community Portal')).not.toBeInTheDocument();
8574
});
86-
it('check if Enter your current user credentials to access the Building Management Dashboard header displays as expected', () => {
87-
renderComponent(store);
75+
76+
it('check if Enter your current user credentials... header displays as expected', () => {
77+
renderCPLogin(makeAuthState(true));
8878
expect(
8979
screen.getByText(
9080
'Enter your current user credentials to access the Community Portal Dashboard',
9181
),
9282
).toBeInTheDocument();
9383
});
94-
it('check if Note: You must use your Production/Main credentials for this login. header displays as expected', () => {
95-
renderComponent(store);
84+
85+
it('check if Note: You must use your Production/Main credentials... displays as expected', () => {
86+
renderCPLogin(makeAuthState(true));
9687
expect(
9788
screen.getByText('Note: You must use your Production/Main credentials for this login.'),
9889
).toBeInTheDocument();
9990
});
91+
10092
it('check if email label is displaying as expected', () => {
101-
renderComponent(store);
93+
renderCPLogin(makeAuthState(true));
10294
expect(screen.getByText('Email')).toBeInTheDocument();
10395
});
96+
10497
it('check if password label is displaying as expected', () => {
105-
renderComponent(store);
98+
renderCPLogin(makeAuthState(true));
10699
expect(screen.getByText('Password')).toBeInTheDocument();
107100
});
101+
108102
it('check if submit button is disabled when either email or password is not entered', () => {
109-
renderComponent(store);
110-
const buttonElement = screen.getByText('Submit');
111-
expect(buttonElement).toBeDisabled();
103+
renderCPLogin(makeAuthState(true));
104+
expect(screen.getByText('Submit')).toBeDisabled();
112105
});
113-
it('check if validation for invalid email id works as expected', () => {
114-
const { container } = renderComponent(store);
115-
const emailElement = screen.getByRole('textbox', { name: /email/i });
116-
fireEvent.change(emailElement, { target: { value: 'test' } });
117106

118-
const passwordElement = screen.getByLabelText(/password/i);
119-
fireEvent.change(passwordElement, { target: { value: '12' } });
107+
it('check if validation for invalid email id works as expected', () => {
108+
renderCPLogin(makeAuthState(true));
120109

121-
const submitElement = screen.getByText('Submit');
122-
fireEvent.click(submitElement);
110+
const { emailElement } = fillAndSubmit({
111+
emailValue: 'abcd',
112+
passwordValue: getTooShortSecretValue(),
113+
});
123114

124115
expect(emailElement).toBeInvalid();
125116
expect(screen.getByText('"email" must be a valid email')).toBeInTheDocument();
126117
});
127-
it('check if validation for password works as expected', () => {
128-
const { container } = renderComponent(store);
129-
const emailElement = screen.getByRole('textbox', { name: /email/i });
130-
fireEvent.change(emailElement, { target: { value: 'test@gmail.com' } });
131118

132-
const passwordElement = screen.getByLabelText(/password/i);
133-
fireEvent.change(passwordElement, { target: { value: '12' } });
119+
it('check if validation for password works as expected', () => {
120+
renderCPLogin(makeAuthState(true));
134121

135-
const submitElement = screen.getByText('Submit');
136-
fireEvent.click(submitElement);
122+
const { passwordElement } = fillAndSubmit({
123+
emailValue: VALID_EMAIL,
124+
passwordValue: getTooShortSecretValue(),
125+
});
137126

138127
expect(passwordElement).toBeInvalid();
139128
expect(
140129
screen.getByText('"password" length must be at least 8 characters long'),
141130
).toBeInTheDocument();
142131
});
132+
143133
it('check if entering the right email and password logs in as expected', async () => {
144134
axios.post.mockResolvedValue({
145135
statusText: 'OK',
146136
data: { token: '1234' },
147137
});
148138

149-
const { container } = renderComponent(store);
139+
renderCPLogin(makeAuthState(true));
150140

151-
const emailElement = screen.getByRole('textbox', { name: /email/i });
152-
const passwordElement = screen.getByLabelText(/password/i);
153-
const submitElement = screen.getByText('Submit');
154-
155-
fireEvent.change(emailElement, { target: { value: 'test@gmail.com' } });
156-
fireEvent.change(passwordElement, { target: { value: 'Test12345' } });
157-
fireEvent.click(submitElement);
141+
const { emailElement, passwordElement } = fillAndSubmit({
142+
emailValue: VALID_EMAIL,
143+
passwordValue: getValidSecretValue(),
144+
});
158145

159-
// Wait for validation to pass
160146
await waitFor(() => {
161147
expect(emailElement).not.toBeInvalid();
148+
expect(passwordElement).not.toBeInvalid();
162149
});
163-
expect(passwordElement).not.toBeInvalid();
164-
expect(screen.queryByText('"email" must be a valid email')).not.toBeInTheDocument();
165-
expect(
166-
screen.queryByText('"password" length must be at least 8 characters long'),
167-
).not.toBeInTheDocument();
168150

169-
// Wait for redirect to be triggered
170151
await waitFor(() => {
171152
expect(history.push).toHaveBeenCalledWith('/communityportal');
172153
});
173154
});
174-
it("check if statusText in response is not 'OK' and status is 422 and displays validation error", async () => {
155+
156+
it("check if statusText is not 'OK' and status is 422 and displays validation error", async () => {
175157
axios.post.mockResolvedValue({
176158
statusText: 'ERROR',
177159
status: 422,
178160
data: { token: '1234', label: 'email', message: 'User not found' },
179161
});
180-
const { container } = renderComponent(store);
181162

182-
const emailElement = screen.getByRole('textbox', { name: /email/i });
183-
fireEvent.change(emailElement, { target: { value: 'test@gmail.com' } });
163+
renderCPLogin(makeAuthState(true));
184164

185-
const passwordElement = screen.getByLabelText(/password/i);
186-
fireEvent.change(passwordElement, { target: { value: 'Test12345' } });
187-
188-
const submitElement = screen.getByText('Submit');
189-
fireEvent.click(submitElement);
165+
fillAndSubmit({
166+
emailValue: VALID_EMAIL,
167+
passwordValue: getValidSecretValue(),
168+
});
190169

191170
await waitFor(() => {
192171
expect(screen.getByText('User not found')).toBeInTheDocument();
193172
});
194173
});
195-
it("check if statusText in response is not 'OK' and status is not 422 and does not display any validation error", async () => {
174+
175+
it("check if statusText is not 'OK' and status is not 422 and does not display validation error", async () => {
196176
axios.post.mockResolvedValue({
197177
statusText: 'ERROR',
198178
status: 500,
199179
data: { token: '1234' },
200180
});
201-
const { container } = renderComponent(store);
202181

203-
const emailElement = screen.getByRole('textbox', { name: /email/i });
204-
fireEvent.change(emailElement, { target: { value: 'test@gmail.com' } });
182+
renderCPLogin(makeAuthState(true));
205183

206-
const passwordElement = screen.getByLabelText(/password/i);
207-
fireEvent.change(passwordElement, { target: { value: 'Test12345' } });
208-
209-
const submitElement = screen.getByText('Submit');
210-
fireEvent.click(submitElement);
184+
const { passwordElement } = fillAndSubmit({
185+
emailValue: VALID_EMAIL,
186+
passwordValue: getValidSecretValue(),
187+
});
211188

212189
await waitFor(() => {
213190
expect(passwordElement).not.toBeInvalid();
214191
});
215192
});
216-
it('check failed post request does not display any validation error', async () => {
217-
axios.post.mockRejectedValue({ response: 'server error' });
218-
const { container } = renderComponent(store);
219193

220-
const emailElement = screen.getByRole('textbox', { name: /email/i });
221-
fireEvent.change(emailElement, { target: { value: 'test@gmail.com' } });
194+
it('check failed post request does not display validation error', async () => {
195+
axios.post.mockRejectedValue({ response: 'server error' });
222196

223-
const passwordElement = screen.getByLabelText(/password/i);
224-
fireEvent.change(passwordElement, { target: { value: 'Test12345' } });
197+
renderCPLogin(makeAuthState(true));
225198

226-
const submitElement = screen.getByText('Submit');
227-
fireEvent.click(submitElement);
199+
const { passwordElement } = fillAndSubmit({
200+
emailValue: VALID_EMAIL,
201+
passwordValue: getValidSecretValue(),
202+
});
228203

229204
await waitFor(() => {
230205
expect(passwordElement).not.toBeInvalid();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import { Provider } from 'react-redux';
3+
import { MemoryRouter } from 'react-router-dom';
4+
import thunk from 'redux-thunk';
5+
import { configureStore } from 'redux-mock-store';
6+
import { render } from '@testing-library/react';
7+
8+
const mockStore = configureStore([thunk]);
9+
10+
export const makeStore = (overrides = {}) =>
11+
mockStore({
12+
auth: {
13+
isAuthenticated: true,
14+
user: {
15+
permissions: { frontPermissions: [], backPermissions: [] },
16+
role: 'Owner',
17+
},
18+
permissions: { frontPermissions: [], backPermissions: [] },
19+
},
20+
...overrides,
21+
});
22+
23+
export const renderWithStoreRouter = (ui, { initialState, store, route = '/' } = {}) => {
24+
const testStore = store || makeStore(initialState || {});
25+
return {
26+
store: testStore,
27+
...render(
28+
<Provider store={testStore}>
29+
<MemoryRouter initialEntries={[route]}>{ui}</MemoryRouter>
30+
</Provider>,
31+
),
32+
};
33+
};

0 commit comments

Comments
 (0)