Skip to content

Instantly share code, notes, and snippets.

@the0neWhoKnocks
Last active July 10, 2020 23:18
Show Gist options
  • Save the0neWhoKnocks/403a7f6fc202e5851aa969acc7a22064 to your computer and use it in GitHub Desktop.
Save the0neWhoKnocks/403a7f6fc202e5851aa969acc7a22064 to your computer and use it in GitHub Desktop.
Jest Configuration Notes
module.exports = {
env: {
browser: true,
es6: true,
jest: true, // <-- important bit
node: true,
},
extends: 'eslint:recommended',
parserOptions: {
ecmaVersion: 2019,
},
};

Jest Configuration Notes


Install

npm i -D cli-color jest jest-localstorage-mock
# or
yarn add -D cli-color jest jest-localstorage-mock

# `jest-localstorage-mock` is optional if you don't use localStorage
# `cli-color` is used for the `UnhandledRejectionReporter`

Troubleshooting

  • On OSX I ran into openSSL hashing errors while trying to install NPM modules.
    ERROR:root:code for hash md5 was not found.
    To fix the errors I ran
    brew unlink openssl
    brew reinstall python@2
  • After upgrading to Jest 26 I started getting window or describe undefined errors.
    • After a lot of digging, I came across jestjs/jest#9538 (comment). Where they call out you should be using Node >=10.14.2.
    • If you have nvm installed, you can just run nvm install 10.14.2 && nvm alias default 10.14.2 && nvm use default

Configuration files

  • Package json scripts
  • ES Lint
  • Babel config
    • If you're not using Babel, omit any items referencing Babel in the configs.
  • Jest config
    • If you're dealing with CSS modules, you'd add
      moduleNameMapper: {
        '\\.(css|styl)$': 'identity-obj-proxy',
      },
    • If you're using Webpack aliases, you'd add
      // bring in your Array of aliases
      const { paths: ALIASES } = require('./conf.repo');
      
      // -- place the below just above module.exports --
      
      // map Webpack alias' so files resolve
      Object.keys(ALIASES).forEach((alias) => {
        conf.moduleNameMapper[`^${alias}(.*)$`] = `${ALIASES[alias]}$1`;
      });
    • Custom reporters
module.exports = {
env: {
// Jest settings
test: {
compact: false, // no need for line/space optimizations, it slows down test runs
presets: [
['@babel/preset-env', {}],
'@babel/preset-react',
],
plugins: [
['@babel/plugin-transform-runtime'],
['@babel/plugin-proposal-export-default-from'],
['dynamic-import-node', { noInterop: true }], // for Webpack dynamic imports
],
},
},
};
const conf = {
automock: false,
collectCoverage: true,
// This will collect coverage information for all the files inside the
// project's `rootDir`.
// If a file matches the specified glob pattern, coverage information will be
// collected for it even if no tests exist for this file and it's never
// required in the test suite.
collectCoverageFrom: [
'src/**/*.js',
// exclusions
'!**/node_modules/**',
'!**/__mocks__/**',
'!**/mocks/**',
'!**/__snapshots__/**',
'!**/stories.js',
'!**/styles.js',
'!**/vendor/**',
],
coverageDirectory: 'reports/unit-tests',
coveragePathIgnorePatterns: [
// ignore everything from the root except for `src`
'<rootDir>\\/(?!src).*\\.js$',
// ignore individual files within `src`
'some/path/index.js',
],
coverageReporters: [
'html',
'json-summary',
'lcov',
'text-summary',
],
coverageThreshold: {
global: {
statements: 0,
branches: 0,
functions: 0,
lines: 0,
},
},
globals: {},
moduleNameMapper: {},
reporters: [
'default',
'<rootDir>/tests/reporters/UnhandledRejectionReporter',
],
roots: [
'src',
],
setupFilesAfterEnv: [
'jest-localstorage-mock',
'<rootDir>/tests/testSetup.js',
],
testEnvironment: 'jsdom',
transform: {
'^.+\\.(js|jsx)?$': 'babel-jest',
},
};
// While watching files, it's still useful to generate coverage, but we only
// want coverage for changed files to cut down on build time. This is
// accomplished via the `--changedSince=master` flag in the `package.json`.
// Note that this doesn't completely isolate per-file changes, but it does
// trim down on a lot.
if (process.env.JEST_WATCH) {
delete conf.collectCoverageFrom;
delete conf.coverageThreshold;
}
module.exports = conf;
{
"scripts": {
"test": "BABEL_ENV=test jest --runInBand --no-watchman --config ./conf.jest.js",
"test:watch": "BABEL_ENV=test JEST_WATCH=true jest --no-watchman --watch --changedSince=master --config ./conf.jest.js",
}
}
window.testCtx = {
/**
* Allows for setting `window.location` props within tests
* @param {Object} options - The prop: val that is to override
*/
location: options => {
Object.defineProperty(window, 'location', {
writable: true,
value: Object.assign({
href: 'http://local',
origin: 'local',
protocol: 'http:',
host: 'http://local',
pathname: '/',
port: '3000',
search: '',
hash: '',
assign: jest.fn(),
match: jest.fn(),
}, options)
})
},
/**
* Allows for setting `window.navigator` props within tests
* @param {String} prop - The `navigator` prop you want to set.
* @param {String} val - The value of the prop.
*/
navigator: function(prop, val){
Object.defineProperty(window.navigator, prop, {
writable: true,
value: val
});
},
};
global.requestAnimationFrame = (callback) => {
setTimeout(callback, 0);
};
global.console.debug = jest.fn();
global.console.error = jest.fn();
global.console.warn = jest.fn();
process.on('unhandledRejection', require('./reporters/UnhandledRejectionReporter').rejectionHandler);
const {
appendFileSync,
existsSync,
readFileSync,
unlinkSync,
} = require('fs');
const { resolve } = require('path');
const color = require('cli-color');
const LOG_FILE = resolve(__dirname, './unhandled.log');
const MISSING_ERROR = '[No Error Emitted]';
class UnhandledRejectionReporter {
constructor(globalConfig, options) {
this.globalConfig = globalConfig;
this.options = options;
}
logExists() {
try { if (existsSync(LOG_FILE)) return true; }
// No log created, everything is good.
catch(e) { return false; }
}
getLogs() {
if (this.logExists()) {
const logs = readFileSync(LOG_FILE, 'utf8');
return {
lines: logs.split('\n').filter(Boolean),
raw: logs,
};
}
return {};
}
onRunStart() {
if (this.logExists()) unlinkSync(LOG_FILE);
}
onTestResult({ path: testFile }) {
const logs = this.getLogs();
if (logs.lines && logs.lines[logs.lines.length - 1] === MISSING_ERROR) {
appendFileSync(LOG_FILE, `\n${testFile}\n\n`);
}
}
onRunComplete() {
const logs = this.getLogs();
if (logs.raw) {
process.stdout.write(
'\n'
+ color.red(`Error: Unhandled Rejection(s) Detected`)
+`\n\n${color.redBright(logs.raw)}`
);
// Only exit when not watching for changes
if (!this.globalConfig.watch) process.exit(1);
}
}
}
UnhandledRejectionReporter.rejectionHandler = (err) => {
const msg = (err)
? `${err.message}\n${err.stack}\n\n`
: MISSING_ERROR;
appendFileSync(LOG_FILE, msg);
}
module.exports = UnhandledRejectionReporter;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment