Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helper to attach a stylesheet to the document #70

Closed
gnapse opened this issue Nov 2, 2018 · 8 comments
Closed

Helper to attach a stylesheet to the document #70

gnapse opened this issue Nov 2, 2018 · 8 comments
Labels
help wanted Extra attention is needed needs discussion We need to discuss this to come up with a good solution

Comments

@gnapse
Copy link
Member

gnapse commented Nov 2, 2018

Describe the feature you'd like:

This is prompted by this question on Twitter:

does toBeVisible in jest-dom take styles from CSS files into account? I'm checking a component that has a class that adds display: none; but expect(component).not.toBeVisible() is failing because it thinks the component is visible.

The problem, as I pointed out responding that tweet, is that when testing isolated components, css stylesheets that would normally be attached to the document via a <link /> element in the head area, are not attached to the virtual jsdom document in which the tests are being run.

Suggested implementation:

I'm not sure how this would be. Especially with css-in-js and css-modules in the picture, I'm not even sure if there's still a framework-agnostic way to automatically handle what these better css systems do with the css to dynamically generate a stylesheet.

Keeping it concrete, only considering stylesheets in general, I'm not even sure if we can make a helper load a css file. All I know is that we can attach css to the document having the actual css code as a string, as we already do here.

We can simplify the above providing some kind of helper to attach styles and run assertions with those styles on. It has to be some kind of wrapper that gives us the opportunity to clean up, because as you see in that test I linked to, the style element has to be detached and cleaned up after the test (something we're not even doing consistently in all those test cases 😱).

So at the very least I can come up with something like this:

const stylesheet = `
  .hidden { display: none }
`

// ...

test('...', () => {
  withStyles(stylesheet, () => {
    // ...
    expect(element).not.toBeVisible();
  });
});

Describe alternatives you've considered:

We can keep things as they are, but at the very least we need to document this better in the README.

If we keep things as they are, we can tell people to assert against the element having the css class name or not, but that kinda goes against the guiding principle. Because end users of web apps do not really care about the element having the class or not, but wether it is visible or not. Furthermore, with the popularity of css-in-js or even css-modules, people cannot rely on static css class names anymore.

Teachability, Documentation, Adoption, Migration Strategy:

TODO

@gnapse gnapse added help wanted Extra attention is needed needs discussion We need to discuss this to come up with a good solution labels Nov 2, 2018
@kentcdodds
Copy link
Member

I don't think this is a good idea for this library. I think it's the responsibility of the application developer to figure out how to load they're styles into the document. This feels pretty out of scope IMO

@gnapse
Copy link
Member Author

gnapse commented Nov 6, 2018

Ok, fair enough. As I said, we can keep things as they are, that was my go to solution anyway. But at the very least we should document it. I'll do that. Thanks for your input and for validating what I was thinking.

@quangta93
Copy link

quangta93 commented Dec 21, 2018

@gnapse @kentcdodds I ran into the same issue and can't find how to get the stylesheet from CSS module files like the example above. Here's an example of my test and what i'm trying to do:

// App.js
import React from 'react'
import styles from './Vis.module.scss';

export const App = () => (
  <div data-testid="app" className={styles.app}>Something here</div>
);

// styles.module.scss
.app {
  display: none;
}

// App.test.js
import React from 'react';
import { render } from 'react-testing-library';
import { App } from './App';
import styles from './App.module.scss';


test('App is invisible', () => {
  const { getByTestId, container } = render(<App />);

  // trying to inject css into test document
  const style = document.createElement('style');

  // Not sure what to put here. "styles" is an object
  // and "styles.app" is just a class name, not the real style attributes.
  style.innerHTML = ???;

  document.body.appendChild(style);
  document.body.appendChild(container);

  expect(getByTestId('app')).not.toBeVisible();

  document.body.removeChild(style);
  document.body.removeChild(container);
});

Any suggestions?

@kentcdodds
Copy link
Member

I really don't know of any tool to enable this and believe this would be pretty nontrivial to build. Definitely possible and very useful! But there's no way to do this today...

@gnapse
Copy link
Member Author

gnapse commented Aug 17, 2019

I'm closing this for now. Feel free to reopen if something actionable arises from all this.

@gnapse gnapse closed this as completed Aug 17, 2019
@dpkshrma
Copy link

dpkshrma commented Oct 4, 2020

I created a simple utility function for my usecase. might come in handy for those coming here looking for a solution:

import _ from 'lodash'
import { render as rtlRender } from '@testing-library/react'
import sass from 'node-sass'
import { FunctionComponent, ReactElement } from 'react'

type RenderOptions = {
  wrapper?: FunctionComponent | undefined
  stylesheet?: string
}

function render(ui: ReactElement, options: RenderOptions = {}) {
  const view = rtlRender(ui, {
    ..._.omit(options, 'stylesheet'),
  })
  if (options.stylesheet) {
    const styles = sass.renderSync({ file: options.stylesheet })

    const styleElement = document.createElement('style')
    styleElement.innerHTML = styles.css.toString()
    document.body.appendChild(styleElement)
    document.body.appendChild(view.container)
  }

  return view
}

export * from '@testing-library/react'
// override React Testing Library's render with our own
export { render }

@Abusayid693
Copy link

@dpkshrma Did you tested if it works because Ignored nodes: comments, <script />, <style /> this is displayed while running test

@balanza
Copy link

balanza commented Jun 19, 2024

Thanks for everyone who has commented, I was looking for a solution for the very same problem. Here our solution, if of any help: trento-project/web#2701

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed needs discussion We need to discuss this to come up with a good solution
Projects
None yet
Development

No branches or pull requests

6 participants