gregnb / mui-datatables

Datatables for React using Material-UI
MIT License
2.69k stars 932 forks source link

Jest & ReactTestingLibrary - Could not parse CSS stylesheet #1886

Open liamlows opened 2 years ago

liamlows commented 2 years ago

Hey y'all,

So we have been going through and adding more tests to some of the components we are using in our app and for some reason every component/page that uses muidatatables throws an error during testing. It does not fail the test but simply spits out the error. It goes as follows:

console.error
  Error: Could not parse CSS stylesheet
      at exports.createStylesheet (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js:34:21)
      at HTMLStyleElementImpl._updateAStyleBlock (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/HTMLStyleElement-impl.js:68:5)
      at HTMLStyleElementImpl._childTextContentChangeSteps (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/HTMLStyleElement-impl.js:36:12)
      at HTMLStyleElementImpl._insert (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:832:14)
      at HTMLStyleElementImpl._preInsert (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:768:10)
      at HTMLStyleElementImpl._append (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:868:17)
      at HTMLStyleElementImpl.appendChild (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:610:17)
      at HTMLStyleElement.appendChild (<MY_USER_PATH>/.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/generated/Node.js:395:60)
      at StyleSheet.insert (<MY_USER_PATH>/.yarn/cache/@emotion-sheet-npm-1.1.0-40e9c90e06-a4b74e16a8.zip/node_modules/@emotion/sheet/dist/emotion-sheet.cjs.dev.js:135:11)
      at Array.finalizingPlugins (<MY_USER_PATH>/.yarn/cache/@emotion-cache-npm-11.7.1-82b45442ee-cf7aa8fe3b.zip/node_modules/@emotion/cache/dist/emotion-cache.cjs.dev.js:296:24) .tss-swqr74-MUIDataTable-@global{@media print{.datatables-noprint{display:none;}}}

  at VirtualConsole.<anonymous> (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/virtual-console.js:29:45)
  at exports.createStylesheet (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js:38:63)
  at HTMLStyleElementImpl._updateAStyleBlock (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/HTMLStyleElement-impl.js:68:5)
  at HTMLStyleElementImpl._childTextContentChangeSteps (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/HTMLStyleElement-impl.js:36:12)
  at HTMLStyleElementImpl._insert (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:832:14)
  at HTMLStyleElementImpl._preInsert (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:768:10)
  at HTMLStyleElementImpl._append (.yarn/__virtual__/jsdom-virtual-f91bf4c0c4/0/cache/jsdom-npm-16.7.0-216c5c4bf9-454b833718.zip/node_modules/jsdom/lib/jsdom/living/nodes/Node-impl.js:868:17)

we have tried a plethora of solutions to try and fix this issue and none work. Could this be due to Next 12 using the rust compiler as opposed to babel in the past? we have tried manually mocking stylesheets, manually transforming stylesheets and media files, using identity-obj-proxy, and a few other random attempts at fixing.

Here is a sample test we have tried, CacheProvider is an emotion cache provider, DarkThemeProvider provides a theme for muidatatable to use, and assume the correct props are provided to the table to render:

it('should render a table with default content', () => {
    render(
      <CacheProvider value={emotionCache}>
        <DarkThemeProvider>
          <MUIDataTable title={tableTitle} data={data} columns={columns} options={options} />
        </DarkThemeProvider>
      </CacheProvider>,
    );
    expect(screen.getByText('row 1 col 1')).toBeInTheDocument();
    expect(screen.getByText('row 1 col 2')).toBeInTheDocument();
});

Expected Behavior

Does not throw could not parse stylesheet error during testing.

Current Behavior

Throws could not parse stylesheet error during testing, despite what seems to be correct testing configuration.

