mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.55k stars 1.33k forks source link

[data grid] Testing with react testing library #1151

Open benjamin-schneider opened 3 years ago

benjamin-schneider commented 3 years ago

With the component @material-ui/data-grid I am unable to get the rows rendered in a jest / react-testing-library environment. I use jest and material-ui since years, and this is the first time I do not manage to find any solution.

Current Behavior 😯

I want to test a fully renderer datagrid. To test if a cell in a row il well formatted, to test row clicks, etc. Unfortunately, I don't manage to render the rows. In debug mode, I get :

<div
  class="rendering-zone"
  style="max-height: 36px; width: 900px; pointer-events: unset; transform: translate3d(-0px, -NaNpx, 0);"
/>

I saw this line in the internal tests : https://github.com/mui-org/material-ui-x/blob/master/packages/grid/x-grid/src/tests/rows.XGrid.test.tsx#L43 Maybe it it actually not possible to achieve testing rows :(

Expected Behavior 🤔

I would like datagrid to fully render in a jest/jestdom env.

Steps to Reproduce 🕹

Here is a very minimal repro to isolate :

import { DataGrid } from '@material-ui/data-grid';
import { render } from '@testing-library/react';
const Repro = () => {
  return (
    <DataGrid
      columns={
        [
          {
            field: 'field',
            headerName: 'col',
            flex: 1,
          },
        ]
      }
      rows={[
        {
          id: 'row1',
          field: 'row',
        },
      ]}
    />
  );
};

const createRender = () => {
  return render(<Repro />);
};

describe('Datagrid problem repro', () => {
  it('should render col', () => {
    const { getByText } = createRender();
    expect(getByText('col')).toBeInTheDocument();
  });
  it('should render row', () => {
    const { getByText } = createRender();
    expect(getByText('row')).toBeInTheDocument();
  });
});

First test (cols or headings) passes, testing rows fails

Your Environment 🌎

Ubuntu 20.04 / jest

Progress on the solution

https://github.com/mui-org/material-ui-x/issues/1151#issuecomment-817209168

oliviertassinari commented 3 years ago

@benjamin-schneider Did you find a solution?

I have looked at this problem a few months ago when I started to share the testing stack of the main repository with this one. I have stopped looking after failing to make the AutoSizer return my fake data in jsdom. So I ended up adding:

https://github.com/mui-org/material-ui-x/blob/59ef9dc874c49ae9be1f25b03339aef0026db884/packages/grid/data-grid/src/tests/components.DataGrid.test.tsx#L30-L33

