React Testing with the help of storybook stories
Tags: testing, webdev, tutorial, react
Published: 2023-06-26 05:59:57


In our latest blog post on creating components with Storybook, we will leverage the existing stories of our button components to create comprehensive test cases using Testing Library.

You can refer to the official documentation for setting up Testing Library
https://testing-library.com/docs/react-testing-library/setup/


The first step is to create our testing file, in my case "button.test.tsx", and add the necessary imports.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gnlpqkvsvwhjfoofzcgg.png) 

and this is the full example: 

```
// Describe block for the Button Component
describe('Button Component', () => {

  // Test case for rendering primary button
  test('should render primary button', () => {
    render(<Primary {...Primary.args} />);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering secondary button
  test('should render secondary button', () => {
    render(<Secondary {...Secondary.args} />);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering danger button
  test('should render danger button', () => {
    render(<Danger {...Danger.args} />);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering disabled button
  test('should render disabled button', () => {
    render(<Disabled {...Disabled.args} disabled />);
    const buttonElement = screen.getByRole('button');
    expect(buttonElement).toBeDisabled();
  });

  // Test case for rendering button instead of spinner when not loading
  test('should render button instead of spinner when not loading', () => {
    render(<Loading {...Loading.args} loading={false} />);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering Spinner instead of button when loading
  test('should render Spinner instead of button when loading', () => {
    render(<Loading {...Loading.args} />);
    const spinnerElement = screen.getByTestId('spinner');
    expect(spinnerElement).toBeInTheDocument();
  });

});


```

Let's explore a couple more test cases, focusing on the Select component and the Login/Signup form:

**▪️ Select component:**
```
// Import necessary libraries and components
import React from 'react';
import { render, screen } from '@testing-library/react';
import InputSelect, { Option } from '.';

// Describe block for the InputSelect component
describe('InputSelect', () => {

  // Define options array for testing
  const options: Option[] = [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' },
    { value: 'option3', label: 'Option 3' },
  ];

  // Create a mock onChange function
  const mockOnChange = jest.fn();

  // Test case for rendering label and options correctly
  test('should label and options correctly', () => {
    render(<InputSelect label='Select' options={options} value={null} onChangeAction={mockOnChange} />);

    // Get elements from the rendered component
    const labelElement = screen.getByText('Select');
    const selectElement = screen.getByRole('combobox');
    const optionElements = screen.queryAllByRole('option');

    // Assert label and select elements are in the document
    expect(labelElement).toBeInTheDocument();
    expect(selectElement).toBeInTheDocument();

    // Assert select element has correct aria-label attribute
    expect(selectElement).toHaveAttribute('aria-label', 'Select');

    // Assert each option element has correct text content
    optionElements.forEach((option, index) => {
      expect(option).toHaveTextContent(options[index].label);
    });
  });

  // Test case for rendering error message when error prop is provided
  test('should render error message when error prop is provided', () => {
    // Define an error object
    const error = { message: 'This field is required' };

    render(<InputSelect label='Select Option' options={options} value={null} error={error} onChangeAction={mockOnChange} />);

    // Get the error message element
    const errorMessage = screen.getByText('This field is required');

    // Assert error message is in the document
    expect(errorMessage).toBeInTheDocument();
  });
});

```
**▪️ Login form:**
```
// Import necessary functions and components from testing libraries
import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from '.';

// Describe block for the LoginForm Component
describe('LoginForm Component', () => {

  // Test case for rendering the login form correctly
  test('should render the login form correctly', () => {
    render(<LoginForm onhandleSubmit={() => {}} loading={false} />);

    // Get form elements
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const rememberMeCheckbox = screen.getByLabelText('Remember me');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });
    const signupLink = screen.getByRole('link', { name: 'Signup' });

    // Assert form elements are in the document
    expect(emailInput).toBeInTheDocument();
    expect(passwordInput).toBeInTheDocument();
    expect(rememberMeCheckbox).toBeInTheDocument();
    expect(signInButton).toBeInTheDocument();
    expect(signupLink).toBeInTheDocument();
  });

  // Test case for displaying error messages when form validation fails
  test('should display error messages when form validation fails', async () => {
    render(<LoginForm onhandleSubmit={() => {}} loading={false} />);

    // Get form elements
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });

    // Simulate form submission
    await act(async () => {
      userEvent.click(emailInput);
      userEvent.click(passwordInput);
      fireEvent.click(signInButton);
    });

    // Get error messages
    const emailErrorMessage = screen.getByTestId('email-error');
    const passwordErrorMessage = screen.getByTestId('password-error');

    // Assert error messages are in the document
    expect(emailErrorMessage).toBeInTheDocument();
    expect(passwordErrorMessage).toBeInTheDocument();
  });

  // Test case for calling onhandleSubmit when form is submitted
  test('should call onhandleSubmit when form is submitted', async () => {
    // Mock the handleSubmit function
    const mockHandleSubmit = jest.fn();
    render(<LoginForm onhandleSubmit={mockHandleSubmit} loading={false} />);
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });

    // Simulate form submission
    await act(async () => {
      userEvent.type(emailInput, 'test@test.com');
      userEvent.type(passwordInput, 'test123');
      fireEvent.submit(signInButton);
    });

    // Assert that handleSubmit function was called
    expect(mockHandleSubmit).toHaveBeenCalled();
  });
});

```

