Skip to content

Commit c8681bf

Browse files
committed
fix: ignore custom Jest configuration file and warn if one is found
Currently with Jest we're experimenting with whether or not Angular CLI can meet user needs without exposing the underlying configuration file. See angular#25434 (comment) for more context. Jest was incorrectly picking up custom configurations, even though they exist outside Jest's root directory. This commit fixes that behavior so Jest does not see user configurations and does not apply them. Ideally we would throw an error if a Jest configuration is found to be more clear that it isn't doing what developers expect. However they may use a Jest config to run other projects in the same workspace outside of Angular CLI and we don't want to prevent that. A warning seems like the best trade off of notifying users that a custom config isn't supported while also not preventing unrelated Jest usage in the same workspace.
1 parent f2f0ac4 commit c8681bf

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
1010
import { execFile as execFileCb } from 'child_process';
11+
import { promises as fs } from 'fs';
1112
import * as path from 'path';
1213
import { promisify } from 'util';
1314
import { colors } from '../../utils/color';
@@ -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,17 @@ 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 undefined;
194+
}
195+
196+
return path.join(dir, config.name);
197+
}
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: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { writeFile } from '../../utils/fs';
2+
import { applyJestBuilder } from '../../utils/jest';
3+
import { ng } from '../../utils/process';
4+
5+
export default async function (): Promise<void> {
6+
await applyJestBuilder();
7+
8+
// Users may incorrectly write a Jest config believing it to be used by Angular.
9+
await writeFile(
10+
'jest.config.mjs',
11+
`
12+
export default {
13+
runner: 'does-not-exist',
14+
};
15+
`.trim(),
16+
);
17+
18+
// Should not fail from the above (broken) configuration. Shouldn't use it at all.
19+
const { stderr } = await ng('test');
20+
21+
// Should warn that a Jest configuration was found but not used.
22+
if (!stderr.includes('A custom Jest config was found')) {
23+
throw new Error(`No warning about custom Jest config:\nSTDERR:\n\n${stderr}`);
24+
}
25+
}

0 commit comments

Comments
 (0)