I'm reopening because it's important. Developers should be able to tests their grid without having to rely on an actual browser as we do (it's not very common for UI components tests inside web applications). As far as I have pushed the exploration, I can think of two paths:

  1. We keep looking into what needs to be mocked in the tests in order for them to render without an empty cell. This might requires some not really pretty code (verbose and brittle).
  2. We add special logic in the source to behave nicely in the test environment. It's something we have already done in the past for the core components.

If you are using jest, you could probably mock the component as it's done in https://github.com/bvaughn/react-window/issues/454#issuecomment-646031139.

A bit of caution. I'm not completely sure that it's something that possible. jsdom might be missing some APIs in order for the grid to work well. This is not a simple button component.

dtassone commented 3 years ago

try to wrap your <DataGrid /> around a div that has a prefixed height and width as the grid calculates what to render according to the size of its parent container

<div style={{ width: 300, height: 300 }}>
  <DataGrid .../>
</div>,
oliviertassinari commented 3 years ago

@dtassone jsdom has no layout capabilities, the width/height would have no effects.

benjamin-schneider commented 3 years ago

@oliviertassinari Hi, thanks for your anwser. I did not find a solution, I just closed to avoid polluting your issues as I was thinking I was maybe wrong. I did not manage to mock the AutoSize component (with jest) correctly, it never enters in the mock function :

jest.mock('@material-ui/data-grid', () => {
  const original = jest.requireActual('@material-ui/data-grid');
  return {
    ...original,
    AutoSizer: ({ children }: AutoSizerProps) => {
      console.log('mocked AutoSizer');
      return children({
        height: 500,
        width: 500,
      });
    },
  };
});

This was just a five minutes try... I will try to investigate further as soon as I will find some time to understand the internals. Thanks a lot for all your work on the best React UI library.

oliviertassinari commented 3 years ago

@benjamin-schneider I have also spent 5 minutes looking at this yesterday. After solving the autosize issue, I was facing a .scrollTo missing API in jsdom 🙈. I have stopped there because I had to look at more pressing matters.

I have added the waiting for users upvotes tag. It will help evaluate if this is a frequent requirement. Fixing jsdom might be challenging and ever more as we add complexity in the internals of the component.

dtassone commented 3 years ago

@oliviertassinari How about we add 2 props to force the height and width of AutoSizer https://github.com/bvaughn/react-virtualized/issues/493#issuecomment-361029657

oliviertassinari commented 3 years ago

@dtassone Yeah, maybe we could do this for test only purposes. It could solve one part of the issue.

I think that we need to understand all the implications of supporting the rendering inside jsdom before making changes. There might be even bigger issues.

I also think that we can wait for more upvotes before dedicating time to it. It's unclear if developers want this. Intuitively, I would assume that many want it, but it seems costly, so if we can live without it, that's even better 🙃.

DavidHanover commented 3 years ago

just chiming in to say I'm having a bear of a time trying to test for row content in @material-ui/core/DataGrid. Tried everything in the past 48 hours & can't get the rows to even render within ReactTestingLibrary

UPDATE: finally got stuff to render. needed the "autoheight" param. for some reason it renders in the browser but not in the tests. needs autoheight. go figure

oliviertassinari commented 3 years ago

@DavidHanover The autoHeight approach sounds interesting. It might actually be good enough in many of the cases. It basically disables the row virtualization.

gap777 commented 3 years ago

Since you're wondering, we're also feeling this pain. We've been using Xgrid for about 4 mos, and felt almost immediate pain since we're a TDD shop. Found a workaround with alpha18 by setting a fixed width on the Grid's parent tied to a theme variable being set in test env only, but that stopped working in alpha24. BTW, we're testing with jest + RTL.

oliviertassinari commented 3 years ago

@gap777 What aspects are you testing on the data grid? Are you testing simple interactions, like what rows are displayed or more advanced things?

1361 is one step toward the resolution of this issue. With it, developers can test that the rows prop renders the correct

value.

gap777 commented 3 years ago

Making sure the right columns are displayed was our first round of tests where we ran into issues. Making sure the data is run through the correct formatting rules (colDef valueFormatter, etc) Making sure the react component using the table updated it with the correct data Making sure the click actions do the right thing

On Apr 10, 2021, at 5:34 PM, Olivier Tassinari @.***> wrote:

@gap777 https://github.com/gap777 What aspects are you testing on the data grid? Are you testing simple interactions, like what rows are displayed or more?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mui-org/material-ui-x/issues/1151#issuecomment-817205241, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABRR26RNT5PXSFZKQPGBQEDTIC76LANCNFSM4YQ7LFFQ.

oliviertassinari commented 3 years ago

@gap777 Ok great. This is covered by #1361.

For closing the issue. I would propose the following requirements:

On the approach, I could find this interesting comment. https://github.com/ag-grid/ag-grid/issues/4165#issuecomment-786916614. They have a prop to disable the virtualization. I guess we will know more about the challenges with our grid as me make progress on 2.

oliviertassinari commented 3 years ago

Since #1361, we now run 69% of our tests inside jsdom (and 100% inside real browsers).

benjamin-schneider commented 3 years ago

Very good job with the coverage improvements, thanks ! Finally, with forced autoHeight like suggested, we can render every Datagrids in our codebase without having to touching the source component, which was mandatory for us. If it can be helpful, here is small snippet :

import type { DataGridProps } from '@material-ui/data-grid';
jest.mock('@material-ui/data-grid', () => {
  const { DataGrid } = jest.requireActual('@material-ui/data-grid');
  return {
    ...jest.requireActual('@material-ui/data-grid'),
    DataGrid: (props: DataGridProps) => {
      return (
        <DataGrid
          {...props}
          autoHeight
        />
      );
    },
  };
});
Eyegee-Eleven commented 3 years ago

This is still causing me major issues :( Even if I look for row in my test It only slightly increase test coverage as opposed to the large increase when using a failing test that looks for the specific text I expect to be found in a cell after an API call is placed. Hopping to see a further resolution of this issue come down the pipe.

oliviertassinari commented 3 years ago

@Eyegee-Eleven do you have an example of a case you can't test? (please provide a codesandbox).

squidsoup commented 3 years ago

Hi, just curious if any progress has been made on this issue? We currently have to disable about a third of our test suite, which is very far from ideal.

oliviertassinari commented 3 years ago

To summarize where we are today for clarity:

  1. Internally, we have 248 tests running with jsdom, 286 tests running in real browsers.
  2. We need to allow developers to disable virtualization completely. The trick with autoHeight is limited as it only covers the rows virtualization, not the columns virtualization. #1781.
  3. Once we have added such an API, we need to document it.

I have added the "important" label to signal that it's one of the most upvoted issues. I'm not saying that we should work on it now as our priority should likely be to add the missing features, and maybe only after to polish, starting from this one. Or actually, to take this into account in #1933. Developers can always run their tests into a real browser, not jsdom.

@squidsoup Could you provide an example of tests that you can't write? Is it about using the columns or something else?

JPTeasdale commented 3 years ago

Is this issue the reason why this test fails?

    it('Renders all columns', async () => {
        render(
            <DataGridPro
                columns={[
                    {
                        field: 'field1',
                    },
                    {
                        field: 'field2',
                    },
                    {
                        field: 'field3',
                    },
                    {
                        field: 'field4',
                    },
                    {
                        field: 'field5',
                    },
                    {
                        field: 'field6',
                    },
                ]}
                rows={[]}
            />,
        );

       // Fails, only three columns render
        expect(within(screen.getAllByRole('row')[0]).getAllByRole('columnheader')).toHaveLength(6);
    });

In the debugger the window has a width of 1024, so idk why this would be such a problem.

Kosurij commented 3 years ago

Is this issue the reason why this test fails?

    it('Renders all columns', async () => {
        render(
            <DataGridPro
                columns={[
                    {
                        field: 'field1',
                    },
                    {
                        field: 'field2',
                    },
                    {
                        field: 'field3',
                    },
                    {
                        field: 'field4',
                    },
                    {
                        field: 'field5',
                    },
                    {
                        field: 'field6',
                    },
                ]}
                rows={[]}
            />,
        );

       // Fails, only three columns render
        expect(within(screen.getAllByRole('row')[0]).getAllByRole('columnheader')).toHaveLength(6);
    });

In the debugger the window has a width of 1024, so idk why this would be such a problem.

It seems like this. Try to add 'autoHeight' props to your DataGridPro component. If problem will gone, then we can confidently say that this is the same problem

m4theushw commented 3 years ago

@JPTeasdale We now have a disableVirtualization prop which you can use to allow tests to see all columns. It's available in the last beta release. There's also the columnBuffer prop which might also be usable if virtualization cannot be disabled.

oliviertassinari commented 3 years ago

I assume the only thing that we miss now on this issue is documentation, and a good chunk of it was done in https://mui.com/components/data-grid/virtualization/#disable-virtualization. Is there something else we could do to close the issue?

bsides commented 2 years ago

disableVirtualization

Is there any tips on how to transform the tested DataGrid into one with disableVirtualization prop only for testing?

jakub-bao commented 2 years ago

:+1: I am running into the same issue. TestingLibrary+JSDom. Experimenting around, I found that columnBuffer does the trick.

ModPhoenix commented 2 years ago

For CRA based src/setupTests.tsx:

import { DataGridProps } from '@mui/x-data-grid';

jest.mock('@mui/x-data-grid', () => {
  const { DataGrid } = jest.requireActual('@mui/x-data-grid');
  return {
    ...jest.requireActual('@mui/x-data-grid'),
    DataGrid: (props: DataGridProps) => {
      return <DataGrid {...props} disableVirtualization />;
    },
  };
});
caedes commented 2 years ago

I ran into the same problem today in a project. @ModPhoenix 's solution works perfectly!

Rafael805 commented 2 years ago

I'm using the following:

render(<DataGrid rows={items} columns={columns} columnBuffer={columns.length} disableVirtualization />);

but my test is still not showing all of the columns.

On the grid it says it has a aria-colcount="5"

tigerabrodi commented 2 years ago

@oliviertassinari @m4theushw @dtassone @samuelsycamore

I'm encountering a similar issue but in my case using the Data Grid Pro, I need to change the aria-label of the checkbox, so that it is unique, we need to include the name in the accessible name if the checkbox.

The issue we're having is passing the name as a prop to baseCheckbox how do we get the name of each row and pass it as a prop?

Or is there another recommended or better approach to this, we want to find the input by the role checkbox and then its name, we're using Cypress and Testing library.

Thanks!

              componentsProps={{
                baseCheckbox: { name: /* I want to pass the name here, how can I do it? */ }
              }}
oliviertassinari commented 2 years ago

I need to change the aria-label of the checkbox, so that it is unique

@narutosstudent Why?

n-junge commented 1 year ago

The problem still exists for me. Neither columnBuffer, disableVirtualization nor autoHeight solve my tests. Not a single column is "rendered" in the tests, despite this output:

<div
  aria-colcount="8"
  aria-multiselectable="false"
  aria-rowcount="3"
  class="MuiDataGrid-root MuiDataGrid-autoHeight MuiDataGrid-root--densityStandard css-15fuvc0-MuiDataGrid-root"
  role="grid"
>
cbush06 commented 1 year ago

The problem still exists for me. Neither columnBuffer, disableVirtualization nor autoHeight solve my tests. Not a single column is "rendered" in the tests, despite this output:

<div
  aria-colcount="8"
  aria-multiselectable="false"
  aria-rowcount="3"
  class="MuiDataGrid-root MuiDataGrid-autoHeight MuiDataGrid-root--densityStandard css-15fuvc0-MuiDataGrid-root"
  role="grid"
>

I'm having this issue. I have a button that, when clicked, adds a row to the DataGrid and places that row into edit mode. The row is added, but it never enters edit mode each cell is only a self-closing DIV.

RafaelAugustScherer commented 1 year ago

@Rafael805 , @n-junge have you tried adding @ModPhoenix's solution? You can add it before the describe sentence, not necessarly in src/setupTests.js. That worked for me!

dnikhila commented 1 year ago

@oliviertassinari @RafaelAugustScherer @m4theushw I Just facing this issue suddently, 3 days back it was working fine with disableVirtualization , autoheight, for V6.1.0 -> but now it was not rendering the rows and columns this is the snap -

 <div
            aria-colcount="4"
            aria-multiselectable="true"
            aria-rowcount="1"
            class="MuiDataGrid-root-bIEPAP kRMoBp MuiDataGrid-root MuiDataGrid-autoHeight MuiDataGrid-root--densityStandard MuiDataGrid-withBorderColor"
            role="grid"
          >
            <div
              class="MuiDataGrid-main-eA-DYQK czLrQD MuiDataGrid-main"
            >
              <div
                class="MuiDataGrid-columnHeaders-clEJFH dhqqwW MuiDataGrid-columnHeaders MuiDataGrid-withBorderColor"
                style="min-height: 56px; max-height: 56px; line-height: 56px;"
              >
                <div
                  class="MuiDataGrid-columnHeadersInner-geCCWJ cGbfoc MuiDataGrid-columnHeadersInner"
                  role="rowgroup"
                />
              </div>
            </div>
            <div
              class="MuiDataGrid-footerContainer-daqAnH evekWm MuiDataGrid-footerContainer MuiDataGrid-withBorderColor"
            >
              <div />
            </div>

I was using datagridpro in parent component which won't accepts any props, it takes directly from parent component. tried using @ModPhoenix solution but still same.


 <DataGridPro
          disableVirtualization
          disableColumnMenu
          autoHeight
          rows={searchValue ? searchResults : users}
          columns={columns}
          disableRowSelectionOnClick
          hideFooterRowCount
          sx={dataGridStyles}
          slots={{
            columnSortedAscendingIcon: SortingIcon,
            columnSortedDescendingIcon: SortingIcon,
          }}
          sortingOrder={["asc", "desc"]}
        />

columns --
 const columns: GridColDef[] = [
    {
      field: "name",
      headerName: "Name",
      flex: 1,
    },
    {
      field: "status",
      headerName: "Status",
      flex: 1,
      sortable: false,
      renderCell: (params: GridCellParams) => {
        const rowData: UserData = params.row;
        const isChecked: boolean = rowData.is_active;
        return (
          <Box
            data-testid="update-status"
            onClick={() => {
              handleStatus(rowData.id as string, rowData.is_active);
            }}
          >
            <Switch checked={isChecked} />
          </Box>
        );
      },
    },
    {
      field: "role",
      headerName: "Role",
      flex: 1,
      renderCell: (params: GridCellParams) => {
        const rowData: UserData = params.row;
        return `${rowData.role.role_name}`;
      },
    },
    {
      field: "actions",
      headerName: "",
      flex: 1,
      sortable: false,
      align: "right",
      renderCell: (params: GridCellParams) => {
        const rowData: UserData = params.row;
        return (
          <div style={{ display: "flex", justifyContent: "flex-end", paddingRight: "1rem" }}>
            <EditUserIconDiv
              data-testid="editRow"
              onClick={() => {
                navigate(`edit-user/${rowData.id}`);
                handleEdit(rowData);
              }}
            >
              <EditIcon />
            </EditUserIconDiv>
            <DeleteUserIconDiv
              data-testid="delete-user"
              onClick={() => {
                setSelectedUser(rowData);
                handleDeleteModal(true, "delete");
              }}
            >
              <DeleteIcon />
            </DeleteUserIconDiv>
          </div>
        );
      },
    },
  ];
  rows---
[
      {
        id: '1',
        first_name: 'John',
        last_name: 'Doe',
        email: '',
        role_id: '1',
        role: { id: '1', role_name: 'Admin' },
        title: '',
        is_active: true,
        is_deleted: false,
        facility_id: '1',
        user_id: '1',
        name: 'John  Doe'
      },
      {
        id: '2',
        first_name: 'testing',
        last_name: 'Doe',
        email: '',
        role_id: '2',
        role: { id: '2', role_name: 'Admin' },
        title: '',
        is_active: false,
        is_deleted: false,
        facility_id: '1',
        user_id: '1',
        name: 'testing  Doe'
     }]

Test case--

jest.mock("@mui/x-data-grid-pro", () => ({ ...jest.requireActual("@mui/x-data-grid-pro"), }));

test("Edit row user management", async () => { const tree = renderWithProviders(

, { store } ); expect(tree).toMatchSnapshot(); const handleSearch = screen.getByTestId("content-input"); fireEvent.change(handleSearch, { target: { value: "testing" }, }); await waitFor(async () => { expect(fireEvent.keyPress(handleSearch, { key: "Ent", code: 13, charCode: 13 })).toBe(true); }); await waitFor(async () => { const handleEdit = screen.getAllByTestId("editRow"); expect(fireEvent.click(handleEdit[0])).toBe(true); }); });
gdams commented 1 year ago

@dnikhila I've observed the same issue here: https://github.com/adoptium/adoptium.net/pull/1758

gdams commented 1 year ago

@oliviertassinari is there any update here? this has been broken for 3 weeks now

gdams commented 1 year ago

Actually, this seems to be fixed in 6.4.0 by https://github.com/mui/mui-x/pull/8968

luizeboli commented 1 year ago

This seems to be partial fixed by #8968

Just some columns are rendered in my case, could not figure why

Matt-Tranzact commented 1 year ago

Still not working with RTL + Jest

import React from 'react'
import { render } from '@testing-library/react'
import { DataGrid } from '@mui/x-data-grid'

describe('Table Component', () => {
  describe('Rendering', () => {
    it('should match snapshot', () => {
      expect(
        render(
          <DataGrid
            checkboxSelection
            columns={[]}
            pageSizeOptions={[5]}
            paginationModel={{ page: 1, pageSize: 5 }}
            rows={[]}
          />
        )
      ).toMatchSnapshot()
    })
  })
})

image

image

dep commented 1 year ago

@Matt-Tranzact

Still not working with RTL + Jest

Thanks for reporting this. Please consider pasting your error message as text instead of images so folks Googling the same error message can find pages like these. 🙏🏻

TrueWill commented 1 year ago

I'm seeing valueFormatter columns rendering but valueGetter columns not. The getter doesn't even fire.

agersea commented 1 year ago

I was also having trouble writing tests. Using renderCell seems to be the culprit as the tests pass when I remove all of the column definitions that use this function.

https://github.com/mui/mui-x/issues/1151#issuecomment-1108349639 resolved the issue.

vileppanen commented 1 year ago

For CRA based src/setupTests.tsx:

import { DataGridProps } from '@mui/x-data-grid';

jest.mock('@mui/x-data-grid', () => {
  const { DataGrid } = jest.requireActual('@mui/x-data-grid');
  return {
    ...jest.requireActual('@mui/x-data-grid'),
    DataGrid: (props: DataGridProps) => {
      return <DataGrid {...props} disableVirtualization />;
    },
  };
});

Had issues with rendering all the columns in the grid. The disableVirtualization did the trick in my case, had a client bootstrapped with Vite.

PrimaMateria commented 11 months ago

For me, it worked only after I used mockResizeObserver as described here: https://stackoverflow.com/a/76571162/1209448

lanceheld commented 10 months ago

I'm using autoPageSize so when I'm testing for rows, I set a testingRows prop to true in order to get things to work: <DataGrid autoPageSize={!testingRows} autoHeight={testingRows} />

frankPairs commented 7 months ago

hey everyone! I am experiencing the same issue after updating the data-grid package from version 6.19.10 to7.3.1.

More concretely, upgrading the version made a test faling. This test is executing some filtering and checking if the rows are filtered correctly. The problem is that the rowCount is showing a number, but not all of the rows are being rendered on the tests.

I tried the solution proposed by @ModPhoenix of adding the disableVirtualization property for testing but it did not worked.

I am using the next react testing library versions:

    "@testing-library/jest-dom": "6.4.2",
    "@testing-library/react": "14.3.1",
    "@testing-library/user-event": "14.5.2",

Any thoughts on how can be fixed?

Thanks a lot in advance!

mchatrola commented 3 months ago

Hi, We are having exact same issue. Only header of first 3 columns are rendering. I tried suggested solutions above but they are not working. We are using MUIDataTable with vitest. Here is the mock function we are using vi.mock('mui-datatables', async () => { const actual = await vi.importActual('mui-datatables') return { ...actual, MUIDataTable: (props) => { return ( <MUIDataTable {...props} autoHeight disableVirtualization /> ); }, }; }); Not sure what is wrong with this mock. Thanks