Skip to content
Snippets Groups Projects
Commit 5c4db776 authored by mateusz-winiarczyk's avatar mateusz-winiarczyk
Browse files

test(overlays): add test for login component and thunk

parent aed10523
No related branches found
No related tags found
2 merge requests!87feat(overlays): MIN-192 add overlay as user,!79feat(overlays): MIN-191 add possibility to login
Pipeline #83426 passed
import { render, screen, fireEvent } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { act } from 'react-dom/test-utils';
import { LoginModal } from './LoginModal.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<LoginModal />
</Wrapper>,
),
{
store,
}
);
};
test('renders LoginModal component', () => {
renderComponent();
const loginInput = screen.getByLabelText(/login/i);
const passwordInput = screen.getByLabelText(/password/i);
expect(loginInput).toBeInTheDocument();
expect(passwordInput).toBeInTheDocument();
});
test('handles input change correctly', () => {
renderComponent();
const loginInput: HTMLInputElement = screen.getByLabelText(/login/i);
const passwordInput: HTMLInputElement = screen.getByLabelText(/password/i);
fireEvent.change(loginInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'testpassword' } });
expect(loginInput.value).toBe('testuser');
expect(passwordInput.value).toBe('testpassword');
});
test('submits form', () => {
renderComponent();
const loginInput = screen.getByLabelText(/login/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByText(/submit/i);
fireEvent.change(loginInput, { target: { value: 'testuser' } });
fireEvent.change(passwordInput, { target: { value: 'testpassword' } });
act(() => {
submitButton.click();
});
expect(submitButton).toBeDisabled();
});
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { loadingUserSelector } from '@/redux/user/user.selectors';
import { login } from '@/redux/user/user.thunks';
import { Button } from '@/shared/Button';
import Link from 'next/link';
......@@ -6,6 +8,8 @@ import React from 'react';
export const LoginModal: React.FC = () => {
const dispatch = useAppDispatch();
const loadingUser = useAppSelector(loadingUserSelector);
const isPending = loadingUser === 'pending';
const [credentials, setCredentials] = React.useState({ login: '', password: '' });
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
......@@ -15,8 +19,7 @@ export const LoginModal: React.FC = () => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
const formBody = new URLSearchParams(credentials);
dispatch(login(formBody));
dispatch(login(credentials));
};
return (
......@@ -51,7 +54,11 @@ export const LoginModal: React.FC = () => {
Forgot password?
</Link>
</div>
<Button type="submit" className="w-full justify-center text-base font-medium">
<Button
type="submit"
className="w-full justify-center text-base font-medium"
disabled={isPending}
>
Submit
</Button>
</form>
......
......@@ -2,17 +2,12 @@ import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { openLoginModal } from '@/redux/modal/modal.slice';
import { authenticatedUserSelector, loadingUserSelector } from '@/redux/user/user.selectors';
import { getSessionValid } from '@/redux/user/user.thunks';
import { Button } from '@/shared/Button';
import { useEffect } from 'react';
export const UserOverlays = (): JSX.Element => {
const dispatch = useAppDispatch();
const loadingUser = useAppSelector(loadingUserSelector);
const authenticatedUser = useAppSelector(authenticatedUserSelector);
useEffect(() => {
dispatch(getSessionValid());
}, [dispatch]);
const handleLoginClick = (): void => {
dispatch(openLoginModal());
......
......@@ -16,6 +16,7 @@ import {
} from '../map/map.thunks';
import { getSearchData } from '../search/search.thunks';
import { setPerfectMatch } from '../search/search.slice';
import { getSessionValid } from '../user/user.thunks';
interface InitializeAppParams {
queryData: QueryData;
......@@ -42,6 +43,9 @@ export const fetchInitialAppData = createAsyncThunk<
/** Create tabs for maps / submaps */
dispatch(initOpenedMaps({ queryData }));
// Check if auth token is valid
dispatch(getSessionValid());
/** Trigger search */
if (queryData.searchValue) {
dispatch(setPerfectMatch(queryData.perfectMatch));
......
......@@ -13,7 +13,10 @@ import userReducer from './user.slice';
const mockedAxiosClient = mockNetworkResponse();
const CREDENTIALS = new URLSearchParams();
const CREDENTIALS = {
login: 'test',
password: 'password',
};
const INITIAL_STATE: UserState = {
loading: 'idle',
......
import { mockNetworkResponse } from '@/utils/mockNetworkResponse';
import { HttpStatusCode } from 'axios';
import { loginFixture } from '@/models/fixtures/loginFixture';
import {
ToolkitStoreWithSingleSlice,
createStoreInstanceUsingSliceReducer,
} from '@/utils/createStoreInstanceUsingSliceReducer';
import { apiPath } from '../apiPath';
import { closeModal } from '../modal/modal.slice';
import userReducer from './user.slice';
import { UserState } from './user.types';
import { login } from './user.thunks';
const mockedAxiosClient = mockNetworkResponse();
const CREDENTIALS = {
login: 'test',
password: 'password',
};
describe('login thunk', () => {
let store = {} as ToolkitStoreWithSingleSlice<UserState>;
beforeEach(() => {
store = createStoreInstanceUsingSliceReducer('user', userReducer);
});
it('dispatches closeModal action on successful login with valid data', async () => {
mockedAxiosClient.onPost(apiPath.postLogin()).reply(HttpStatusCode.Ok, loginFixture);
const mockDispatch = jest.fn(action => {
if (action.type === closeModal.type) {
expect(action).toEqual(closeModal());
}
});
store.dispatch = mockDispatch;
await store.dispatch(login(CREDENTIALS));
});
it('does not dispatch closeModal action on failed login with invalid data', async () => {
mockedAxiosClient.onPost(apiPath.postLogin()).reply(HttpStatusCode.NotFound, loginFixture);
const mockDispatch = jest.fn(action => {
if (action.type === closeModal.type) {
expect(action).not.toEqual(closeModal());
}
});
store.dispatch = mockDispatch;
await store.dispatch(login(CREDENTIALS));
});
});
......@@ -8,8 +8,9 @@ import { closeModal } from '../modal/modal.slice';
export const login = createAsyncThunk(
'user/login',
async (credentials: URLSearchParams, { dispatch }) => {
const response = await axiosInstance.post(apiPath.postLogin(), credentials);
async (credentials: { login: string; password: string }, { dispatch }) => {
const searchParams = new URLSearchParams(credentials);
const response = await axiosInstance.post(apiPath.postLogin(), searchParams);
const isDataValid = validateDataUsingZodSchema(response.data, loginSchema);
dispatch(closeModal());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment