Search code examples
javascriptreactjsvitevitest

Reactjs - How to unit test login form in Vitest?


I am working on React App with Vite and Vitest for unit testing.

I Need to test the following cases on the Login Form

  • Required validation on input. `// working fine
  • Invalid username, and password (response coming form API). `// working fine
  • Login Success. `// Not working as expected

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.


Solution

  • 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();
            });
        });
    });