Skip to content

Commit

Permalink
fix(testing): update tsconfig.spec.json for next apps with project pa…
Browse files Browse the repository at this point in the history
…rserOptions

fixes: #9982
  • Loading branch information
barbados-clemens committed May 11, 2022
1 parent 3d7f243 commit 59ba748
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,20 @@ exports[`Jest Migration (v14.0.0) should update jest.config.ts preset to use the
};
"
`;

exports[`Jest Migration (v14.0.0) should update the excludes of next js apps using the project parser settings 1`] = `
Object {
"files": Array [
"*.ts",
"*.tsx",
"*.js",
"*.jsx",
],
"parserOptions": Object {
"project": Array [
"libs/lib-one/tsconfig.*?.json",
],
},
"rules": Object {},
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,62 @@ import { jestInitGenerator } from '@nrwl/jest';
import { libraryGenerator as workspaceLib } from '@nrwl/workspace';
import { updateJestConfigExt } from './update-jest-config-ext';

const setupDefaults = {
js: true,
skipPackageJson: true,
libName: 'lib-one',
setParserOptionsProject: false,
};

async function libSetUp(tree: Tree, options = setupDefaults) {
jestInitGenerator(tree, {
js: options.js,
skipPackageJson: options.skipPackageJson,
});
await workspaceLib(tree, {
name: options.libName,
setParserOptionsProject: options.setParserOptionsProject,
});
tree.rename(
`libs/${options.libName}/jest.config.ts`,
`libs/${options.libName}/jest.config.js`
);
const config = tree.read(`libs/${options.libName}/jest.config.js`, 'utf-8');
tree.write(
`libs/${options.libName}/jest.config.js`,
config.replace(
/\/\/ eslint-disable-next-line @typescript-eslint\/naming-convention/g,
''
)
);
updateProjectConfiguration(tree, options.libName, {
...readProjectConfiguration(tree, options.libName),
targets: {
test: {
executor: '@nrwl/jest:jest',
options: {
jestConfig: `libs/${options.libName}/jest.config.js`,
passWithNoTests: true,
},
configurations: {
production: {
silent: true
}
},
}
},
});
}

describe('Jest Migration (v14.0.0)', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace(2);
jestInitGenerator(tree, { js: true, skipPackageJson: true });
await workspaceLib(tree, { name: 'lib-one' });
const content = tree.read('libs/lib-one/jest.config.ts', 'utf-8');
tree.write(
'libs/lib-one/jest.config.ts',
content.replace('export default', 'module.exports =')
);
tree.rename('libs/lib-one/jest.config.ts', 'libs/lib-one/jest.config.js');
updateProjectConfiguration(tree, 'lib-one', {
...readProjectConfiguration(tree, 'lib-one'),
targets: {
test: {
executor: '@nrwl/jest:jest',
options: {
jestConfig: 'libs/lib-one/jest.config.js',
passWithNoTests: true,
},
configurations: {
production: {
silent: true,
},
},
},
},
});
});

it('should rename project jest.config.js to jest.config.ts', async () => {
await libSetUp(tree);

await updateJestConfigExt(tree);
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
expect(tree.read('libs/lib-one/jest.config.ts', 'utf-8')).toMatchSnapshot();
Expand All @@ -54,6 +78,8 @@ describe('Jest Migration (v14.0.0)', () => {
});

it('should NOT update jest.config.ts preset', async () => {
await libSetUp(tree);

tree.rename('libs/lib-one/jest.config.js', 'libs/lib-one/jest.config.ts');
const projectConfig = readProjectConfiguration(tree, 'lib-one');
updateProjectConfiguration(tree, 'lib-one', {
Expand All @@ -75,6 +101,8 @@ describe('Jest Migration (v14.0.0)', () => {
});

it('should only update js/ts files', async () => {
await libSetUp(tree);

tree.rename('libs/lib-one/jest.config.js', 'libs/lib-one/jest.config.ts');
updateProjectConfiguration(tree, 'lib-one', {
...readProjectConfiguration(tree, 'lib-one'),
Expand All @@ -89,7 +117,7 @@ describe('Jest Migration (v14.0.0)', () => {
},
});

await workspaceLib(tree, { name: 'lib-two' });
await libSetUp(tree, { ...setupDefaults, libName: 'lib-two' });
tree.delete('libs/lib-two/jest.config.ts'); // lib generator creates a ts file
tree.write('libs/lib-two/jest.config.json', '{}');
updateProjectConfiguration(tree, 'lib-two', {
Expand All @@ -104,8 +132,8 @@ describe('Jest Migration (v14.0.0)', () => {
},
},
});
await workspaceLib(tree, { name: 'lib-three' });

await libSetUp(tree, { ...setupDefaults, libName: 'lib-three' });
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
await updateJestConfigExt(tree);
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
Expand All @@ -115,13 +143,17 @@ describe('Jest Migration (v14.0.0)', () => {
});

it('should not throw error if file does not exit', async () => {
await libSetUp(tree);

tree.delete('libs/lib-one/jest.config.js');
await updateJestConfigExt(tree);
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeFalsy();
expect(tree.exists('libs/lib-one/jest.config.js')).toBeFalsy();
});

it('should update correct tsconfigs', async () => {
await libSetUp(tree);

updateJson(tree, 'libs/lib-one/tsconfig.lib.json', (json) => {
json.exclude = ['**/*.spec.ts'];
return json;
Expand All @@ -145,6 +177,8 @@ describe('Jest Migration (v14.0.0)', () => {
});

it('should add exclude to root tsconfig with no references', async () => {
await libSetUp(tree);

tree.delete('libs/lib-one/tsconfig.spec.json');
tree.delete('libs/lib-one/tsconfig.lib.json');

Expand All @@ -161,4 +195,28 @@ describe('Jest Migration (v14.0.0)', () => {
expect(tree.exists('libs/lib-one/tsconfig.spec.json')).toBeFalsy();
expect(tree.exists('libs/lib-one/tsconfig.lib.json')).toBeFalsy();
});

it('should update the excludes of next js apps using the project parser settings', async () => {
await libSetUp(tree, { ...setupDefaults, setParserOptionsProject: true });

const projectConfig = readProjectConfiguration(tree, 'lib-one');
projectConfig.targets['build'] = {
executor: '@nrwl/next:build',
options: {},
};
updateProjectConfiguration(tree, 'lib-one', projectConfig);
updateJson(tree, 'libs/lib-one/tsconfig.json', (json) => {
// simulate nextJS tsconfig;
json.exclude = ['node_modules'];
return json;
});
const esLintJson = readJson(tree, 'libs/lib-one/.eslintrc.json');
// make sure the parserOptions are set correctly
expect(esLintJson.overrides[0]).toMatchSnapshot();

await updateJestConfigExt(tree);

const tsconfigSpec = readJson(tree, 'libs/lib-one/tsconfig.spec.json');
expect(tsconfigSpec.exclude).toEqual(['node_modules']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,44 @@ function isJestConfigValid(tree: Tree, options: JestExecutorOptions) {
function updateTsconfigSpec(
tree: Tree,
projectConfig: ProjectConfiguration,
path
path,
options: { isNextWithProjectParse: boolean; tsConfigPath: string } = {
isNextWithProjectParse: false,
tsConfigPath: '',
}
) {
updateJson(tree, joinPathFragments(projectConfig.root, path), (json) => {
json.include = Array.from(
new Set([...(json.include || []), 'jest.config.ts'])
);
if (options.isNextWithProjectParse) {
const tsConfig = readJson(tree, options.tsConfigPath);
const tsConfigExclude = (tsConfig.exclude || []).filter(
(e) => e !== 'jest.config.ts'
);
json.exclude = Array.from(
new Set([...(json.exclude || []), ...tsConfigExclude])
);
}
return json;
});
}

function isNextWithProjectLint(
projectConfig: ProjectConfiguration,
esLintJson: any
) {
const esLintOverrides = esLintJson?.overrides?.find((o) =>
['*.ts', '*.tsx', '*.js', '*.jsx'].every((ext) => o.files.includes(ext))
);

// check if it's a next app and has a parserOptions.project set in the eslint overrides
return !!(
projectConfig?.targets?.['build']?.executor === '@nrwl/next:build' &&
esLintOverrides?.parserOptions?.project
);
}

export async function updateJestConfigExt(tree: Tree) {
if (tree.exists('jest.config.js')) {
tree.rename('jest.config.js', 'jest.config.ts');
Expand Down Expand Up @@ -98,7 +126,19 @@ export async function updateJestConfigExt(tree: Tree) {
if (tsConfig.references) {
for (const { path } of tsConfig.references) {
if (path.endsWith('tsconfig.spec.json')) {
updateTsconfigSpec(tree, projectConfig, path);
const eslintPath = joinPathFragments(
projectConfig.root,
'.eslintrc.json'
);
updateTsconfigSpec(tree, projectConfig, path, {
isNextWithProjectParse: tree.exists(eslintPath)
? isNextWithProjectLint(
projectConfig,
readJson(tree, eslintPath)
)
: false,
tsConfigPath: filePath,
});
continue;
}

Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/generators/application/lib/add-jest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,20 @@ export async function addJest(host: Tree, options: NormalizedSchema) {
}
);

const tsConfig = readJson(
host,
joinPathFragments(options.appProjectRoot, 'tsconfig.json')
);

updateJson(
host,
joinPathFragments(options.appProjectRoot, 'tsconfig.spec.json'),
(json) => {
json.compilerOptions.jsx = 'react';
// have to override exclude otherwise lint will fail with setParserOptionsProject and jest.config.ts
if (options.setParserOptionsProject) {
json.exclude = tsConfig.exclude.filter((e) => e !== 'jest.config.ts');
}
return json;
}
);
Expand Down

0 comments on commit 59ba748

Please sign in to comment.