Skip to content

screen.findByText not finding text fetched from mocked axios call using msw #382

@LuisBarroso37

Description

@LuisBarroso37
  • @testing-library/jest-dom version: 5.11.9
  • msw version: 0.28.2
  • node version: 14.17.1
  • react-testing-library version: 11.2.5
  • @material-ui/core: 4.11.3,
  • @material-ui/data-grid: 4.0.0-alpha.19
  • axios: 0.21.1

Relevant code or config:

UsersTable.test.tsx

import UsersTable from './UsersTable';
import { render, screen, waitFor } from '../../test/setup';
import { server, rest } from '../../test/server';

describe('Testing UsersTable component', () => {
  it('renders the component', async () => {
    render(<UsersTable />);

    await waitFor(() => {
      const tableHeader = screen.getByRole('row', {
        name: 'Id Email Company',
      });
      expect(tableHeader).toBeInTheDocument();
    });
  });

  it('renders error message if not able to fetch models from server', async () => {
    // Get back error message from server
    server.use(
      rest.get('http://localhost:9000/api/users', (req, res, ctx) => {
        return res(
          ctx.status(400),
          ctx.json({ message: 'Access to account denied' })
        );
      })
    );

    render(<UsersTable />);

    await waitFor(() => {
      // Expect not find any rows in the table
      const companyNameCells = screen.queryByText(/Rootit/i);
      expect(companyNameCells).toBeNull();

      // Expect alert message to pop up
      const alertMessage = screen.getByText('Access to account denied');
      expect(alertMessage).toBeInTheDocument();
    });
  });

  it('renders models that are fetched from server in the table', async () => {
    render(<UsersTable />);

    const emailOneCell = await screen.findByText('test1@test.com');
    const emailTwoCell = await screen.findByText('test2@test.com');

    expect(emailOneCell).toBeDefined();
    expect(emailTwoCell).toBeDefined();
  });
});
mws setup

import { rest } from 'msw';
import { setupServer } from 'msw/node';

const users = [
  {
    id: 'abc',
    email: 'test1@test.com',
    company: 'Test',
  },
  {
    id: 'def',
    email: 'test2@test.com',
    company: 'Test',
  },
  {
    id: 'ghi',
    email: 'test3@test.com',
    company: 'Test',
  },
  {
    id: 'jkl',
    email: 'test4@test.com',
    company: 'Test',
  },
];

const handlers = [
  rest.get('http://localhost:9000/api/users', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(users));
  }),
];

// This configures a request mocking server with the given request handlers
const server = setupServer(...handlers);

export { server, rest };
UsersTable.tsx

import { useCallback, useEffect, useRef, useState, useContext } from 'react';
import { DataGrid, GridColDef } from '@material-ui/data-grid';
import { Box } from '@material-ui/core';

import TableTooltip from '../../components/Tooltip/Tooltip';
import { useStyles } from './UsersTable.styles';
import { ActionTypes, AppContext } from '../../context/app-context';
import { ApiResult } from '../../api/ApiResultInterface';
import { ApiService } from '../../api/ApiSevice';

type User = {
  id: string;
  email: string;
  company: string;
};

const UsersTable = () => {
  const [users, setUsers] = useState<User[]>([]);
  const classes = useStyles();
  const { dispatch } = useContext(AppContext);

  // Used to not update state if component is unmounted before data fetch is complete
  let _isMounted = useRef(true);

  const setAlert = useCallback(
    (msg: string, status: number) => {
      dispatch({
        type: ActionTypes.SetAlertMessage,
        payload: new ApiResult(msg, status),
      });
      dispatch({ type: ActionTypes.OpenAlertMessage, payload: true });
    },
    [dispatch]
  );

  useEffect(() => {
    return () => {
      _isMounted.current = false;
    };
  }, []);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await ApiService.getUsers();

        // Do nothing if component has unmounted while fetching data
        if (!_isMounted.current) {
          return;
        }

        setUsers(response.data);
      } catch (err) {
        if (err.response) {
          const status = err.response.status;
          const error = err.response.data;

          setAlert(error.message, status);
        } else {
          setAlert(err.message, 400);
        }
      }
    };

    fetchUsers();
  }, [setAlert]);

  // Table columns
  const columns: GridColDef[] = [
    {
      field: 'id',
      headerName: 'Id',
      description: 'Id',
      headerAlign: 'center',
      align: 'center',
      flex: 2,
      renderCell: (cellParams) => {
        return (
          <TableTooltip title={cellParams.value as string}>
            <div className={classes.cellText}>{cellParams.value}</div>
          </TableTooltip>
        );
      },
      renderHeader: (headerParams) => {
        return (
          <TableTooltip title={headerParams.colDef.description as string}>
            <div className={classes.cellText}>
              {headerParams.colDef.description}
            </div>
          </TableTooltip>
        );
      },
    },
    {
      field: 'email',
      description: 'Email',
      headerName: 'Email',
      headerAlign: 'center',
      align: 'center',
      flex: 2,
      renderCell: (cellParams) => {
        return (
          <TableTooltip title={cellParams.value as string}>
            <div className={classes.cellText}>{cellParams.value}</div>
          </TableTooltip>
        );
      },
      renderHeader: (headerParams) => {
        return (
          <TableTooltip title={headerParams.colDef.description as string}>
            <div className={classes.cellText}>
              {headerParams.colDef.description}
            </div>
          </TableTooltip>
        );
      },
    },
    {
      field: 'company',
      description: 'Company',
      headerName: 'Company',
      headerAlign: 'center',
      align: 'center',
      flex: 1,
      renderCell: (cellParams) => {
        return (
          <TableTooltip title={cellParams.value as string}>
            <div className={classes.cellText}>{cellParams.value}</div>
          </TableTooltip>
        );
      },
      renderHeader: (headerParams) => {
        return (
          <TableTooltip title={headerParams.colDef.description as string}>
            <div className={classes.cellText}>
              {headerParams.colDef.description}
            </div>
          </TableTooltip>
        );
      },
    },
  ];

  return (
    <Box boxShadow={3} className={classes.usersTableContainer}>
      <DataGrid
        rows={users}
        loading={users === []}
        columns={columns}
        pageSize={100}
        className={classes.usersTable}
        checkboxSelection={false}
        localeText={{
          footerRowSelected: () => '',
        }}
      />
    </Box>
  );
};

export default UsersTable;

What happened:

This is the error message:
image
image

Problem description:

I expected screen to find the fetched data in the DataGrid component but it does not. I have written other very similar tests with the same setup and they all pass so I do not understand why this one does not.

I console logged the response that I get back from the mocked axios call and I see the data. I know that the state is being updated but for some reason screen.findByText() does not find it.

Can you help me figure out why this is happening?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions