Skip to content

Commit ca43171

Browse files
dgp1130clydin
authored andcommitted
refactor: add node: prefix to imports of Jest builder
1 parent d158db1 commit ca43171

File tree

3 files changed

+115
-4
lines changed

3 files changed

+115
-4
lines changed

packages/angular_devkit/build_angular/src/builders/jest/index.ts

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
*/
88

99
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
10-
import { execFile as execFileCb } from 'child_process';
11-
import * as path from 'path';
12-
import { promisify } from 'util';
10+
import { execFile as execFileCb } from 'node:child_process';
11+
import * as fs from 'node:fs/promises';
12+
import * as path from 'node:path';
13+
import { promisify } from 'node:util';
1314
import { colors } from '../../utils/color';
1415
import { findTestFiles } from '../../utils/test-files';
1516
import { buildApplicationInternal } from '../application';
@@ -54,8 +55,25 @@ export default createBuilder(
5455
};
5556
}
5657

58+
const [testFiles, customConfig] = await Promise.all([
59+
findTestFiles(options.include, options.exclude, context.workspaceRoot),
60+
findCustomJestConfig(context.workspaceRoot),
61+
]);
62+
63+
// Warn if a custom Jest configuration is found. We won't use it, so if a developer is trying to use a custom config, this hopefully
64+
// makes a better experience than silently ignoring the configuration.
65+
// Ideally, this would be a hard error. However a Jest config could exist for testing other files in the workspace outside of Angular
66+
// CLI, so we likely can't produce a hard error in this situation without an opt-out.
67+
if (customConfig) {
68+
context.logger.warn(
69+
'A custom Jest config was found, but this is not supported by `@angular-devkit/build-angular:jest` and will be' +
70+
` ignored: ${customConfig}. This is an experiment to see if completely abstracting away Jest's configuration is viable. Please` +
71+
` consider if your use case can be met without directly modifying the Jest config. If this is a major obstacle for your use` +
72+
` case, please post it in this issue so we can collect feedback and evaluate: https://github.com/angular/angular-cli/issues/25434.`,
73+
);
74+
}
75+
5776
// Build all the test files.
58-
const testFiles = await findTestFiles(options.include, options.exclude, context.workspaceRoot);
5977
const jestGlobal = path.join(__dirname, 'jest-global.mjs');
6078
const initTestBed = path.join(__dirname, 'init-test-bed.mjs');
6179
const buildResult = await build(context, {
@@ -85,6 +103,7 @@ export default createBuilder(
85103
jest,
86104

87105
`--rootDir="${path.join(testOut, 'browser')}"`,
106+
`--config=${path.join(__dirname, 'jest.config.mjs')}`,
88107
'--testEnvironment=jsdom',
89108

90109
// TODO(dgp1130): Enable cache once we have a mechanism for properly clearing / disabling it.
@@ -162,3 +181,31 @@ function resolveModule(module: string): string | undefined {
162181
return undefined;
163182
}
164183
}
184+
185+
/** Returns whether or not the provided directory includes a Jest configuration file. */
186+
async function findCustomJestConfig(dir: string): Promise<string | undefined> {
187+
const entries = await fs.readdir(dir, { withFileTypes: true });
188+
189+
// Jest supports many file extensions (`js`, `ts`, `cjs`, `cts`, `json`, etc.) Just look
190+
// for anything with that prefix.
191+
const config = entries.find((entry) => entry.isFile() && entry.name.startsWith('jest.config.'));
192+
if (config) {
193+
return path.join(dir, config.name);
194+
}
195+
196+
// Jest also supports a `jest` key in `package.json`, look for a config there.
197+
const packageJsonPath = path.join(dir, 'package.json');
198+
let packageJson: string | undefined;
199+
try {
200+
packageJson = await fs.readFile(packageJsonPath, 'utf8');
201+
} catch {
202+
return undefined; // No package.json, therefore no Jest configuration in it.
203+
}
204+
205+
const json = JSON.parse(packageJson);
206+
if ('jest' in json) {
207+
return packageJsonPath;
208+
}
209+
210+
return undefined;
211+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// Empty config file, everything is specified via CLI options right now.
10+
// This file is used just so Jest doesn't accidentally inherit a custom user-specified Jest config.
11+
export default {};
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { deleteFile, writeFile } from '../../utils/fs';
2+
import { applyJestBuilder } from '../../utils/jest';
3+
import { ng } from '../../utils/process';
4+
import { updateJsonFile } from '../../utils/project';
5+
6+
export default async function (): Promise<void> {
7+
await applyJestBuilder();
8+
9+
{
10+
// Users may incorrectly write a Jest config believing it to be used by Angular.
11+
await writeFile(
12+
'jest.config.mjs',
13+
`
14+
export default {
15+
runner: 'does-not-exist',
16+
};
17+
`.trim(),
18+
);
19+
20+
// Should not fail from the above (broken) configuration. Shouldn't use it at all.
21+
const { stderr } = await ng('test');
22+
23+
// Should warn that a Jest configuration was found but not used.
24+
if (!stderr.includes('A custom Jest config was found')) {
25+
throw new Error(`No warning about custom Jest config:\nSTDERR:\n\n${stderr}`);
26+
}
27+
if (!stderr.includes('jest.config.mjs')) {
28+
throw new Error(`Warning did not call out 'jest.config.mjs':\nSTDERR:\n\n${stderr}`);
29+
}
30+
31+
await deleteFile('jest.config.mjs');
32+
}
33+
34+
{
35+
// Use `package.json` configuration instead of a `jest.config` file.
36+
await updateJsonFile('package.json', (json) => {
37+
json['jest'] = {
38+
runner: 'does-not-exist',
39+
};
40+
});
41+
42+
// Should not fail from the above (broken) configuration. Shouldn't use it at all.
43+
const { stderr } = await ng('test');
44+
45+
// Should warn that a Jest configuration was found but not used.
46+
if (!stderr.includes('A custom Jest config was found')) {
47+
throw new Error(`No warning about custom Jest config:\nSTDERR:\n\n${stderr}`);
48+
}
49+
if (!stderr.includes('package.json')) {
50+
throw new Error(`Warning did not call out 'package.json':\nSTDERR:\n\n${stderr}`);
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)