UNPKG

jest-ex

Version:

A runner and a transformer to simplify (a little bit) your work with Jest.

249 lines (209 loc) 8.98 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _path = _interopRequireDefault(require("path")); var _yargs = _interopRequireDefault(require("yargs")); var _jest = _interopRequireDefault(require("../jest")); var _functions = _interopRequireDefault(require("../utils/functions")); var _fileFinder = _interopRequireDefault(require("../utils/fileFinder")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * The Jest-Ex Runner is just a utility that uses the JestCLI defaults runner but it also allows you * to have some extra functionality and have some defaults that are probably not that important to * you. */ class JestExRunner { /** * @param {Object|String} config This can be either and object with your Jest * configuration or the path to the * configuration file, relative to where the * tests are run (`process.cwd()`). * @param {Object} [options={}] Optional. A set of preferences for the * runner. * @property {Boolean} [runInParallel=true] Optional. Whether you want the tests to run * in parallel or not. * @property {Boolean} [cache=false] Optional. Whether you want to use Jest's * cache or not. * @property {Boolean} [addTransformer=false] Optional. If you want the runner to * automatically add the Jest-Ex Transformer to * your configuration. * @property {Boolean} [addStubs=false] Optional. If you want the runner to * automatically add the Jest-Ex's stubs to * your configuration. * @return {JestExRunner} A new instance of the runner. */ constructor(config, { runInParallel, cache, addTransformer, addStubs } = {}) { /** * The absolute path to where the script that called the runner was executed. * @type {String} */ this.rootPath = _path.default.resolve(process.cwd()); /** * This dictionary will hold the Jest configuration and its possible modifications. * The reason this is initialized empty it's because it can be either assigned, if the user * sent an object as argument, or `require`d, if the user sent the just the path. * @type {Object} */ this.config = {}; if (typeof config === 'string') { // eslint-disable-next-line global-require, import/no-dynamic-require this.config = require(_path.default.join(this.rootPath, config)); } else { this.config = Object.assign({}, config); } /** * Whether the tests are going to run one at a time, or in parallel. The Jest-Ex runner * exposes the option as 'in parallel' (because IMO it's more clear), but the actual name * on the Jest configuration is `runInBand`.. * @type {Boolean} */ this.runInBand = typeof runInParallel !== 'undefined' ? !runInParallel : false; /** * Whether the Jest runner will use cache or not. * @type {Boolean} */ this.cache = !!cache; /** * A dictionary with the regular expressions for the stubs. The reason this is exposed it's * because in case you are using other format of stylesheets, or your templates have another * extension, you can change the expression. * @type {Object} */ this.stubsRegexs = { images: '^[\\.\\/a-zA-Z0-9$_-]+\\.(jpe?g|png|gif|svg)$', styles: '^[\\.\\/a-zA-Z0-9$_-]+\\.s?css$' }; if (addTransformer) { this._addTransformer(); } if (addStubs) { this.addStubs(); } if (_yargs.default.argv.files) { this._detectFiles(_yargs.default.argv.files); } } /** * This method is used in case you want to manually add the stubs you want * @param {Array} [stubs=['images', 'styles', 'html']] Optional. The list of stubs you want to * inject in your configuration. The only * ones available, for now, are: `images`, * `styles` and `html`. * @return {JestExRunner} The current instance of this object, so it can be chained to another * method. */ addStubs(stubs = ['images', 'styles']) { const newConfig = {}; stubs.forEach(name => { const regex = this.stubsRegexs[name]; if (regex) { newConfig[regex] = this._getProjectFilePath(`../stubs/${name}.js`); } }, this); if (this.config.moduleNameMapper) { Object.assign(this.config.moduleNameMapper, newConfig); } else { this.config.moduleNameMapper = newConfig; } return this; } /** * Run the runner :D! * @return {Promise} Once the Jest runner finishes, this promise will be either resolved, if * everything went well, or rejected, if something happend. */ run() { const config = this._getFormattedConfig(); return _jest.default.runCLI(config, [config.rootDir]).then(data => data.results.success ? data : Promise.reject(data)); } /** * Inject the path of the Jest-Ex Transformer to the Jest configuration. The transformer only * runs with `.js` and `.jsx` files. * This method is called from the constructor and only if the `addTransformer` option was set to * true. * @access protected * @ignore */ _addTransformer() { const newConfig = { '\\.[js|jsx]+$': this._getProjectFilePath('../transform.js') }; if (this.config.transform) { Object.assign(this.config.transform, newConfig); } else { this.config.transform = newConfig; } } /** * Given a list of files, this method will create a native regular expression so the runner * will ignore every test file that doesn't match your files. In case you are also collecting * coverage, this method will also try to find the source file for the test(s) you want to run, * and limit the coverage only to that/those file(s). * This is called from the constructor and only if the `yargs` module detected a `--files` * argument being used on the instruction that executed the runner. * @param {String} files A comma separated list of files you intend to match with your test * cases. * @access protected * @ignore */ _detectFiles(files) { if (!this.config.testPathIgnorePatterns) { this.config.testPathIgnorePatterns = []; } const regexList = []; files.split(',').forEach(file => { regexList.push(_functions.default.escapeRegex(file.trim())); }, this); const sanitized = regexList.join('|'); this.config.testPathIgnorePatterns.push(`^((?!(?:${sanitized})).)*$`); if (this.config.collectCoverage) { const coverageFiles = (0, _fileFinder.default)(_path.default.join(process.cwd(), 'src'), new RegExp(sanitized, 'ig'), /\.css|\.scss?$/ig); if (coverageFiles.length) { this.config.collectCoverageOnlyFrom = coverageFiles.slice(); } } } /** * A utility function to get the path of one of the project files, relative to this one. * @param {String} filepath The relative path to a file the runner intend to add to the Jest * configuration. * @return {String} The absolute path to a project file relative to this file. * @access protected * @ignore */ _getProjectFilePath(filepath) { return _path.default.resolve(_path.default.relative(this.rootPath, _path.default.join(__dirname, filepath))); } /** * Prepares the configuration to be sent to Jest by adding the options for cache and parallel * runs; then, it converts any dictionary on the configuration to JSON string, because that's * the way Jest accepts them. * @return {Object} A configuration dictionary that can be used on the Jest `runCLI` method. * @access protected * @ignore */ _getFormattedConfig() { const config = Object.assign({}, this.config, { rootDir: this.rootPath, runInBand: this.runInBand, cache: this.cache }); const stringified = {}; Object.keys(config).forEach(key => { const value = config[key]; if (typeof value === 'object' && !Array.isArray(value)) { stringified[key] = JSON.stringify(value); } }); return Object.assign({}, config, stringified); } } var _default = JestExRunner; exports.default = _default;