@nx/jest 21.0.0-beta.1 → 21.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/executors/jest/jest.impl.js +7 -1
- package/src/generators/configuration/files/common/src/test-setup.ts__tmpl__ +7 -1
- package/src/generators/configuration/lib/create-jest-config.js +3 -2
- package/src/generators/configuration/schema.d.ts +6 -1
- package/src/plugins/plugin.d.ts +1 -1
- package/src/plugins/plugin.js +91 -60
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/jest",
|
|
3
|
-
"version": "21.0.0-beta.
|
|
3
|
+
"version": "21.0.0-beta.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Nx Plugin for Jest contains executors and generators allowing your workspace to use the powerful Jest testing capabilities.",
|
|
6
6
|
"repository": {
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@jest/reporters": "^29.4.1",
|
|
39
39
|
"@jest/test-result": "^29.4.1",
|
|
40
|
-
"@nx/devkit": "21.0.0-beta.
|
|
41
|
-
"@nx/js": "21.0.0-beta.
|
|
40
|
+
"@nx/devkit": "21.0.0-beta.2",
|
|
41
|
+
"@nx/js": "21.0.0-beta.2",
|
|
42
42
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
43
43
|
"identity-obj-proxy": "3.0.0",
|
|
44
44
|
"jest-config": "^29.4.1",
|
|
@@ -16,7 +16,13 @@ process.env.NODE_ENV ??= 'test';
|
|
|
16
16
|
async function jestExecutor(options, context) {
|
|
17
17
|
// Jest registers ts-node with module CJS https://github.com/SimenB/jest/blob/v29.6.4/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L117-L119
|
|
18
18
|
// We want to support of ESM via 'module':'nodenext', we need to override the resolution until Jest supports it.
|
|
19
|
-
process.env
|
|
19
|
+
const existingValue = process.env['TS_NODE_COMPILER_OPTIONS'];
|
|
20
|
+
process.env['TS_NODE_COMPILER_OPTIONS'] = JSON.stringify({
|
|
21
|
+
...(existingValue ? JSON.parse(existingValue) : {}),
|
|
22
|
+
moduleResolution: 'Node10',
|
|
23
|
+
module: 'commonjs',
|
|
24
|
+
customConditions: null,
|
|
25
|
+
});
|
|
20
26
|
const config = await parseJestConfig(options, context);
|
|
21
27
|
const { results } = await (0, jest_1.runCLI)(config, [options.jestConfig]);
|
|
22
28
|
return { success: results.success };
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
<% if(setupFile === 'react-native') { %>import '@testing-library/jest-native/extend-expect';<% } %>
|
|
1
|
+
<% if(setupFile === 'react-native') { %>import '@testing-library/jest-native/extend-expect';<% } %>
|
|
2
|
+
<%_ if(setupFile === 'react-router') { _%>
|
|
3
|
+
import { TextEncoder, TextDecoder as NodeTextDecoder } from "util";
|
|
4
|
+
|
|
5
|
+
global.TextEncoder = TextEncoder;
|
|
6
|
+
global.TextDecoder = NodeTextDecoder as typeof TextDecoder; // necessary because there is a mismatch between ts type and node type
|
|
7
|
+
<%_ } _%>
|
|
@@ -99,9 +99,10 @@ function generateGlobalConfig(tree, isJS) {
|
|
|
99
99
|
module.exports = async () => ({
|
|
100
100
|
projects: await getJestProjectsAsync()
|
|
101
101
|
});`
|
|
102
|
-
: `import {
|
|
102
|
+
: `import type { Config } from 'jest';
|
|
103
|
+
import { getJestProjectsAsync } from '@nx/jest';
|
|
103
104
|
|
|
104
|
-
export default async () => ({
|
|
105
|
+
export default async (): Promise<Config> => ({
|
|
105
106
|
projects: await getJestProjectsAsync()
|
|
106
107
|
});`;
|
|
107
108
|
tree.write(`jest.config.${isJS ? 'js' : 'ts'}`, contents);
|
|
@@ -6,7 +6,12 @@ export interface JestProjectSchema {
|
|
|
6
6
|
* @deprecated use setupFile instead
|
|
7
7
|
*/
|
|
8
8
|
skipSetupFile?: boolean;
|
|
9
|
-
setupFile?:
|
|
9
|
+
setupFile?:
|
|
10
|
+
| 'angular'
|
|
11
|
+
| 'web-components'
|
|
12
|
+
| 'react-native'
|
|
13
|
+
| 'react-router'
|
|
14
|
+
| 'none';
|
|
10
15
|
skipSerializers?: boolean;
|
|
11
16
|
testEnvironment?: 'node' | 'jsdom' | 'none';
|
|
12
17
|
/**
|
package/src/plugins/plugin.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface JestPluginOptions {
|
|
|
7
7
|
*/
|
|
8
8
|
ciGroupName?: string;
|
|
9
9
|
/**
|
|
10
|
-
* Whether to use jest-config and jest-runtime to load Jest configuration and context.
|
|
10
|
+
* Whether to use jest-config and jest-runtime are used to load Jest configuration and context.
|
|
11
11
|
* Disabling this is much faster but could be less correct since we are using our own config loader
|
|
12
12
|
* and test matcher instead of Jest's.
|
|
13
13
|
*/
|
package/src/plugins/plugin.js
CHANGED
|
@@ -31,8 +31,36 @@ exports.createNodesV2 = [
|
|
|
31
31
|
const targetsCache = readTargetsCache(cachePath);
|
|
32
32
|
// Cache jest preset(s) to avoid penalties of module load times. Most of jest configs will use the same preset.
|
|
33
33
|
const presetCache = {};
|
|
34
|
+
const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
|
|
35
|
+
options = normalizeOptions(options);
|
|
36
|
+
const { roots: projectRoots, configFiles: validConfigFiles } = configFiles.reduce((acc, configFile) => {
|
|
37
|
+
const potentialRoot = (0, path_1.dirname)(configFile);
|
|
38
|
+
if (checkIfConfigFileShouldBeProject(configFile, potentialRoot, packageManagerWorkspacesGlob, context)) {
|
|
39
|
+
acc.roots.push(potentialRoot);
|
|
40
|
+
acc.configFiles.push(configFile);
|
|
41
|
+
}
|
|
42
|
+
return acc;
|
|
43
|
+
}, {
|
|
44
|
+
roots: [],
|
|
45
|
+
configFiles: [],
|
|
46
|
+
});
|
|
47
|
+
const hashes = await (0, calculate_hash_for_create_nodes_1.calculateHashesForCreateNodes)(projectRoots, options, context);
|
|
34
48
|
try {
|
|
35
|
-
return await (0, devkit_1.createNodesFromFiles)(
|
|
49
|
+
return await (0, devkit_1.createNodesFromFiles)(async (configFilePath, options, context, idx) => {
|
|
50
|
+
const projectRoot = projectRoots[idx];
|
|
51
|
+
const hash = hashes[idx];
|
|
52
|
+
targetsCache[hash] ??= await buildJestTargets(configFilePath, projectRoot, options, context, presetCache);
|
|
53
|
+
const { targets, metadata } = targetsCache[hash];
|
|
54
|
+
return {
|
|
55
|
+
projects: {
|
|
56
|
+
[projectRoot]: {
|
|
57
|
+
root: projectRoot,
|
|
58
|
+
targets,
|
|
59
|
+
metadata,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}, validConfigFiles, options, context);
|
|
36
64
|
}
|
|
37
65
|
finally {
|
|
38
66
|
writeTargetsToCache(cachePath, targetsCache);
|
|
@@ -45,26 +73,39 @@ exports.createNodesV2 = [
|
|
|
45
73
|
*/
|
|
46
74
|
exports.createNodes = [
|
|
47
75
|
jestConfigGlob,
|
|
48
|
-
(
|
|
76
|
+
async (configFilePath, options, context) => {
|
|
49
77
|
devkit_1.logger.warn('`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.');
|
|
50
|
-
|
|
78
|
+
const projectRoot = (0, path_1.dirname)(configFilePath);
|
|
79
|
+
const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
|
|
80
|
+
if (!checkIfConfigFileShouldBeProject(configFilePath, projectRoot, packageManagerWorkspacesGlob, context)) {
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
options = normalizeOptions(options);
|
|
84
|
+
const { targets, metadata } = await buildJestTargets(configFilePath, projectRoot, options, context, {});
|
|
85
|
+
return {
|
|
86
|
+
projects: {
|
|
87
|
+
[projectRoot]: {
|
|
88
|
+
root: projectRoot,
|
|
89
|
+
targets,
|
|
90
|
+
metadata,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
51
94
|
},
|
|
52
95
|
];
|
|
53
|
-
|
|
54
|
-
const projectRoot = (0, path_1.dirname)(configFilePath);
|
|
55
|
-
const packageManagerWorkspacesGlob = (0, globs_1.combineGlobPatterns)((0, package_json_1.getGlobPatternsFromPackageManagerWorkspaces)(context.workspaceRoot));
|
|
96
|
+
function checkIfConfigFileShouldBeProject(configFilePath, projectRoot, packageManagerWorkspacesGlob, context) {
|
|
56
97
|
// Do not create a project if package.json and project.json isn't there.
|
|
57
98
|
const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
|
|
58
99
|
if (!siblingFiles.includes('package.json') &&
|
|
59
100
|
!siblingFiles.includes('project.json')) {
|
|
60
|
-
return
|
|
101
|
+
return false;
|
|
61
102
|
}
|
|
62
103
|
else if (!siblingFiles.includes('project.json') &&
|
|
63
104
|
siblingFiles.includes('package.json')) {
|
|
64
105
|
const path = (0, devkit_1.joinPathFragments)(projectRoot, 'package.json');
|
|
65
106
|
const isPackageJsonProject = (0, minimatch_1.minimatch)(path, packageManagerWorkspacesGlob);
|
|
66
107
|
if (!isPackageJsonProject) {
|
|
67
|
-
return
|
|
108
|
+
return false;
|
|
68
109
|
}
|
|
69
110
|
}
|
|
70
111
|
const jestConfigContent = (0, fs_1.readFileSync)((0, path_1.resolve)(context.workspaceRoot, configFilePath), 'utf-8');
|
|
@@ -72,21 +113,9 @@ async function createNodesInternal(configFilePath, options, context, targetsCach
|
|
|
72
113
|
// The `getJestProjectsAsync` function uses the project graph, which leads to a
|
|
73
114
|
// circular dependency. We can skip this since it's no intended to be used for
|
|
74
115
|
// an Nx project.
|
|
75
|
-
return
|
|
116
|
+
return false;
|
|
76
117
|
}
|
|
77
|
-
|
|
78
|
-
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context);
|
|
79
|
-
targetsCache[hash] ??= await buildJestTargets(configFilePath, projectRoot, options, context, presetCache);
|
|
80
|
-
const { targets, metadata } = targetsCache[hash];
|
|
81
|
-
return {
|
|
82
|
-
projects: {
|
|
83
|
-
[projectRoot]: {
|
|
84
|
-
root: projectRoot,
|
|
85
|
-
targets,
|
|
86
|
-
metadata,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
};
|
|
118
|
+
return true;
|
|
90
119
|
}
|
|
91
120
|
async function buildJestTargets(configFilePath, projectRoot, options, context, presetCache) {
|
|
92
121
|
const absConfigFilePath = (0, path_1.resolve)(context.workspaceRoot, configFilePath);
|
|
@@ -95,13 +124,22 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
95
124
|
const rawConfig = await (0, config_utils_1.loadConfigFile)(absConfigFilePath);
|
|
96
125
|
const targets = {};
|
|
97
126
|
const namedInputs = (0, get_named_inputs_1.getNamedInputs)(projectRoot, context);
|
|
127
|
+
const existingTsNodeCompilerOptions = process.env['TS_NODE_COMPILER_OPTIONS'];
|
|
128
|
+
const tsNodeCompilerOptions = JSON.stringify({
|
|
129
|
+
...(existingTsNodeCompilerOptions
|
|
130
|
+
? JSON.parse(existingTsNodeCompilerOptions)
|
|
131
|
+
: {}),
|
|
132
|
+
moduleResolution: 'node10',
|
|
133
|
+
module: 'commonjs',
|
|
134
|
+
customConditions: null,
|
|
135
|
+
});
|
|
98
136
|
const target = (targets[options.targetName] = {
|
|
99
137
|
command: 'jest',
|
|
100
138
|
options: {
|
|
101
139
|
cwd: projectRoot,
|
|
102
140
|
// Jest registers ts-node with module CJS https://github.com/SimenB/jest/blob/v29.6.4/packages/jest-config/src/readConfigFileAndSetRootDir.ts#L117-L119
|
|
103
141
|
// We want to support of ESM via 'module':'nodenext', we need to override the resolution until Jest supports it.
|
|
104
|
-
env: { TS_NODE_COMPILER_OPTIONS:
|
|
142
|
+
env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
|
|
105
143
|
},
|
|
106
144
|
metadata: {
|
|
107
145
|
technologies: ['jest'],
|
|
@@ -116,11 +154,13 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
116
154
|
},
|
|
117
155
|
},
|
|
118
156
|
});
|
|
157
|
+
// Not normalizing it here since also affects options for convert-to-inferred.
|
|
158
|
+
const disableJestRuntime = options.disableJestRuntime !== false;
|
|
119
159
|
const cache = (target.cache = true);
|
|
120
|
-
const inputs = (target.inputs = getInputs(namedInputs, rawConfig.preset, projectRoot, context.workspaceRoot,
|
|
160
|
+
const inputs = (target.inputs = getInputs(namedInputs, rawConfig.preset, projectRoot, context.workspaceRoot, disableJestRuntime));
|
|
121
161
|
let metadata;
|
|
122
162
|
const groupName = options?.ciGroupName ?? deductGroupNameFromTarget(options?.ciTargetName);
|
|
123
|
-
if (
|
|
163
|
+
if (disableJestRuntime) {
|
|
124
164
|
const outputs = (target.outputs = getOutputs(projectRoot, rawConfig.coverageDirectory
|
|
125
165
|
? (0, path_1.join)(context.workspaceRoot, projectRoot, rawConfig.coverageDirectory)
|
|
126
166
|
: undefined, undefined, context));
|
|
@@ -148,7 +188,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
148
188
|
outputs,
|
|
149
189
|
options: {
|
|
150
190
|
cwd: projectRoot,
|
|
151
|
-
env: { TS_NODE_COMPILER_OPTIONS:
|
|
191
|
+
env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
|
|
152
192
|
},
|
|
153
193
|
metadata: {
|
|
154
194
|
technologies: ['jest'],
|
|
@@ -192,10 +232,17 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
192
232
|
}
|
|
193
233
|
else {
|
|
194
234
|
const { readConfig } = requireJestUtil('jest-config', projectRoot, context.workspaceRoot);
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
235
|
+
let config;
|
|
236
|
+
try {
|
|
237
|
+
config = await readConfig({
|
|
238
|
+
_: [],
|
|
239
|
+
$0: undefined,
|
|
240
|
+
}, rawConfig, undefined, (0, path_1.dirname)(absConfigFilePath));
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
console.error(e);
|
|
244
|
+
throw e;
|
|
245
|
+
}
|
|
199
246
|
const outputs = (target.outputs = getOutputs(projectRoot, config.globalConfig?.coverageDirectory, config.globalConfig?.outputFile, context));
|
|
200
247
|
if (options?.ciTargetName) {
|
|
201
248
|
// nx-ignore-next-line
|
|
@@ -208,8 +255,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
208
255
|
const source = new jest.SearchSource(jestContext);
|
|
209
256
|
const jestVersion = (0, version_utils_1.getInstalledJestMajorVersion)();
|
|
210
257
|
const specs = jestVersion >= 30
|
|
211
|
-
?
|
|
212
|
-
await source.getTestPaths(config.globalConfig, config.projectConfig)
|
|
258
|
+
? await source.getTestPaths(config.globalConfig, config.projectConfig)
|
|
213
259
|
: await source.getTestPaths(config.globalConfig);
|
|
214
260
|
const testPaths = new Set(specs.tests.map(({ path }) => path));
|
|
215
261
|
if (testPaths.size > 0) {
|
|
@@ -252,9 +298,7 @@ async function buildJestTargets(configFilePath, projectRoot, options, context, p
|
|
|
252
298
|
outputs,
|
|
253
299
|
options: {
|
|
254
300
|
cwd: projectRoot,
|
|
255
|
-
env: {
|
|
256
|
-
TS_NODE_COMPILER_OPTIONS: '{"moduleResolution":"node10"}',
|
|
257
|
-
},
|
|
301
|
+
env: { TS_NODE_COMPILER_OPTIONS: tsNodeCompilerOptions },
|
|
258
302
|
},
|
|
259
303
|
metadata: {
|
|
260
304
|
technologies: ['jest'],
|
|
@@ -401,32 +445,19 @@ function requireJestUtil(packageName, projectRoot, workspaceRoot) {
|
|
|
401
445
|
}
|
|
402
446
|
async function getTestPaths(projectRoot, rawConfig, absConfigFilePath, context, presetCache) {
|
|
403
447
|
const testMatch = await getJestOption(rawConfig, absConfigFilePath, 'testMatch', presetCache);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
for (const file of projectFiles) {
|
|
416
|
-
if (testRegexes.some((r) => r.test(file)))
|
|
417
|
-
files.push(file);
|
|
418
|
-
}
|
|
419
|
-
return files;
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
// Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84
|
|
423
|
-
const defaultTestMatch = [
|
|
424
|
-
'**/__tests__/**/*.?([mc])[jt]s?(x)',
|
|
425
|
-
'**/?(*.)+(spec|test).?([mc])[jt]s?(x)',
|
|
426
|
-
];
|
|
427
|
-
return await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, defaultTestMatch.map((pattern) => (0, path_1.join)(projectRoot, pattern)), []);
|
|
428
|
-
}
|
|
448
|
+
let paths = await (0, workspace_context_1.globWithWorkspaceContext)(context.workspaceRoot, (testMatch || [
|
|
449
|
+
// Default copied from https://github.com/jestjs/jest/blob/d1a2ed7/packages/jest-config/src/Defaults.ts#L84
|
|
450
|
+
'**/__tests__/**/*.?([mc])[jt]s?(x)',
|
|
451
|
+
'**/?(*.)+(spec|test).?([mc])[jt]s?(x)',
|
|
452
|
+
]).map((pattern) => (0, path_1.join)(projectRoot, pattern)), []);
|
|
453
|
+
const testRegex = await getJestOption(rawConfig, absConfigFilePath, 'testRegex', presetCache);
|
|
454
|
+
if (testRegex) {
|
|
455
|
+
const testRegexes = Array.isArray(rawConfig.testRegex)
|
|
456
|
+
? rawConfig.testRegex.map((r) => new RegExp(r))
|
|
457
|
+
: [new RegExp(rawConfig.testRegex)];
|
|
458
|
+
paths = paths.filter((path) => testRegexes.some((r) => r.test(path)));
|
|
429
459
|
}
|
|
460
|
+
return paths;
|
|
430
461
|
}
|
|
431
462
|
async function getJestOption(rawConfig, absConfigFilePath, optionName, presetCache) {
|
|
432
463
|
if (rawConfig[optionName])
|