glideapps / glide-data-grid

🚀 Glide Data Grid is a no compromise, outrageously react fast data grid with rich rendering, first class accessibility, and full TypeScript support.
https://grid.glideapps.com
MIT License
4.07k stars 301 forks source link

Render glide-data-grid in jsdom tests #905

Open johnkim-det opened 9 months ago

johnkim-det commented 9 months ago

I want to write tests for a component that uses glide-data-grid, in a project using vitest.

As a proof of concept, I'm creating a test that only renders a basic example grid, adapting the testing strategy I see in glide-data-grid/packages/core/test.

I'm running into an issue where the grid itself does not seem to be rendering, though the wrapper divs and another empty canvas element does.

I'm using vitest-mock-canvas for canvas support, which seems to work fine for other simple canvas tests (and the empty canvas element does seem to render without errors). I'm also using the wrapper option to render a "portal" div.

Just wondering if there are any suggestions for what I could try to make this work. Perhaps something I'm missing that made this strategy work for the internal library tests? Thanks.

Test code:

import {
  DataEditor,
  GridCell,
  GridCellKind,
  GridColumn,
  Item,
} from '@glideapps/glide-data-grid';
import { act, render, screen } from '@testing-library/react';

function prep(resetTimers: boolean = true) {
  const scroller = document.getElementsByClassName('dvn-scroller').item(0);
  if (scroller !== null) {
    vi.spyOn(scroller, 'clientWidth', 'get').mockImplementation(() => 1000);
    vi.spyOn(scroller, 'offsetWidth' as any, 'get').mockImplementation(() => 1000);
    vi.spyOn(scroller, 'clientHeight', 'get').mockImplementation(() => 1000);
    vi.spyOn(scroller, 'offsetHeight' as any, 'get').mockImplementation(() => 1000);
  }

  act(() => {
    vi.runAllTimers();
  });
  if (resetTimers) {
    vi.useRealTimers();
  }

  return scroller;
}

const Context: React.FC = (p) => {
  return (
    <>
      {p.children}
      <div id="portal" />
    </>
  );
};

const setup = () => {
  const data = [
    {
      firstName: 'John',
      lastName: 'Doe',
    },
    {
      firstName: 'Maria',
      lastName: 'Garcia',
    },
    {
      firstName: 'Nancy',
      lastName: 'Jones',
    },
    {
      firstName: 'James',
      lastName: 'Smith',
    },
  ];

  // Grid columns may also provide icon, overlayIcon, menu, style, and theme overrides
  const columns: GridColumn[] = [
    { title: 'First Name', width: 100 },
    { title: 'Last Name', width: 100 },
  ];

  // If fetching data is slow you can use the DataEditor ref to send updates for cells
  // once data is loaded.
  function getData([col, row]: Item): GridCell {
    const person = data[row];

    if (col === 0) {
      return {
        allowOverlay: false,
        data: person.firstName,
        displayData: person.firstName,
        kind: GridCellKind.Text,
      };
    } else if (col === 1) {
      return {
        allowOverlay: false,
        data: person.lastName,
        displayData: person.lastName,
        kind: GridCellKind.Text,
      };
    } else {
      throw new Error();
    }
  }
  render(
    <DataEditor columns={columns} getCellContent={getData} rows={data.length} />,
    { wrapper: Context },
  );
};
describe('grid', () => {
  it('should render', () => {
    vi.useFakeTimers();
    setup();
    prep();
    screen.getByTestId('data-grid-canvas');
  });
});

Output:

TestingLibraryElementError: Unable to find an element by: [data-testid="data-grid-canvas"]

Ignored nodes: comments, script, style
<body>
  <div>
    <div
      class="mocked-styled-8 gdg-wmyidgi"
      style="--wmyidgi-0: 1000px; --wmyidgi-1: 1000px; --gdg-accent-color: #4F5DFF; --gdg-accent-fg: #FFFFFF; --gdg-accent-light: rgba(62, 116, 253, 0.1); --gdg-text-dark: #313139; --gdg-text-medium: #737383; --gdg-text-light: #B2B2C0; --gdg-text-bubble: #313139; --gdg-bg-icon-header: #737383; --gdg-fg-icon-header: #FFFFFF; --gdg-text-header: #313139; --gdg-text-group-header: #313139BB; --gdg-text-header-selected: #FFFFFF; --gdg-bg-cell: #FFFFFF; --gdg-bg-cell-medium: #FAFAFB; --gdg-bg-header: #F7F7F8; --gdg-bg-header-has-focus: #E9E9EB; --gdg-bg-header-hovered: #EFEFF1; --gdg-bg-bubble: #EDEDF3; --gdg-bg-bubble-selected: #FFFFFF; --gdg-bg-search-result: #fff9e3; --gdg-border-color: rgba(115, 116, 131, 0.16); --gdg-horizontal-border-color: rgba(115, 116, 131, 0.16); --gdg-drilldown-border: rgba(0, 0, 0, 0); --gdg-link-color: #4F5DFF; --gdg-cell-horizontal-padding: 8px; --gdg-cell-vertical-padding: 3px; --gdg-header-font-style: 600 13px; --gdg-base-font-style: 13px; --gdg-marker-font-style: 9px; --gdg-font-family: Inter, Roboto, -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Ubuntu, noto, arial, sans-serif; --gdg-editor-font-size: 13px;"
    >
      <div />
    </div>
    <div
      id="portal"
    />
  </div>
</body>