fix(testing): deprecate cypress tsconfig option (#17165)

This commit is contained in:
Caleb Ukle 2023-06-21 10:40:25 -05:00 committed by GitHub
parent 5af50b966e
commit f74ff4bddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 285 additions and 6 deletions

View File

@ -32,6 +32,7 @@
"default": false
},
"tsConfig": {
"x-deprecated": "This option no longer has any effect. Cypress supports typescript out of the box. Add any options directly to <projectRoot>/tsconfig.json or <projectRoot>/cypress/tsconfig.json",
"type": "string",
"description": "The path of the Cypress tsconfig configuration json file.",
"x-completion-type": "file",

View File

@ -53,6 +53,12 @@
"version": "16.2.0-beta.0",
"description": "Normalize tsconfig.cy.json files to be located at '<projectRoot>/cypress/tsconfig.json'",
"implementation": "./src/migrations/update-16-2-0/update-cy-tsconfig"
},
"update-16-3-0-remove-old-tsconfigs": {
"cli": "nx",
"version": "16.4.0-beta.10",
"description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.",
"implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps"
}
},
"packageJsonUpdates": {

View File

@ -24,7 +24,6 @@ export type Json = { [k: string]: any };
export interface CypressExecutorOptions extends Json {
cypressConfig: string;
watch?: boolean;
tsConfig?: string;
devServerTarget?: string;
headed?: boolean;
/**
@ -91,11 +90,6 @@ function normalizeOptions(
context: ExecutorContext
): NormalizedCypressExecutorOptions {
options.env = options.env || {};
if (options.tsConfig) {
const tsConfigPath = join(context.root, options.tsConfig);
options.env.tsConfig = tsConfigPath;
process.env.TS_NODE_PROJECT = tsConfigPath;
}
if (options.testingType === 'component') {
const project = context?.projectGraph?.nodes?.[context.projectName];
if (project?.data?.root) {

View File

@ -32,6 +32,7 @@
"default": false
},
"tsConfig": {
"x-deprecated": "This option no longer has any effect. Cypress supports typescript out of the box. Add any options directly to <projectRoot>/tsconfig.json or <projectRoot>/cypress/tsconfig.json",
"type": "string",
"description": "The path of the Cypress tsconfig configuration json file.",
"x-completion-type": "file",

View File

@ -0,0 +1,175 @@
import {
Tree,
addProjectConfiguration,
readJson,
readProjectConfiguration,
updateJson,
updateProjectConfiguration,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
import { fixLegacyCypressTsconfig } from './tsconfig-sourcemaps';
describe('Cypress Migration: tsconfig-sourcemaps', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should remove tsconfig.e2e.json and update tsconfig.json', async () => {
addLegacyProject(tree);
await fixLegacyCypressTsconfig(tree);
expect(readProjectConfiguration(tree, 'legacy-e2e').targets.e2e)
.toMatchInlineSnapshot(`
{
"configurations": {
"production": {
"devServerTarget": "legacy-e2e:serve:production",
},
},
"executor": "@nx/cypress:cypress",
"options": {
"cypressConfig": "apps/legacy-e2e/cypress.config.ts",
"devServerTarget": "legacy-e2e:serve",
"testingType": "e2e",
},
}
`);
expect(readJson(tree, 'apps/legacy-e2e/tsconfig.json'))
.toMatchInlineSnapshot(`
{
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"resolveJsonModule": true,
"sourceMap": false,
"strict": true,
"types": [
"cypress",
"node",
],
},
"exclude": [],
"extends": "../../tsconfig.base.json",
"files": [],
"include": [
"cypress.ci.1.config.ts",
"cypress.ci.2.config.ts",
"cypress.ci.3.config.ts",
"cypress.ci.4.config.ts",
"src/**/*.ts",
"cypress.config.ts",
],
"references": [],
}
`);
expect(tree.exists('apps/legacy-e2e/tsconfig.e2e.json')).toBeFalsy();
expect(tree.exists('apps/legacy-e2e/tsconfig.e2e.prod.json')).toBeFalsy();
});
it('should do nothing if tsconfig option is not used', async () => {
addLegacyProject(tree);
tree.delete('apps/legacy-e2e/tsconfig.e2e.json');
tree.delete('apps/legacy-e2e/tsconfig.e2e.prod.json');
const pc = readProjectConfiguration(tree, 'legacy-e2e');
delete pc.targets.e2e.options.tsConfig;
delete pc.targets.e2e.configurations;
const existingProjectConfig = pc.targets.e2e;
updateProjectConfiguration(tree, 'legacy-e2e', pc);
updateJson(tree, 'apps/legacy-e2e/tsconfig.json', (json) => {
json.references = [];
return json;
});
const existingTsConfig = readJson(tree, 'apps/legacy-e2e/tsconfig.json');
await fixLegacyCypressTsconfig(tree);
expect(readProjectConfiguration(tree, 'legacy-e2e').targets.e2e).toEqual(
existingProjectConfig
);
expect(readJson(tree, 'apps/legacy-e2e/tsconfig.json')).toEqual(
existingTsConfig
);
});
});
function addLegacyProject(tree: Tree) {
addProjectConfiguration(tree, 'legacy-e2e', {
root: 'apps/legacy-e2e',
sourceRoot: 'apps/legacy-e2e/src',
targets: {
e2e: {
executor: '@nx/cypress:cypress',
options: {
cypressConfig: 'apps/legacy-e2e/cypress.config.ts',
tsConfig: 'apps/legacy-e2e/tsconfig.e2e.json',
devServerTarget: 'legacy-e2e:serve',
testingType: 'e2e',
},
configurations: {
production: {
devServerTarget: 'legacy-e2e:serve:production',
tsConfig: 'apps/legacy-e2e/tsconfig.e2e.prod.json',
},
},
},
},
});
tree.write(
'apps/legacy-e2e/tsconfig.e2e.json',
JSON.stringify({
extends: './tsconfig.json',
compilerOptions: {
sourceMap: false,
outDir: '../../dist/out-tsc',
types: ['cypress', 'node'],
},
include: ['src/**/*.ts', 'cypress.config.ts'],
})
);
tree.write(
'apps/legacy-e2e/tsconfig.e2e.prod.json',
JSON.stringify({
extends: './tsconfig.e2e.json',
compilerOptions: {
sourceMap: false,
outDir: '../../dist/out-tsc',
types: ['cypress', 'node'],
strict: true,
},
include: ['src/**/*.ts', 'cypress.config.ts'],
})
);
tree.write(
'apps/legacy-e2e/tsconfig.json',
JSON.stringify({
extends: '../../tsconfig.base.json',
compilerOptions: {
types: ['cypress', 'node'],
resolveJsonModule: true,
},
include: [
'cypress.ci.1.config.ts',
'cypress.ci.2.config.ts',
'cypress.ci.3.config.ts',
'cypress.ci.4.config.ts',
],
files: [],
references: [
{
path: './tsconfig.e2e.json',
},
{
path: './tsconfig.e2e.prod.json',
},
],
})
);
}

View File

@ -0,0 +1,102 @@
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
import {
updateJson,
Tree,
getProjects,
joinPathFragments,
readJson,
updateProjectConfiguration,
formatFiles,
} from '@nx/devkit';
import { posix } from 'path';
export async function fixLegacyCypressTsconfig(tree: Tree) {
const projects = getProjects(tree);
forEachExecutorOptions<CypressExecutorOptions>(
tree,
'@nx/cypress:cypress',
(options, projectName, targetName, configName) => {
const projectConfig = projects.get(projectName);
if (
options.testingType !== 'e2e' &&
projectConfig.targets[targetName]?.options?.testingType !== 'e2e'
) {
return;
}
const tsconfigToRemove =
options.tsConfig ??
joinPathFragments(projectConfig.root, 'tsconfig.e2e.json');
const projectLevelConfigPath = joinPathFragments(
projectConfig.root,
'tsconfig.json'
);
if (
!tree.exists(projectLevelConfigPath) ||
!tree.exists(tsconfigToRemove)
) {
return;
}
if (tsconfigToRemove === projectLevelConfigPath) {
updateJson(tree, projectLevelConfigPath, (json) => {
json.compilerOptions = {
sourceMap: false,
...json.compilerOptions,
};
return json;
});
} else {
const e2eConfig = readJson(tree, tsconfigToRemove);
updateJson(tree, projectLevelConfigPath, (json) => {
json.compilerOptions = {
sourceMap: false,
...json.compilerOptions,
...e2eConfig.compilerOptions,
};
json.files = Array.from(
new Set([...(json.files ?? []), ...(e2eConfig.files ?? [])])
);
json.include = Array.from(
new Set([...(json.include ?? []), ...(e2eConfig.include ?? [])])
);
json.exclude = Array.from(
new Set([...(json.exclude ?? []), ...(e2eConfig.exclude ?? [])])
);
// these paths will always be 'unix style'
// and on windows relative will not work on these paths
const tsConfigFromProjRoot = posix.relative(
projectConfig.root,
tsconfigToRemove
);
json.references = (json.references ?? []).filter(
({ path }) => !path.includes(tsConfigFromProjRoot)
);
return json;
});
tree.delete(tsconfigToRemove);
}
if (configName) {
delete projectConfig.targets[targetName].configurations[configName]
.tsConfig;
} else {
delete projectConfig.targets[targetName].options.tsConfig;
}
updateProjectConfiguration(tree, projectName, projectConfig);
}
);
await formatFiles(tree);
}
export default fixLegacyCypressTsconfig;