6

I'm trying to test the generated class of a component that uses React CSS modules

Here is a sample Foo component:

import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './Foo.css';

const Foo = ({ className })  => <div styleName={className} />;

Foo.propTypes = {
  className: React.PropTypes.string.isRequired,
};

export default CSSModules(Foo, styles, { allowMultiple: true, errorWhenNotFound: false });

Test code (Foo.spec.jsx):

import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import Foo from '../../../app/components/Foo';

describe('<Foo/>', () => {
    it.only('should return a className prop as class value', () => {
      const wrapper = shallow(<Foo className="bar" />);
      console.log('DEBUG----->', wrapper.html());
    });
});

Foo.css code:

.bar {
  background-color: red;
}

And here is the test html output:

DEBUG-----> <div></div>

As you can see it generates an empty div without any class, so no assertion works.

Also I would like to bring up the fact that this only fails in the test, the component generates the expected html class in the web app generated html code.

Any idea how to fix this?

UPDATE

As suggested in the comment by fabio I've tried to remove the option errorWhenNotFound: false

Now the test gives the following error message:

Error: "bar" CSS module is undefined.

Which I don't understand since the bar class is defined in the Foo.css file.

Also I've found that if I debug the styles in the Foo component:

import styles from './Foo.css';
console.log('STYLES', styles);

It returns an empty output:

STYLES {}

Although both in this simplified example and in the full fledged code the component generates the expected html class in the web app generated html code with the corresponding styles working fine.

4
  • 1
    If you're using webpack, do you have a test-specific config/loaders that ignores the imported css files? It may be that CSSModules doesn't find that class name and the error is ignored by errorWhenNotFound: false (which you could also try to remove that to help debugging the issue). Commented Oct 10, 2016 at 19:16
  • 1
    So now we at least know why you don't see the class in the output. I still think you have some test related config or code that ignores the content of the css file, something like this: npmjs.com/package/ignore-styles which is pretty common to use in tests. Can you post you full webpack config you use for tests? Are you using mocha as a test runner? Are you trying to do anything related to this github.com/airbnb/enzyme/issues/202? Commented Oct 11, 2016 at 8:51
  • I think you hit the jackpot!. Mocha was being executed with the ignore-styles option, now that this is fixed, the following error message is being generated: SyntaxError: /var/www/web/app/components/Icon.css: Leading decorators must be attached to a class declaration But at least it's a different problem now. Commented Oct 11, 2016 at 10:08
  • Glat to help, I added an answer with some more details. Commented Oct 11, 2016 at 10:24

2 Answers 2

11
+100

As the OP stated in the comments, the problem is that Mocha was running with the ignore-styles option. That, together with the errorWhenNotFound: false option was masking the actual problem, which is that the style object exported by the module is actually empty, the module being ignored by Mocha.

Without that option on, you still need to instruct Mocha to import the CSS module and to parse it correctly. The /var/www/web/app/components/Icon.css: Leading decorators must be attached to a class declaration error is probably due to the fact that Mocha tries to interpret the content of the css file as js, which obviously fails.

To properly set things up, have a look here: https://gist.github.com/mmrko/288d159a55adf1916f71

The idea is to do something like this:

var hook = require('css-modules-require-hook')
var sass = require('node-sass')

hook({
  extensions: [ '.scss', '.css' ],
  generateScopedName: '[local]___[hash:base64:5]',
  preprocessCss: ( data, file ) => sass.renderSync({ file }).css
})

in your Mocha entry file (before you do anything else), to you tell it how to handle the css/sass files.

You can put the content of that gist inside a test/setup.js file for example, and then run Mocha in this way:

mocha --compilers js:babel-register --require ./test/setup.js \"./test/**/*.spec.js\"

In this way, your setup.js initialisation code will run before the tests.

Sign up to request clarification or add additional context in comments.

3 Comments

what is mocha entry?
@angrykiwi I mean the file you require with mocha before you run your actual tests (hence "entry" as in "entry point"). See the last command in my post, I pass two file paths to the --require flag. The first is what I called "entry", the second is a glob to include all the actual test files. You want the code in setup.js to be run before anything else so you can instruct mocha how to compile the sass files required by your code under test.
do you have example of fully integrated react && css module and testing with mocha?
0

Thanks for the answer! If you're not concerned with SASS you can also just do this...

import hook from 'css-modules-require-hook'

hook({
  generateScopedName: '[local]___[hash:base64:5]',
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.