UNPKG

@nx/jest

Version:

The Nx Plugin for Jest contains executors and generators allowing your workspace to use the powerful Jest testing capabilities.

139 lines (138 loc) 5.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = default_1; const tslib_1 = require("tslib"); const devkit_1 = require("@nx/devkit"); const tsquery_1 = require("@phenomnomnominal/tsquery"); const jest_1 = require("jest"); const jest_config_1 = require("jest-config"); const jest_runtime_1 = tslib_1.__importDefault(require("jest-runtime")); const path_1 = require("path"); const matcherAliasesMap = new Map([ ['toBeCalled', 'toHaveBeenCalled'], ['toBeCalledTimes', 'toHaveBeenCalledTimes'], ['toBeCalledWith', 'toHaveBeenCalledWith'], ['lastCalledWith', 'toHaveBeenLastCalledWith'], ['nthCalledWith', 'toHaveBeenNthCalledWith'], ['toReturn', 'toHaveReturned'], ['toReturnTimes', 'toHaveReturnedTimes'], ['toReturnWith', 'toHaveReturnedWith'], ['lastReturnedWith', 'toHaveLastReturnedWith'], ['nthReturnedWith', 'toHaveNthReturnedWith'], ['toThrowError', 'toThrow'], ]); // migration for https://github.com/jestjs/jest/commit/eac241cf0bcb7a808e192e6fcf3afe67edbdbf8e async function default_1(tree) { const testFilePaths = await getTestFilePaths(tree); for (const testFilePath of testFilePaths) { const testFileContent = tree.read(testFilePath, 'utf-8'); const updatedContent = replaceMatcherAliases(testFileContent); if (updatedContent !== testFileContent) { tree.write(testFilePath, updatedContent); } } await (0, devkit_1.formatFiles)(tree); } function replaceMatcherAliases(fileContent) { // Build a selector that matches any of the deprecated matcher aliases const aliasNames = Array.from(matcherAliasesMap.keys()); const aliasPattern = aliasNames.join('|'); // Quick check to avoid parsing files that don't contain any aliases const hasAnyAlias = aliasNames.some((alias) => fileContent.includes(alias)); if (!hasAnyAlias) { return fileContent; } const sourceFile = (0, tsquery_1.ast)(fileContent); const updates = []; // Query for all deprecated matcher identifiers in expect() chains // The selector matches: expect(...).toBeCalled(), expect(...).not.toBeCalled(), etc. const selector = `CallExpression PropertyAccessExpression:has(CallExpression Identifier[name=expect]) Identifier[name=/^(${aliasPattern})$/]`; const matchedNodes = (0, tsquery_1.query)(sourceFile, selector); for (const node of matchedNodes) { const alias = node.text; const replacement = matcherAliasesMap.get(alias); if (replacement) { updates.push({ start: node.getStart(sourceFile), end: node.getEnd(), text: replacement, }); } } if (!updates.length) { return fileContent; } // Apply updates in reverse order to preserve positions let updatedContent = fileContent; for (const update of updates.sort((a, b) => b.start - a.start)) { updatedContent = updatedContent.slice(0, update.start) + update.text + updatedContent.slice(update.end); } return updatedContent; } async function getTestFilePaths(tree) { const jestConfigFiles = await (0, devkit_1.globAsync)(tree, [ '**/jest.config.{cjs,mjs,js,cts,mts,ts}', ]); if (!jestConfigFiles.length) { return []; } const testFilePaths = new Set(); for (const jestConfigFile of jestConfigFiles) { const jestConfigContent = tree.read(jestConfigFile, 'utf-8'); if (jestConfigContent.includes('getJestProjectsAsync()')) { // skip the root jest config file which includes all projects continue; } const resolvedPaths = await resolveTestPaths(tree, jestConfigFile); if (resolvedPaths) { for (const testPath of resolvedPaths) { testFilePaths.add(testPath); } } } return Array.from(testFilePaths); } /** * Resolves test file paths for a single jest config by loading the config * through Jest's own resolution. Returns null if the config cannot be * resolved (e.g. missing files, uninstalled transform modules, invalid * presets), logging a warning so the user knows which project was skipped. */ async function resolveTestPaths(tree, jestConfigFile) { const fullConfigPath = (0, path_1.join)(tree.root, jestConfigFile); let config; try { config = await (0, jest_config_1.readConfig)({ _: [], $0: undefined }, fullConfigPath); } catch (e) { const message = e instanceof Error ? e.message : String(e); devkit_1.logger.warn(`Could not read Jest config "${jestConfigFile}": ${message}. Skipping this project for matcher alias replacement.`); return null; } let jestContext; try { jestContext = await jest_runtime_1.default.createContext(config.projectConfig, { maxWorkers: 1, watchman: false, }); } catch (e) { const message = e instanceof Error ? e.message : String(e); devkit_1.logger.warn(`Could not create Jest context for "${jestConfigFile}": ${message}. Skipping this project for matcher alias replacement.`); return null; } let specs; try { const source = new jest_1.SearchSource(jestContext); specs = await source.getTestPaths(config.globalConfig, config.projectConfig); } catch (e) { const message = e instanceof Error ? e.message : String(e); devkit_1.logger.warn(`Could not resolve test paths for "${jestConfigFile}": ${message}. Skipping this project for matcher alias replacement.`); return null; } return specs.tests.map((t) => path_1.posix.normalize((0, path_1.relative)(tree.root, t.path))); }