Skip to content

Commit

Permalink
fix(testing): add the preset from the jest config to the inferred tas…
Browse files Browse the repository at this point in the history
…k inputs (#26511)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

## Current Behavior
<!-- This is the behavior we have today -->

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
  • Loading branch information
leosvelperez authored Jun 12, 2024
1 parent a8efe59 commit 783bfe6
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 12 deletions.
137 changes: 135 additions & 2 deletions packages/jest/src/plugins/plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { CreateNodesContext } from '@nx/devkit';
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';
import { join } from 'path';

import { createNodesV2 } from './plugin';
import { TempFs } from 'nx/src/internal-testing-utils/temp-fs';

jest.mock('nx/src/utils/cache-directory', () => ({
...jest.requireActual('nx/src/utils/cache-directory'),
workspaceDataDirectory: 'tmp/project-graph-cache',
}));

describe('@nx/jest/plugin', () => {
let createNodesFunction = createNodesV2[1];
Expand Down Expand Up @@ -35,6 +39,7 @@ describe('@nx/jest/plugin', () => {

afterEach(() => {
jest.resetModules();
tempFs.cleanup();
process.chdir(cwd);
});

Expand Down Expand Up @@ -215,6 +220,134 @@ describe('@nx/jest/plugin', () => {
]
`);
});

it('should add preset to the inputs', async () => {
mockJestConfig(
{ coverageDirectory: '../coverage', preset: '../jest.preset.js' },
context
);
tempFs.createFileSync('jest.preset.js', 'module.exports = {};');

const results = await createNodesFunction(
['proj/jest.config.js'],
{ targetName: 'test' },
context
);

expect(results).toMatchInlineSnapshot(`
[
[
"proj/jest.config.js",
{
"projects": {
"proj": {
"metadata": undefined,
"root": "proj",
"targets": {
"test": {
"cache": true,
"command": "jest",
"inputs": [
"default",
"^production",
"{workspaceRoot}/jest.preset.js",
{
"externalDependencies": [
"jest",
],
},
],
"metadata": {
"description": "Run Jest Tests",
"technologies": [
"jest",
],
},
"options": {
"cwd": "proj",
},
"outputs": [
"{workspaceRoot}/coverage",
],
},
},
},
},
},
],
]
`);
});

it.each`
presetFileName | content
${'jest-preset.json'} | ${'{}'}
${'jest-preset.js'} | ${'module.exports = {};'}
${'jest-preset.cjs'} | ${'module.exports = {};'}
`(
'should add package as externalDependencies to the inputs when specified as preset and containing a $presetFileName file',
async ({ presetFileName, content }) => {
mockJestConfig(
{ coverageDirectory: '../coverage', preset: 'some-package' },
context
);
await tempFs.createFiles({
[`node_modules/some-package/${presetFileName}`]: content,
'node_modules/some-package/package.json':
'{ "name": "some-package", "version": "1.0.0" }',
});

const results = await createNodesFunction(
['proj/jest.config.js'],
{ targetName: 'test' },
context
);

expect(results).toMatchInlineSnapshot(`
[
[
"proj/jest.config.js",
{
"projects": {
"proj": {
"metadata": undefined,
"root": "proj",
"targets": {
"test": {
"cache": true,
"command": "jest",
"inputs": [
"default",
"^production",
{
"externalDependencies": [
"jest",
"some-package",
],
},
],
"metadata": {
"description": "Run Jest Tests",
"technologies": [
"jest",
],
},
"options": {
"cwd": "proj",
},
"outputs": [
"{workspaceRoot}/coverage",
],
},
},
},
},
},
],
]
`);
}
);
});

function mockJestConfig(config: any, context: CreateNodesContext) {
Expand Down
88 changes: 78 additions & 10 deletions packages/jest/src/plugins/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import {
TargetConfiguration,
writeJsonFile,
} from '@nx/devkit';
import { dirname, join, relative, resolve } from 'path';
import { dirname, isAbsolute, join, relative, resolve } from 'path';

import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
import { existsSync, readdirSync, readFileSync } from 'fs';
import { readConfig } from 'jest-config';
import { readConfig, replaceRootDirInPath } from 'jest-config';
import jestResolve from 'jest-resolve';
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
import { clearRequireCache } from '@nx/devkit/src/utils/config-utils';
import {
clearRequireCache,
loadConfigFile,
} from '@nx/devkit/src/utils/config-utils';
import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json-workspaces';
import { combineGlobPatterns } from 'nx/src/utils/globs';
import { minimatch } from 'minimatch';
Expand Down Expand Up @@ -157,12 +161,15 @@ async function buildJestTargets(
clearRequireCache();
}

const rawConfig = await loadConfigFile(absConfigFilePath);
const config = await readConfig(
{
_: [],
$0: undefined,
},
absConfigFilePath
rawConfig,
undefined,
dirname(absConfigFilePath)
);

const namedInputs = getNamedInputs(projectRoot, context);
Expand All @@ -181,7 +188,12 @@ async function buildJestTargets(
});

const cache = (target.cache = true);
const inputs = (target.inputs = getInputs(namedInputs));
const inputs = (target.inputs = getInputs(
namedInputs,
rawConfig,
projectRoot,
context.workspaceRoot
));
const outputs = (target.outputs = getOutputs(projectRoot, config, context));

let metadata: ProjectConfiguration['metadata'];
Expand Down Expand Up @@ -257,16 +269,72 @@ async function buildJestTargets(
}

function getInputs(
namedInputs: NxJsonConfiguration['namedInputs']
namedInputs: NxJsonConfiguration['namedInputs'],
jestConfig: { preset?: string },
projectRoot: string,
workspaceRoot: string
): TargetConfiguration['inputs'] {
return [
const inputs: TargetConfiguration['inputs'] = [
...('production' in namedInputs
? ['default', '^production']
: ['default', '^default']),
{
externalDependencies: ['jest'],
},
];

const externalDependencies = ['jest'];
const presetInput = resolvePresetInput(
jestConfig.preset,
projectRoot,
workspaceRoot
);
if (presetInput) {
if (
typeof presetInput !== 'string' &&
'externalDependencies' in presetInput
) {
externalDependencies.push(...presetInput.externalDependencies);
} else {
inputs.push(presetInput);
}
}

inputs.push({ externalDependencies });

return inputs;
}

// preset resolution adapted from:
// https://github.com/jestjs/jest/blob/c54bccd657fb4cf060898717c09f633b4da3eec4/packages/jest-config/src/normalize.ts#L122
function resolvePresetInput(
presetValue: string | undefined,
projectRoot: string,
workspaceRoot: string
): TargetConfiguration['inputs'][number] | null {
if (!presetValue) {
return null;
}

let presetPath = replaceRootDirInPath(projectRoot, presetValue);
const isNpmPackage = !presetValue.startsWith('.') && !isAbsolute(presetPath);
presetPath = presetPath.startsWith('.')
? presetPath
: join(presetPath, 'jest-preset');
const presetModule = jestResolve.findNodeModule(presetPath, {
basedir: projectRoot,
extensions: ['.json', '.js', '.cjs', '.mjs'],
});

if (!presetModule) {
return null;
}

if (isNpmPackage) {
return { externalDependencies: [presetValue] };
}

const relativePath = relative(join(workspaceRoot, projectRoot), presetModule);
return relativePath.startsWith('..')
? join('{workspaceRoot}', join(projectRoot, relativePath))
: join('{projectRoot}', relativePath);
}

function getOutputs(
Expand Down

0 comments on commit 783bfe6

Please sign in to comment.