**▪️ Signup form:**

```
import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import RegisterForm from '.';

// Describe the RegisterForm component test suite
describe('RegisterForm Component', () => {
  // Define options for the select input
  let options = [{ label: 'lessor', value: 'lessor' }];

  // Test case for rendering the RegisterForm correctly
  test('should render the login form correctly', () => {
    render(<RegisterForm onhandleSubmit={() => {}} loading={false} roles={options} />);

    // Get form inputs and buttons
    const usernameInput = screen.getByTestId('username');
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const selectInput = screen.getByTestId('selectrole');
    const signInButton = screen.getByRole('button', { name: 'Sign up' });
    const signupLink = screen.getByRole('link', { name: 'Login' });

    // Assert form elements are in the document
    expect(usernameInput).toBeInTheDocument();
    expect(emailInput).toBeInTheDocument();
    expect(passwordInput).toBeInTheDocument();
    expect(selectInput).toBeInTheDocument();
    expect(signInButton).toBeInTheDocument();
    expect(signupLink).toBeInTheDocument();
  });

  // Test case for displaying error messages when form validation fails
  test('should display error messages when form validation fails', async () => {
    render(<RegisterForm onhandleSubmit={() => {}} loading={false} roles={options} />);

    // Get form inputs and submit button
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign up' });

    // Simulate form submission
    await act(async () => {
      userEvent.click(emailInput);
      userEvent.click(passwordInput);
      fireEvent.click(signInButton);
    });

    // Get error messages
    const emailErrorMessage = screen.getByTestId('email-error');
    const passwordErrorMessage = screen.getByTestId('password-error');

    // Assert error messages are in the document
    expect(emailErrorMessage).toBeInTheDocument();
    expect(passwordErrorMessage).toBeInTheDocument();
  });

  // Test case for submitting the form with valid data
  test('should submit the form with valid data', async () => {
    const handleSubmit = jest.fn();

    render(<RegisterForm onhandleSubmit={handleSubmit} loading={false} roles={options} />);

    // Simulate form submission with valid data
    await act(async () => {
      fireEvent.change(screen.getByTestId('username'), { target: { value: 'testname' } });
      fireEvent.change(screen.getByTestId('email'), { target: { value: 'email@provider.com' } });
      fireEvent.change(screen.getByTestId('password'), { target: { value: 'test123' } });

      fireEvent.click(screen.getByTestId('selectrole'));

      const optionElements = screen.queryAllByRole('option');
      optionElements.forEach((option, index) => {
        fireEvent.click(screen.getByRole('button', { name: 'Sign up' }));

        // Assert option text content and form submission
        expect(option).toHaveTextContent(options[index].label);
        expect(handleSubmit).toHaveBeenCalled();
      });
    });
  });
});
```

We have reached the end of the process. If you have any further questions or need more tips, feel free to contact me. 👋