Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

testRegex mask picks wrong files to run tests

Update on 2018/07/13:

After hacking around, I figured out why the testRegex didn't work. See my own answer posted below.


Original Question:

I am writing the test code for my React.js project. I'm using react-scripts and enzyme (which I believe implies jest as well) to run and manage the tests. Here is the package.json file:

{
  "name": "front",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "~15.5.4",
    "react-dom": "~15.5.4",
    "react-scripts": "0.9.5",
    "redux": "~3.6.0",
    "react-redux": "~5.0.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "enzyme": "^3.3.0",
    "enzyme-adapter-react-15": "^1.0.6",
    "react-test-renderer": "~15.5.4"
  },
  "jest": {
    "testRegex": "(/abc/.*|(\\.|/)(defg|spec))\\.jsx?$"
  }
}

After npm install, I checked the versions:

  • [email protected]
  • [email protected]
  • [email protected]

(Yeah, I know I'm using an older version of React.js but that's what I can't change right now because it is not my personal project.)

What I don't understand is the testRegex of jest doesn't work as I expect. Currently, I put all my test files under __tests__ folder and name them as "something.test.js". However, if you look at the testRegex above, firstly, it doesn't look for __tests__ but abc as the folder name and, secondly, it doesn't look for test but defg as the file name.

Because of the configuration above, I was expecting my test files wouldn't be executed at all. However, when I ran npm test, the test files were still executed.

What did I do wrong?

Some of my thoughts:

  • [email protected] may not support the testRegex.
  • I may need to configure jest via react-scripts but its user guide seems to show that I can just configure jest the way I'm doing (putting a "jest" section in package.json).
  • I also tried to use the latest jest v23.3, but it still didn't work as I expected.
  • I am aware of testMatch, but the documentation says "note that you cannot specify both options", so I am assuming that if I have specified testRegex in package.json, testMatch is ignored. Is this true?
like image 254
yaobin Avatar asked Dec 04 '25 23:12

yaobin


2 Answers

After hacking around, I realized it is because testRegex cannot be overridden if you use react-scripts(note it is the link to the tag v0.9.5) to run the tests, in either the older or the latest versions (By the time I posted this answer, the latest version of react-scripts was 1.1.4).

Here are more details.

In my original question above, I was using react-scripts v0.9.5. When I ran CI=true npm test to run the test, it was react-scripts/scripts/test.js that was actually run. This is a wrapper script that eventually still calls jest. In this line, it creates Jest configuration and passes into jest as its --config options:

argv.push('--config', JSON.stringify(createJestConfig(
  relativePath => path.resolve(__dirname, '..', relativePath),
  path.resolve(paths.appSrc, '..'),
  false
)));

Tracking down the createJestConfig function in this file, you'll see it creates some of the Jest configuration items and returns them as a JavaScript object.

Therefore, jest (I'm using 18.1.0) is eventually run with a --config option that has a stringified JSON string.

If you trace down jest.run, it eventually comes to this runCli function(this is a link to v18.1.0) which reads the configuration here, and this readConfig function is imported from jest-config module.

Then let's look at the jest-config module. The aforesaid readConfig is defined in this file.

The readConfig function calls readRawConfig and then includes the configurations set from argv by calling setFromArgv:

const readConfig = (argv: any, packageRoot: string) =>
  readRawConfig(argv, packageRoot)
    .then(config => Object.freeze(setFromArgv(config, argv)));

Here is the readRawConfig implementation with my comments (preceded with "ywen") about what the lines do:

const parseConfig = argv => {
  if (argv.config && typeof argv.config === 'string') {
    // If the passed in value looks like JSON, treat it as an object.
    if (argv.config[0] === '{' && argv.config[argv.config.length - 1] === '}') {
      return JSON.parse(argv.config);
    }
  }
  return argv.config;
};

const readRawConfig = (argv, root) => {
  // [ywen]
  // Calls the `parseConfig` above. Remember that react-scripts's
  // test.js passes in the configuration as a stringified JSON
  // string, so the "If the passed in value looks like JSON..."
  // test is met. The stringified JSON string is read and parsed
  // back to a JavaScript object, returned, and stored in the
  // rawConfig variable.
  const rawConfig = parseConfig(argv);

  if (typeof rawConfig === 'string') {
    return loadFromFile(path.resolve(process.cwd(), rawConfig));
  }

  if (typeof rawConfig === 'object') {
    // [ywen]
    // Because `rawConfig` is a JS object, this if branch is
    // executed. The function returns with the normalized
    // configuration, so the `loadFromPackage` function, which
    // reads the `jest` configuration from the `package.json`,
    // is **NOT** called.
    const config = Object.assign({}, rawConfig);
    config.rootDir = config.rootDir || root;
    return Promise.resolve(normalize(config, argv));
  }

  return loadFromPackage(path.join(root, 'package.json'), argv).
  then(config => {
    console.log('package.json config: ', config);
    config || normalize({ rootDir: root }, argv)
  });
};

The setFromArgv overrides the configuration with those provided on the argv, but testRegex is NOT one of them.

If you provide your own --config on the command line such as CI=true npm test -- --config ./jest.config.js /path/to/your/test/file.js, there will be two --config items added to the argv passed to jest: one is provided by you, the other is (secretly) created by react-scripts's createJestConfig. They are grouped as a list in argv.config:

  config: 
   [ './jest.config.js',
     '{"collectCoverageFrom":["src/**/*.{js,jsx}"],...'
   ],

When the parseConfig is called by readRawConfig, the if (argv.config && typeof argv.config === 'string') { test fails because the passed in value is a list, not a string. As a result, the parseConfig returns this list as-is. This list is then stored in rawConfig. The type of rawConfig is still object. But in the line const config = Object.assign({}, rawConfig);, it is transformed into something as follows:

{ '0': './jest.config.js',
  '1': '{"collectCoverageFrom":["src/**/*.{js,jsx}"], ...
}'

which will finally fail at this line because 0 or 1 is not a valid Jest configuration.

As a result, when using react-scripts v0.9.5, its test script does not support the customization of testRegex.

This is still the case in react-scripts v1.1.4 which is the latest version by the time of this writing. The createJestConfig.js: it allows some of the keys to be overridden, but testRegex is still not one of them.

like image 124
yaobin Avatar answered Dec 06 '25 13:12

yaobin


try this: create a separate file named jest.config.js

module.exports = {

  coverageDirectory: "coverage",

  moduleFileExtensions: [
   "ts",
   "tsx",
   "js"
  ],

  testEnvironment: "node",

  testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",

};
like image 21
zakariya harti Avatar answered Dec 06 '25 13:12

zakariya harti