Skip to content

Commit

Permalink
fix(jest-config): handle frozen config object (#14054)
Browse files Browse the repository at this point in the history
* fix(jest-config): handle frozen config object

* change log entry
  • Loading branch information
mrazauskas committed Apr 5, 2023
1 parent 8a9609d commit 08ef105
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### Fixes

- `[jest-config]` Handle frozen config object ([#14054](https://github.com/facebook/jest/pull/14054))
- `[jest-environment-jsdom, jest-environment-node]` Fix assignment of `customExportConditions` via `testEnvironmentOptions` when custom env subclass defines a default value ([#13989](https://github.com/facebook/jest/pull/13989))
- `[jest-matcher-utils]` Fix copying value of inherited getters ([#14007](https://github.com/facebook/jest/pull/14007))
- `[jest-snapshot]` Fix a potential bug when not using prettier and improve performance ([#14036](https://github.com/facebook/jest/pull/14036))
Expand Down
169 changes: 169 additions & 0 deletions packages/jest-config/src/__tests__/readConfigFileAndSetRootDir.test.ts
@@ -0,0 +1,169 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import path = require('path');
import * as fs from 'graceful-fs';
import {requireOrImportModule} from 'jest-util';
import readConfigFileAndSetRootDir from '../readConfigFileAndSetRootDir';

jest.mock('graceful-fs').mock('jest-util');

describe('readConfigFileAndSetRootDir', () => {
describe('JavaScript file', () => {
test('reads config and sets `rootDir`', async () => {
jest.mocked(requireOrImportModule).mockResolvedValueOnce({notify: true});

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.js'),
);

expect(config).toEqual({notify: true, rootDir});
});

test('handles exported function', async () => {
jest
.mocked(requireOrImportModule)
.mockResolvedValueOnce(() => ({bail: 1}));

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.js'),
);

expect(config).toEqual({bail: 1, rootDir});
});

test('handles exported async function', async () => {
jest
.mocked(requireOrImportModule)
.mockResolvedValueOnce(async () => ({testTimeout: 10000}));

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.js'),
);

expect(config).toEqual({rootDir, testTimeout: 10000});
});
});

describe('JSON file', () => {
test('reads config and sets `rootDir`', async () => {
jest.mocked(fs.readFileSync).mockReturnValueOnce('{ "verbose": true }');

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.json'),
);

expect(config).toEqual({rootDir, verbose: true});
});

test('supports comments in JSON', async () => {
jest
.mocked(fs.readFileSync)
.mockReturnValueOnce('{ // test comment\n "bail": true }');

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.json'),
);

expect(config).toEqual({bail: true, rootDir});
});
});

describe('package.json file', () => {
test('reads config from "jest" key and sets `rootDir`', async () => {
jest
.mocked(fs.readFileSync)
.mockReturnValueOnce('{ "jest": { "coverage": true } }');

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'package.json'),
);

expect(config).toEqual({coverage: true, rootDir});
});

test('sets rootDir if "jest" is absent', async () => {
jest.mocked(fs.readFileSync).mockReturnValueOnce('{ "name": "test" }');

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'package.json'),
);

expect(config).toEqual({rootDir});
});
});

describe('sets `rootDir`', () => {
test('handles frozen config object', async () => {
jest
.mocked(requireOrImportModule)
.mockResolvedValueOnce(Object.freeze({preset: 'some-preset'}));

const rootDir = path.resolve('some', 'path', 'to');
const config = await readConfigFileAndSetRootDir(
path.join(rootDir, 'jest.config.js'),
);

expect(config).toEqual({preset: 'some-preset', rootDir});
});

test('keeps the path if it is absolute', async () => {
const rootDir = path.resolve('some', 'path', 'to');
jest.mocked(requireOrImportModule).mockResolvedValueOnce({
rootDir,
testEnvironment: 'node',
});

const config = await readConfigFileAndSetRootDir(
path.join(path.resolve('other', 'path', 'to'), 'jest.config.js'),
);

expect(config).toEqual({rootDir, testEnvironment: 'node'});
});

test('resolves the path relative to dirname of the config file', async () => {
jest.mocked(requireOrImportModule).mockResolvedValueOnce({
restoreMocks: true,
rootDir: path.join('path', 'to'),
});

const config = await readConfigFileAndSetRootDir(
path.join(path.resolve('some'), 'jest.config.js'),
);

expect(config).toEqual({
restoreMocks: true,
rootDir: path.resolve('some', 'path', 'to'),
});
});

test('resolves relative path when the read config object if frozen', async () => {
jest.mocked(requireOrImportModule).mockResolvedValueOnce(
Object.freeze({
resetModules: true,
rootDir: path.join('path', 'to'),
}),
);

const config = await readConfigFileAndSetRootDir(
path.join(path.resolve('some'), 'jest.config.js'),
);

expect(config).toEqual({
resetModules: true,
rootDir: path.resolve('some', 'path', 'to'),
});
});
});
});
13 changes: 8 additions & 5 deletions packages/jest-config/src/readConfigFileAndSetRootDir.ts
Expand Up @@ -64,14 +64,17 @@ export default async function readConfigFileAndSetRootDir(
// We don't touch it if it has an absolute path specified
if (!path.isAbsolute(configObject.rootDir)) {
// otherwise, we'll resolve it relative to the file's __dirname
configObject.rootDir = path.resolve(
path.dirname(configPath),
configObject.rootDir,
);
configObject = {
...configObject,
rootDir: path.resolve(path.dirname(configPath), configObject.rootDir),
};
}
} else {
// If rootDir is not there, we'll set it to this file's __dirname
configObject.rootDir = path.dirname(configPath);
configObject = {
...configObject,
rootDir: path.dirname(configPath),
};
}

return configObject;
Expand Down

0 comments on commit 08ef105

Please sign in to comment.