I am working on React App
with Vite
and Vitest
for unit testing.
I Need to test the following cases on the Login Form
On login success, it is navigating to Profile Page
. When it comes to Unit test case it seems like it is not navigating properly. Here is what i Have tried so.
React:
v18
, Vite:4.4.4
, Vitest:0.33
Using dummyjson api for testing purpose
Test file :
__tests__/Login/Validation.test.jsx
import { describe, test } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import LoginPage from '../../src/pages/proteced-pages/LoginPage';
import { Provider } from 'react-redux';
import Store from '../../src/redux/store.js';
import { BrowserRouter} from 'react-router-dom';
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<BrowserRouter>
<LoginPage />
</BrowserRouter>
</Provider>
);
};
describe('Test ==> Login Form:', () => {
// TEST 1
test('Client side validation (REQUIRED): ', async () => {
renderLoginPage();
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.click(submitButton);
await waitFor(() => {
const emailError = screen.getByText('username is required.', { selector: 'span' });
const passError = screen.getByText('Password is required.', { selector: 'span' });
expect(emailError).toBeInTheDocument();
expect(passError).toBeInTheDocument();
});
});
// TEST 2
test('Invalid username, password: ', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'Sam12_#' } });
fireEvent.change(passwordInput, { target: { value: 'pass' } });
fireEvent.click(submitButton);
await waitFor(() => {
const invalidMessage = screen.getByText('Invalid credentials', { selector: 'strong' });
expect(invalidMessage).toBeInTheDocument();
});
});
// TEST 3 Not working
test('Valid username, password: ', async () => {
renderLoginPage();
screen.debug();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('Profile pages', { selector: 'p' });
console.log(profilePageHeading)
expect(profilePageHeading).toBeInTheDocument();
});
});
});
Error message
Unable to find an element with the text: Profile pages, which matches selector 'p'. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
I have fixed the problem replacing BrowserRouter
with MemoryRouter
If someone has better suggestion then he is welcome to share.
Here is the solution that worked for me
//import profile page
import ProfilePage from '../../src/pages/auth-pages/ProfilePage';
//replacing BrowserRouter with MemoryRouter
const renderLoginPage = () => {
render(
<Provider store={Store()}>
<MemoryRouter initialEntries={['/login']}>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Routes>
</MemoryRouter>
</Provider>
);
};
describe('Test ==> Login Form:', () => {
// TEST 3
test('Valid username, password: ', async () => {
renderLoginPage();
const emailInput = screen.getByLabelText('username:');
const passwordInput = screen.getByLabelText('Password:');
const submitButton = screen.getByRole('button', { name: /login/i });
fireEvent.change(emailInput, { target: { value: 'kminchelle' } });
fireEvent.change(passwordInput, { target: { value: '0lelplR' } });
fireEvent.click(submitButton);
expect(emailInput.value).toBe('kminchelle');
expect(passwordInput.value).toBe('0lelplR');
await waitFor(() => {
const profilePageHeading = screen.getByText('Profile pages', { selector: 'p' });
expect(profilePageHeading).toBeInTheDocument();
});
});
});