UNPKG

@teambit/jest

Version:
331 lines (318 loc) • 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JestTester = void 0; function _path() { const data = require("path"); _path = function () { return data; }; return data; } function _fsExtra() { const data = require("fs-extra"); _fsExtra = function () { return data; }; return data; } function _normalizePath() { const data = _interopRequireDefault(require("normalize-path")); _normalizePath = function () { return data; }; return data; } function _minimatch() { const data = _interopRequireDefault(require("minimatch")); _minimatch = function () { return data; }; return data; } function _lodash() { const data = require("lodash"); _lodash = function () { return data; }; return data; } function _comlink() { const data = require("comlink"); _comlink = function () { return data; }; return data; } function _tester() { const data = require("@teambit/tester"); _tester = function () { return data; }; return data; } function _testsResults() { const data = require("@teambit/tests-results"); _testsResults = function () { return data; }; return data; } function _jestMessageUtil() { const data = require("jest-message-util"); _jestMessageUtil = function () { return data; }; return data; } function _component() { const data = require("@teambit/component"); _component = function () { return data; }; return data; } function _error() { const data = require("./error"); _error = function () { return data; }; return data; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } // import { Environment } from '@teambit/envs'; // import { EnvPolicyConfigObject, PeersAutoDetectPolicy } from '@teambit/dependency-resolver'; class JestTester { constructor(id, jestConfig, jestModulePath, jestWorker, logger, opts = {}) { this.id = id; this.jestConfig = jestConfig; this.jestModulePath = jestModulePath; this.jestWorker = jestWorker; this.logger = logger; this.opts = opts; _defineProperty(this, "jestModule", void 0); _defineProperty(this, "configPath", this.jestConfig); _defineProperty(this, "displayName", 'Jest'); _defineProperty(this, "_callback", void 0); // eslint-disable-next-line global-require,import/no-dynamic-require this.jestModule = require(jestModulePath); } displayConfig() { return (0, _fsExtra().readFileSync)(this.jestConfig, 'utf8'); } version() { return this.jestModule.getVersion(); } attachTestsToComponent(testerContext, testResult) { return _component().ComponentMap.as(testerContext.components, component => { const componentPatternValue = testerContext.patterns.get(component); if (!componentPatternValue) return undefined; const [currComponent, patternEntry] = componentPatternValue; const resolvedPatterns = this.resolveComponentPattern(currComponent, patternEntry, testerContext); return testResult.filter(test => resolvedPatterns.some(resolvedPattern => (0, _minimatch().default)(test.testFilePath, resolvedPattern) || // on Windows, `test.testFilePath` has only single black slashes, while `resolvedPattern` has double black slashes. (0, _minimatch().default)((0, _normalizePath().default)(test.testFilePath), (0, _normalizePath().default)(resolvedPattern)))); }); } buildTestsObj(aggregatedResult, components, testerContext, config) { const testsSuiteResult = components.toArray().map(([component, testsFiles]) => { if (!testsFiles) return undefined; if (testsFiles?.length === 0) return undefined; const errors = this.getErrors(testsFiles); const tests = testsFiles.map(test => { const testResults = test.testResults.map(testResult => { const error = (0, _jestMessageUtil().formatResultsErrors)([testResult], config, { noStackTrace: true }) || undefined; const isFailure = testResult.status === 'failed'; return new (_testsResults().TestResult)(testResult.ancestorTitles, testResult.title, testResult.status, testResult.duration, isFailure ? undefined : error, isFailure ? error : undefined); }); const filePath = (0, _path().basename)(test.testFilePath); const getError = () => { if (!test.testExecError) return undefined; if (testerContext.watch) { // for some reason, during watch ('bit start'), if a file has an error, the `test.testExecError` is `{}` // (an empty object). the failureMessage contains the stringified error. // @todo: consider to always use the failureMessage, regardless the context.watch. return new (_error().JestError)(test.failureMessage); } return new (_error().JestError)(test.testExecError?.message, test.testExecError?.stack); }; const error = getError(); return new (_testsResults().TestsFiles)(filePath, testResults, test.numPassingTests, test.numFailingTests, test.numPendingTests, test.perfStats.runtime, test.perfStats.slow, error); }); return { componentId: component.id, results: new (_testsResults().TestsResult)(tests, aggregatedResult.success, aggregatedResult.startTime), errors }; }); return (0, _lodash().compact)(testsSuiteResult); } getErrors(testResult) { return testResult.reduce((errors, test) => { if (test.testExecError) { const { message, stack, code, type } = test.testExecError; errors.push(new (_error().JestError)(message, stack, code, type)); } else if (test.failureMessage) { errors.push(new (_error().JestError)(test.failureMessage)); } return errors; }, []); } async onTestRunComplete(callback) { this._callback = callback; } async test(context) { // const envRootDir = context.envRuntime.envAspectDefinition.aspectPath; const config = { // Setting the rootDir to the env root dir to make sure we can resolve all the jest presets/plugins // from the env context // rootDir: envRootDir, // TODO: set it to envRootDir and make sure we can make the --coverage to work // with the current value as context.rootPath it will probably won't work correctly when using rootComponents:true (maybe even won't work at all) // TODO: when changing to envRootDir we have some issues with the react-native tests. so once changed again, it needs to be validated. rootDir: context.rootPath, // Setting the roots (where to search for spec files) to the root path (either workspace or capsule root) // TODO: consider change this to be an array of the components running dir. // TODO: aka: in the workspace it will be something like <ws>/node_modules/<comp-package-name>/node_modules/<comp-package-name> // TODO: see dependencyResolver.getRuntimeModulePath (this will make sure the peer deps resolved correctly) // TODO: (@GiladShoham - when trying to set it to this paths, jest ignores it probably because the paths contains "node_modules" // TODO: trying to set the https://jestjs.io/docs/27.x/configuration#testpathignorepatterns-arraystring to something else (as it contain node_modules by default) // TODO: didn't help) roots: [context.rootPath] }; // eslint-disable-next-line no-console console.warn = message => { this.logger.warn(message); }; if (context.debug) { config.debug = true; config.runInBand = true; } if (context.coverage) config.coverage = true; config.runInBand = true; if (context.watch) { config.watchAll = true; config.noCache = true; } // eslint-disable-next-line global-require,import/no-dynamic-require const jestConfig = require(this.jestConfig); // TODO: rollback this for now, as it makes issues. // TODO: it's mostly relevant for when the root components feature is enabled. // TODO: we might want to enable it only on that case (along with setting the env root dir as the root dir, above) // const moduleNameMapper = await this.calculateModuleNameMapper( // context.env, // context.rootPath, // context.additionalHostDependencies // ); // jestConfig.moduleNameMapper = Object.assign({}, jestConfig.moduleNameMapper || {}, moduleNameMapper); const jestConfigWithSpecs = Object.assign(jestConfig, { testMatch: this.patternsToArray(context) }); const withEnv = Object.assign(jestConfigWithSpecs, config); const testsOutPut = await this.jestModule.runCLI(withEnv, [this.jestConfig]); const testResults = testsOutPut.results.testResults; const componentsWithTests = this.attachTestsToComponent(context, testResults); const componentTestResults = this.buildTestsObj(testsOutPut.results, componentsWithTests, context, jestConfigWithSpecs); return new (_tester().Tests)(componentTestResults); } async watch(context) { // eslint-disable-next-line return new Promise(async resolve => { const workerApi = this.jestWorker.initiate(context.ui ? { stdout: true, stderr: true, stdin: true } : { stdout: false, stderr: false, stdin: false }); // eslint-disable-next-line const jestConfig = require(this.jestConfig); const envRootDir = context.envRuntime.envAspectDefinition?.aspectPath; if (!envRootDir) { this.logger.warn(`jest tester, envRootDir is not defined, for env ${context.envRuntime.id}`); } const jestConfigWithSpecs = Object.assign(jestConfig, { testMatch: this.patternsToArray(context) }); try { const cbFn = (0, _comlink().proxy)(results => { if (!this._callback) return; const testResults = results.testResults; const componentsWithTests = this.attachTestsToComponent(context, testResults); const componentTestResults = this.buildTestsObj(results, componentsWithTests, context, jestConfigWithSpecs); const globalErrors = this.getErrors(testResults); const watchTestResults = { loading: false, errors: globalErrors, components: componentTestResults }; this._callback(watchTestResults); resolve(watchTestResults); }); // eslint-disable-next-line @typescript-eslint/no-floating-promises await workerApi.onTestComplete(cbFn); await workerApi.watch(this.jestConfig, this.patternsToArray(context), context.rootPath, this.jestModulePath, envRootDir); } catch (err) { this.logger.error('jest.tester.watch() caught an error', err); } }); } // private async calculateModuleNameMapper( // env: Environment, // rootPath: string, // additionalHostDependencies?: string[] // ): Promise<Record<string, Array<string>>> { // const peerDepsConfig: EnvPolicyConfigObject = await env.getDependencies(); // const peersAutoDetectPolicy = new PeersAutoDetectPolicy(peerDepsConfig.peers || []); // const peers = Object.keys(peerDepsConfig.peerDependencies || {}).concat(peersAutoDetectPolicy?.names); // const depsToMap = peers.concat(additionalHostDependencies || []); // /** // * Try to resolve the dependency from the rootDir (the env dir) or from the root path (workspace/capsule root) // */ // const mappedValues = ['<rootDir>/node_modules/$1', `${rootPath}/node_modules/$1`]; // const moduleNameMapper = depsToMap.reduce((acc, peerName) => { // const keyName = `^(${peerName})$`; // acc[keyName] = mappedValues; // const internalPathKeyName = `^(${peerName}/.*)$`; // acc[internalPathKeyName] = mappedValues; // return acc; // }, {}); // return moduleNameMapper; // } patternsToArray(context) { return (0, _lodash().flatten)(context.patterns.toArray().map(([component, patternEntry]) => { return this.resolveComponentPattern(component, patternEntry, context); })); } resolveComponentPattern(component, patternEntry, context) { if (this.opts.resolveSpecPaths) { return this.opts.resolveSpecPaths(component, context); } const customPatterns = this.opts.patterns; // If pattern were provided to the specific instance of the tester, use them if (customPatterns && !(0, _lodash().isEmpty)(customPatterns)) { customPatterns.map(customPattern => { const rootDirs = this.opts.roots || [patternEntry.componentDir]; return this.resolvePattern(customPattern, rootDirs); }); } return patternEntry.paths.map(p => p.path); } resolvePattern(pattern, rootDirs) { return rootDirs.map(dir => (0, _path().resolve)(dir, pattern)); } } exports.JestTester = JestTester; //# sourceMappingURL=jest.tester.js.map