Steps to Reproduce (for bugs)

  1. Using next.js, jest, ts-jest, and react testing library (set up as directed https://nextjs.org/docs/testing) create a test using a muidatatable.
  2. Run the testing library and observe the error.

Your Environment

Tech Version
Material-UI 5.4.3
MUI-datatables 4.1.2
React 17.0.2
Next 12.1,0
Jest 27.5.1
ts-jest 27.1.3
@testing-library/react 12.1.3
@testing-library/jest-dom 5.16.2
nickraphael commented 2 years ago

Also getting this issue suddenly.

aaneitchik commented 2 years ago

Getting the same error, but without using Next.js. I am using emotion though because of the MUI v5, and since the error is coming from @emotion/cache, I'm guessing that's the issue here.

Looking at the stack trace, the error originates node_modules/jsdom/lib/jsdom/living/helpers/stylesheets.js when trying to do sheet = cssom.parse(sheetText);. Seems like there could be issues in cssom related to @media print: https://github.com/NV/CSSOM/issues/110, as this is where the error fails based on the stacktrace: .tss-swqr74-MUIDataTable-@global{@media print{.datatables-noprint{display:none;}}} (@global in the selector kind of looks suspicious to me as well)

The issue happens to me when trying to update from v4.0.0 to 4.1.0. To me it looks like the biggest change was switching to tss-react, so maybe the styles that it outputs are different and cssom cannot handle it.

Don't really know how this should be dealt with, but maybe this would help with investigating 🙂

aaneitchik commented 2 years ago

To follow up with a bit more investigation: I decided to try and see how the CSS that cssom is trying to parse looks like with both versions (the original styles are here), and here's what I got: mui-datatables@4.0.0 (no tss-react):

.MUIDataTable-paperResponsiveScrollFullHeightFullWidth-42 {
  position: absolute;
}
.MUIDataTable-tableRoot-43 {
  outline: none;
}
.MUIDataTable-responsiveBase-44 {
  overflow: auto;
}
@media print {
  .MUIDataTable-responsiveBase-44 {
    height: auto !important;
  }
}
.MUIDataTable-responsiveScroll-45 {
  height: 100%;
  overflow: auto;
}
.MUIDataTable-responsiveScrollMaxHeight-46 {
  height: 100%;
  overflow: auto;
}
.MUIDataTable-responsiveScrollFullHeight-47 {
  height: 100%;
}
.MUIDataTable-responsiveStacked-48 {
  overflow: auto;
}
@media (max-width:899.95px) {
  .MUIDataTable-responsiveStacked-48 {
    overflow: hidden;
  }
}
.MUIDataTable-caption-50 {
  left: -3000px;
  position: absolute;
}
.MUIDataTable-liveAnnounce-51 {
  clip: rect(0 0 0 0);
  width: 1px;
  border: 0;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  position: absolute;
}
@media print {
  .datatables-noprint {
    display: none;
  }
}

Notice how the last @media rule, which in the original is defined under @global key, is just in the stylesheet, there's no class name generated for it

vs.

mui-datatables@4.1.0 (with tss-react):

.tss-swqr74-MUIDataTable-@global{@media print{.datatables-noprint{display:none;}}}

Not sure why the stylesheet contains only one rule, but I assume it doesn't really matter.

What I assume matters here is that a class name is created with @global in it, which doesn't look correct, but I may be lacking some context of course.

x3igh7 commented 2 years ago

Any ideas for a work around? I tried downgrading to mui-datatables@4.0.0, which helped some but that seemed to cause other issues.

EDIT: Poked around a little more and it seems like an out of date jsdom version may be to blame?

garronej commented 2 years ago

Hi @aaneitchik, I am the maintainer of tss-react, let me know if there is something I can do on my end to smooth things up.

aaneitchik commented 2 years ago

Hi @garronej , thank you for reaching out! I made a PR that should fix the issue for mui-datatables. Since the problem seems to be in @global style, I rewrote the styles to remove it.

I don't know if @global is a "special" style that should be supported and handled separately by tss-react (like for example how @media queries are), since it doesn't seem to be one of CSS at-rules. To me it looks like it shouldn't, so nothing to do on tss-react's side, but in case I'm wrong then I guess supporting @global would also solve it.

cagosto commented 2 years ago

I am having the same issue and I am not using Next.js I have done this as temp work around.

jest.spyOn(console, 'error').mockImplementation(() => {});
filipomar commented 1 year ago

I encountered this issue as well on 4.2.2. Here is the (typescript eslint ready) workaround I put together:

jest.mock('tss-react/mui', () => {
    const real: typeof import('tss-react/mui') = jest.requireActual('tss-react/mui')

    return {
        ...real,
        withStyles: (...args: unknown[]) => {
            /** Making sure we are not targeting the wrong rule */
            if (
                args.length !== 3 ||
                typeof args[1] !== 'function' ||
                args[1].name !== 'defaultTableStyles' ||
                !args[2] ||
                typeof args[2] !== 'object' ||
                !('name' in args[2]) ||
                (args[2] as Record<string, unknown>).name !== 'MUIDataTable'
            ) {
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                return real.withStyles(...args)
            }

            const [component, styleGenerator, config] = args

            return real.withStyles(
                // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                // @ts-ignore
                component,
                (...args: unknown[]) => {
                    const styles: unknown = styleGenerator(...args)

                    /** Making sure we are deleting the right styles */
                    if (styles && typeof styles === 'object' && '@global' in styles) {
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-ignore
                        delete styles['@global']
                    }

                    return styles
                },
                config
            )
        }
    }
})

Put it before your tests that involve this library and the bad styles complaints should go away. This could have other side effects but its good enough for my usecase and brittle on purpose.

garronej commented 1 year ago

Hi @aaneitchik,

Sorry your answer went under my radar for some reason.

I don't know if @global is a "special" style that should be supported and handled separately by tss-react (like for example how @media queries are)

You are correct, thank for clearing this out for me, I add to my backlog to support @media. I wasn't aware the legacy API supported that.

@filipomar thanks for sharing a workaround.

Best,

Npervic commented 1 year ago

Still experiencing this in "mui-datatables": "^4.2.2" any idea if this will get resolved?

garronej commented 1 year ago

@Npervic, I've open an issue about it.
But the mainteainer of mui-datatables don't review PRs very often and I'm not mainteaner.
I'll do what I can but I encourage you to try to find a fix on your end.

Best

liamlows commented 1 year ago

@wdh2100 anyway we could get these PRs merged in? looks like they are GTG.

Npervic commented 1 year ago

@wdh2100 Any way we can get the above mentioned pr merged?

jonnyeom commented 3 weeks ago

Im still seeing this issue with "@global" styles. Any new finds recently?