feat(misc): remove usages of @nx/cypress:cypress-project internally (#19766)

This commit is contained in:
Jack Hsu 2023-10-24 21:04:15 -04:00 committed by GitHub
parent 1389fc0dab
commit b5ed979b90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 252 additions and 153 deletions

View File

@ -33,6 +33,11 @@
"type": "string",
"description": "A directory where the project is placed relative from the project root",
"default": "cypress"
},
"jsx": {
"description": "Whether or not this project uses JSX.",
"type": "boolean",
"default": true
}
},
"required": ["project"],

View File

@ -81,6 +81,11 @@
"enum": ["vite", "webpack", "none"],
"x-prompt": "Which Cypress bundler do you want to use?",
"default": "webpack"
},
"jsx": {
"description": "Whether or not this project uses JSX.",
"type": "boolean",
"default": true
}
},
"required": ["project"],

View File

@ -57,12 +57,6 @@
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
},
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside workspace.json.",
"type": "boolean",
"default": true,
"x-deprecated": "Nx only supports standaloneConfig"
},
"skipPackageJson": {
"type": "boolean",
"default": false,

View File

@ -33,12 +33,6 @@
"enum": ["eslint", "none"],
"default": "eslint"
},
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean",
"default": true,
"x-deprecated": "Nx only supports standaloneConfig"
},
"ciTargetName": {
"type": "string",
"description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using <storybook-project>:static-storybook:ci or <storybook-project>:storybook:ci",
@ -49,6 +43,11 @@
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"projectNameAndRootFormat": {
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
"type": "string",
"enum": ["as-provided", "derived"]
}
},
"required": ["name"],

View File

@ -369,6 +369,7 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
@ -383,9 +384,12 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
},
"extends": "../../../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.js",
"**/*.d.ts",
],
}
`;
@ -609,6 +613,7 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
@ -623,9 +628,12 @@ exports[`app --project-name-and-root-format=derived should generate correctly wh
},
"extends": "../../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.js",
"**/*.d.ts",
],
}
`;
@ -874,6 +882,7 @@ exports[`app --strict should enable strict type checking: e2e tsconfig.json 1`]
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
@ -888,9 +897,12 @@ exports[`app --strict should enable strict type checking: e2e tsconfig.json 1`]
},
"extends": "../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.js",
"**/*.d.ts",
],
}
`;
@ -1218,6 +1230,7 @@ exports[`app not nested should generate files: e2e tsconfig.json 1`] = `
"compilerOptions": {
"allowJs": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
@ -1232,9 +1245,12 @@ exports[`app not nested should generate files: e2e tsconfig.json 1`] = `
},
"extends": "../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.js",
"**/*.d.ts",
],
}
`;

View File

@ -10,23 +10,27 @@ import {
} from '@nx/devkit';
import { nxVersion } from '../../../utils/versions';
import type { NormalizedSchema } from './normalized-schema';
import { cypressProjectGenerator } from '@nx/cypress';
import { configurationGenerator } from '@nx/cypress';
export async function addE2e(tree: Tree, options: NormalizedSchema) {
if (options.e2eTestRunner === 'cypress') {
// TODO: This can call `@nx/web:static-config` generator when ready
addFileServerTarget(tree, options, 'serve-static');
await cypressProjectGenerator(tree, {
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
// the name and root are already normalized, instruct the generator to use them as is
projectNameAndRootFormat: 'as-provided',
project: options.name,
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.name],
});
await configurationGenerator(tree, {
project: options.e2eProjectName,
directory: 'src',
linter: options.linter,
standaloneConfig: options.standaloneConfig,
skipPackageJson: options.skipPackageJson,
skipFormat: true,
devServerTarget: `${options.name}:serve:development`,
});
} else if (options.e2eTestRunner === 'playwright') {
const { configurationGenerator: playwrightConfigurationGenerator } =
@ -35,6 +39,7 @@ export async function addE2e(tree: Tree, options: NormalizedSchema) {
nxVersion
);
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},

View File

@ -13,7 +13,7 @@ import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-
import { generateTestApplication, generateTestLibrary } from '../utils/testing';
import { cypressComponentConfiguration } from './cypress-component-configuration';
let projectGraph: ProjectGraph;
let projectGraph: ProjectGraph = { nodes: {}, dependencies: {} };
jest.mock('@nx/cypress/src/utils/cypress-version');
jest.mock('@nx/devkit', () => ({
...jest.requireActual<any>('@nx/devkit'),

View File

@ -337,11 +337,11 @@ export class E2eMigrator extends ProjectMigrator<SupportedTargets> {
private async migrateCypressE2eProject(): Promise<void> {
const oldCypressConfigFilePath = this.getOldCypressConfigFilePath();
// TODO(v18): This needs to be removed before v18 since the generator is going away.
await cypressProjectGenerator(this.tree, {
name: this.project.name,
project: this.appName,
linter: this.isProjectUsingEsLint ? Linter.EsLint : Linter.None,
standaloneConfig: true,
skipFormat: true,
});

View File

@ -1,12 +1,17 @@
import { configurationGenerator } from './src/generators/configuration/configuration';
import { componentConfigurationGenerator } from './src/generators/component-configuration/component-configuration';
import { cypressProjectGenerator as _cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
export { configurationGenerator, componentConfigurationGenerator };
// Maintain backwards compatibility with the old names in case community plugins used them.
/** @deprecated Use `configurationGenerator` instead. */
// TODO(v18): Remove old name
/** @deprecated Use `configurationGenerator` instead. It will be removed in Nx 18. */
export const cypressComponentConfiguration = componentConfigurationGenerator;
export { configurationGenerator as cypressE2EConfigurationGenerator };
export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
// TODO(v18): Remove project generator
/** @deprecated Add a new project and call `configurationGenerator` instead. It will be removed in Nx 18. */
export const cypressProjectGenerator = _cypressProjectGenerator;
export { cypressInitGenerator } from './src/generators/init/init';
export { migrateCypressProject } from './src/generators/migrate-to-cypress-11/migrate-to-cypress-11';

View File

@ -17,6 +17,7 @@ export interface CypressBaseSetupSchema {
* default is `cypress`
* */
directory?: string;
jsx?: boolean;
}
export function addBaseCypressSetup(
@ -33,6 +34,7 @@ export function addBaseCypressSetup(
generateFiles(tree, join(__dirname, 'files'), projectConfig.root, {
...opts,
jsx: !!opts.jsx,
offsetFromRoot: offsetFromRoot(projectConfig.root),
offsetFromProjectRoot: opts.hasTsConfig ? opts.offsetFromProjectRoot : '',
tsConfigPath: opts.hasTsConfig

View File

@ -12,9 +12,9 @@
"**/*.js",
"<%= offsetFromProjectRoot %>cypress.config.ts",
"<%= offsetFromProjectRoot %>**/*.cy.ts",
"<%= offsetFromProjectRoot %>**/*.cy.tsx",
<% if (jsx) { %> "<%= offsetFromProjectRoot %>**/*.cy.tsx",<% } %>
"<%= offsetFromProjectRoot %>**/*.cy.js",
"<%= offsetFromProjectRoot %>**/*.cy.jsx",
<% if (jsx) { %>"<%= offsetFromProjectRoot %>**/*.cy.jsx",<% } %>
"<%= offsetFromProjectRoot %>**/*.d.ts"
]
}

View File

@ -100,6 +100,7 @@ describe('Cypress Component Configuration', () => {
await componentConfigurationGenerator(tree, {
project: 'cool-lib',
skipFormat: false,
jsx: true,
});
const projectConfig = readProjectConfiguration(tree, 'cool-lib');
expect(tree.exists('libs/cool-lib/cypress.config.ts')).toEqual(true);

View File

@ -85,6 +85,7 @@ function addProjectFiles(
addBaseCypressSetup(tree, {
project: opts.project,
directory: opts.directory,
jsx: opts.jsx,
});
generateFiles(

View File

@ -3,4 +3,5 @@ export interface CypressComponentConfigurationSchema {
skipFormat: boolean;
directory?: string;
bundler?: 'webpack' | 'vite';
jsx?: boolean;
}

View File

@ -31,6 +31,11 @@
"type": "string",
"description": "A directory where the project is placed relative from the project root",
"default": "cypress"
},
"jsx": {
"description": "Whether or not this project uses JSX.",
"type": "boolean",
"default": true
}
},
"required": ["project"],

View File

@ -52,11 +52,6 @@ describe('Cypress e2e configuration', () => {
expect(readProjectConfiguration(tree, 'my-app').targets.e2e)
.toMatchInlineSnapshot(`
{
"configurations": {
"production": {
"devServerTarget": "my-app:serve:production",
},
},
"executor": "@nx/cypress:cypress",
"options": {
"cypressConfig": "apps/my-app/cypress.config.ts",
@ -85,9 +80,7 @@ describe('Cypress e2e configuration', () => {
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.tsx",
"**/*.cy.js",
"**/*.cy.jsx",
"**/*.d.ts",
],
}
@ -208,9 +201,7 @@ describe('Cypress e2e configuration', () => {
"**/*.js",
"../../cypress.config.ts",
"../../**/*.cy.ts",
"../../**/*.cy.tsx",
"../../**/*.cy.js",
"../../**/*.cy.jsx",
"../../**/*.d.ts",
],
}

View File

@ -35,6 +35,7 @@ export interface CypressE2EConfigSchema {
devServerTarget?: string;
linter?: Linter;
port?: number | 'cypress-auto';
jsx?: boolean;
}
type NormalizedSchema = ReturnType<typeof normalizeOptions>;
@ -55,10 +56,12 @@ export async function configurationGenerator(
}
await addFiles(tree, opts);
addTarget(tree, opts);
addLinterToCyProject(tree, {
const linterTask = await addLinterToCyProject(tree, {
...opts,
cypressDir: opts.directory,
});
tasks.push(linterTask);
if (!opts.skipFormat) {
await formatFiles(tree);
@ -132,6 +135,7 @@ async function addFiles(tree: Tree, options: NormalizedSchema) {
addBaseCypressSetup(tree, {
project: options.project,
directory: options.directory,
jsx: options.jsx,
});
const cyFile = joinPathFragments(projectConfig.root, 'cypress.config.ts');
@ -197,18 +201,25 @@ function addTarget(tree: Tree, opts: NormalizedSchema) {
port: opts.port,
};
projectConfig.targets.e2e.configurations = {
[parsedTarget.configuration || 'production']: {
devServerTarget: `${opts.devServerTarget}${
parsedTarget.configuration ? '' : ':production'
}`,
},
};
const devServerProjectConfig = readProjectConfiguration(
tree,
parsedTarget.project
);
// Add production e2e target if serve target is found
if (
parsedTarget.configuration !== 'production' &&
devServerProjectConfig.targets?.[parsedTarget.target]?.configurations?.[
'production'
]
) {
projectConfig.targets.e2e.configurations ??= {};
projectConfig.targets.e2e.configurations['production'] = {
devServerTarget: `${parsedTarget.project}:${parsedTarget.target}:production`,
};
}
// Add ci/static e2e target if serve target is found
if (devServerProjectConfig.targets?.['serve-static']) {
projectConfig.targets.e2e.configurations ??= {};
projectConfig.targets.e2e.configurations.ci = {
devServerTarget: `${parsedTarget.project}:serve-static`,
};

View File

@ -84,6 +84,11 @@
"enum": ["vite", "webpack", "none"],
"x-prompt": "Which Cypress bundler do you want to use?",
"default": "webpack"
},
"jsx": {
"description": "Whether or not this project uses JSX.",
"type": "boolean",
"default": true
}
},
"required": ["project"],

View File

@ -11,7 +11,6 @@ export interface Schema {
js?: boolean;
skipFormat?: boolean;
setParserOptionsProject?: boolean;
standaloneConfig?: boolean;
skipPackageJson?: boolean;
bundler?: 'webpack' | 'vite' | 'none';
}

View File

@ -59,12 +59,6 @@
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
},
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside workspace.json.",
"type": "boolean",
"default": true,
"x-deprecated": "Nx only supports standaloneConfig"
},
"skipPackageJson": {
"type": "boolean",
"default": false,

View File

@ -12,18 +12,24 @@ import { NormalizedSchema } from './normalize-options';
export async function addE2e(host: Tree, options: NormalizedSchema) {
if (options.e2eTestRunner === 'cypress') {
const { cypressProjectGenerator } = ensurePackage<
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
return cypressProjectGenerator(host, {
addProjectConfiguration(host, options.e2eProjectName, {
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
return configurationGenerator(host, {
...options,
linter: Linter.EsLint,
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
// the name and root are already normalized, instruct the generator to use them as is
projectNameAndRootFormat: 'as-provided',
project: options.projectName,
project: options.e2eProjectName,
directory: 'src',
skipFormat: true,
devServerTarget: `${options.projectName}:serve`,
jsx: true,
});
} else if (options.e2eTestRunner === 'playwright') {
const { configurationGenerator } = ensurePackage<

View File

@ -29,6 +29,7 @@ export async function cypressComponentConfiguration(
await baseCyCtConfig(tree, {
project: options.project,
skipFormat: true,
jsx: true,
})
);

View File

@ -132,14 +132,14 @@ exports[`app generated files content - as-provided should create all new files i
"my-app/.eslintrc.json",
"my-app/vite.config.ts",
"my-app/tsconfig.spec.json",
"my-app-e2e/cypress.config.ts",
"my-app-e2e/src/e2e/app.cy.ts",
"my-app-e2e/src/fixtures/example.json",
"my-app-e2e/src/support/app.po.ts",
"my-app-e2e/src/support/commands.ts",
"my-app-e2e/src/support/e2e.ts",
"my-app-e2e/tsconfig.json",
"my-app-e2e/project.json",
"my-app-e2e/src/e2e/app.cy.ts",
"my-app-e2e/src/support/app.po.ts",
"my-app-e2e/src/support/e2e.ts",
"my-app-e2e/src/fixtures/example.json",
"my-app-e2e/src/support/commands.ts",
"my-app-e2e/cypress.config.ts",
"my-app-e2e/tsconfig.json",
"my-app-e2e/.eslintrc.json",
]
`;

View File

@ -12,17 +12,25 @@ import { NormalizedSchema } from '../schema';
export async function addE2e(host: Tree, options: NormalizedSchema) {
if (options.e2eTestRunner === 'cypress') {
const { cypressProjectGenerator } = ensurePackage<
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
return cypressProjectGenerator(host, {
addProjectConfiguration(host, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
return await configurationGenerator(host, {
...options,
linter: Linter.EsLint,
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
projectNameAndRootFormat: 'as-provided',
project: options.projectName,
project: options.e2eProjectName,
directory: 'src',
bundler: 'vite',
skipFormat: true,
devServerTarget: `${options.projectName}:serve`,
jsx: true,
});
} else if (options.e2eTestRunner === 'playwright') {
const { configurationGenerator } = ensurePackage<

View File

@ -143,6 +143,7 @@ describe('app', () => {
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"outDir": "../dist/out-tsc",
"sourceMap": false,
"types": [
@ -152,9 +153,14 @@ describe('app', () => {
},
"extends": "../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.tsx",
"**/*.cy.js",
"**/*.cy.jsx",
"**/*.d.ts",
],
}
`);

View File

@ -15,31 +15,42 @@ export async function addE2e(
options: NormalizedSchema
): Promise<GeneratorCallback> {
switch (options.e2eTestRunner) {
case 'cypress':
case 'cypress': {
webStaticServeGenerator(tree, {
buildTarget: `${options.projectName}:build`,
targetName: 'serve-static',
});
const { cypressProjectGenerator } = ensurePackage<
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
return await cypressProjectGenerator(tree, {
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
implicitDependencies: [options.projectName],
tags: [],
});
return await configurationGenerator(tree, {
...options,
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
project: options.e2eProjectName,
directory: 'src',
// the name and root are already normalized, instruct the generator to use them as is
projectNameAndRootFormat: 'as-provided',
project: options.projectName,
bundler: options.bundler === 'rspack' ? 'webpack' : options.bundler,
skipFormat: true,
devServerTarget: `${options.projectName}:serve`,
jsx: true,
});
case 'playwright':
}
case 'playwright': {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/playwright')
>('@nx/playwright', nxVersion);
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
@ -58,6 +69,7 @@ export async function addE2e(
}`,
webServerAddress: 'http://localhost:4200',
});
}
case 'none':
default:
return () => {};

View File

@ -25,6 +25,7 @@ export async function cypressComponentConfigGenerator(
const installTask = await baseCyCtConfig(tree, {
project: options.project,
skipFormat: true,
jsx: true,
});
const found = await addCTTargetWithBuildTarget(tree, {

View File

@ -1,19 +1,13 @@
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
import { readJson, readProjectConfiguration, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Linter } from '@nx/eslint';
import { libraryGenerator } from '@nx/js';
import { cypressProjectGenerator } from './cypress-project';
jest.mock('@nx/cypress/src/utils/cypress-version');
describe('@nx/storybook:cypress-project', () => {
let tree: Tree;
let mockedInstalledCypressVersion: jest.Mock<
ReturnType<typeof installedCypressVersion>
> = installedCypressVersion as never;
beforeEach(async () => {
mockedInstalledCypressVersion.mockReturnValue(10);
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
await libraryGenerator(tree, {
name: 'test-ui-lib',
@ -36,19 +30,6 @@ describe('@nx/storybook:cypress-project', () => {
expect(cypressConfig).toMatchSnapshot();
});
it('should update cypress.json file if present', async () => {
mockedInstalledCypressVersion.mockReturnValue(9);
await cypressProjectGenerator(tree, {
name: 'test-ui-lib',
linter: Linter.EsLint,
});
expect(tree.exists('apps/test-ui-lib-e2e/cypress.json')).toBeTruthy();
const cypressConfig = readJson(tree, 'apps/test-ui-lib-e2e/cypress.json');
expect(cypressConfig.baseUrl).toEqual('http://localhost:4400');
});
it('should update `angular.json` file', async () => {
await cypressProjectGenerator(tree, {
name: 'test-ui-lib',

View File

@ -1,15 +1,11 @@
import { getE2eProjectName } from '@nx/cypress/src/utils/project-name';
import {
cypressInitGenerator as _cypressInitGenerator,
cypressProjectGenerator as _cypressProjectGenerator,
} from '@nx/cypress';
import {
getE2eProjectName,
getUnscopedLibName,
} from '@nx/cypress/src/utils/project-name';
import {
addProjectConfiguration,
ensurePackage,
formatFiles,
generateFiles,
GeneratorCallback,
joinPathFragments,
readJson,
readProjectConfiguration,
runTasksInSerial,
@ -18,9 +14,11 @@ import {
updateProjectConfiguration,
} from '@nx/devkit';
import { Linter } from '@nx/eslint';
import { determineProjectNameAndRootOptions } from '@nx/devkit/src/generators/project-name-and-root-utils';
import { join } from 'path';
import { safeFileDelete } from '../../utils/utilities';
import { nxVersion } from '../../utils/versions';
export interface CypressConfigureSchema {
name: string;
@ -30,31 +28,61 @@ export interface CypressConfigureSchema {
standaloneConfig?: boolean;
ciTargetName?: string;
skipFormat?: boolean;
projectNameAndRootFormat?: 'as-provided' | 'derived';
}
export async function cypressProjectGenerator(
tree: Tree,
schema: CypressConfigureSchema
) {
return await cypressProjectGeneratorInternal(tree, {
projectNameAndRootFormat: 'derived',
...schema,
});
}
export async function cypressProjectGeneratorInternal(
tree: Tree,
schema: CypressConfigureSchema
) {
const { configurationGenerator, cypressInitGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
const e2eName = schema.name ? `${schema.name}-e2e` : undefined;
const { projectName, projectRoot } = await determineProjectNameAndRootOptions(
tree,
{
name: e2eName,
projectType: 'application',
directory: schema.directory,
projectNameAndRootFormat: schema.projectNameAndRootFormat,
callingGenerator: '@nx/storybook:cypress-project',
}
);
const libConfig = readProjectConfiguration(tree, schema.name);
const libRoot = libConfig.root;
const cypressProjectName = `${
schema.directory ? getUnscopedLibName(libRoot) : schema.name
}-e2e`;
const tasks: GeneratorCallback[] = [];
if (!projectAlreadyHasCypress(tree)) {
tasks.push(await _cypressInitGenerator(tree, {}));
tasks.push(await cypressInitGenerator(tree, {}));
}
const installTask = await _cypressProjectGenerator(tree, {
name: cypressProjectName,
project: schema.name,
addProjectConfiguration(tree, projectName, {
root: projectRoot,
projectType: 'application',
sourceRoot: joinPathFragments(projectRoot, 'src'),
targets: {},
implicitDependencies: [projectName],
});
const installTask = await configurationGenerator(tree, {
project: projectName,
js: schema.js,
linter: schema.linter,
directory: schema.directory,
standaloneConfig: schema.standaloneConfig,
directory: projectRoot,
devServerTarget: `${schema.name}:storybook`,
skipFormat: true,
});
tasks.push(installTask);

View File

@ -33,12 +33,6 @@
"enum": ["eslint", "none"],
"default": "eslint"
},
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean",
"default": true,
"x-deprecated": "Nx only supports standaloneConfig"
},
"ciTargetName": {
"type": "string",
"description": "The name of the devServerTarget to use for the Cypress CI configuration. Used to control if using <storybook-project>:static-storybook:ci or <storybook-project>:storybook:ci",
@ -49,6 +43,11 @@
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"projectNameAndRootFormat": {
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
"type": "string",
"enum": ["as-provided", "derived"]
}
},
"required": ["name"]

View File

@ -15,30 +15,39 @@ export async function addE2e(
options: NormalizedSchema
): Promise<GeneratorCallback> {
switch (options.e2eTestRunner) {
case 'cypress':
case 'cypress': {
webStaticServeGenerator(tree, {
buildTarget: `${options.projectName}:build`,
targetName: 'serve-static',
});
const { cypressProjectGenerator } = ensurePackage<
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
return await cypressProjectGenerator(tree, {
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
return await configurationGenerator(tree, {
...options,
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
projectNameAndRootFormat: 'as-provided',
project: options.projectName,
project: options.e2eProjectName,
directory: 'src',
bundler: 'vite',
skipFormat: true,
devServerTarget: `${options.projectName}:serve`,
jsx: true,
});
case 'playwright':
}
case 'playwright': {
const { configurationGenerator } = ensurePackage<
typeof import('@nx/playwright')
>('@nx/playwright', nxVersion);
addProjectConfiguration(tree, options.e2eProjectName, {
projectType: 'application',
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
targets: {},
@ -57,6 +66,7 @@ export async function addE2e(
}`,
webServerAddress: 'http://localhost:4200',
});
}
case 'none':
default:
return () => {};

View File

@ -86,6 +86,7 @@ describe('app', () => {
{
"compilerOptions": {
"allowJs": true,
"module": "commonjs",
"outDir": "../dist/out-tsc",
"sourceMap": false,
"types": [
@ -95,9 +96,12 @@ describe('app', () => {
},
"extends": "../tsconfig.base.json",
"include": [
"src/**/*.ts",
"src/**/*.js",
"**/*.ts",
"**/*.js",
"cypress.config.ts",
"**/*.cy.ts",
"**/*.cy.js",
"**/*.d.ts",
],
}
`);

View File

@ -6,7 +6,6 @@ import {
generateFiles,
GeneratorCallback,
getPackageManagerCommand,
getWorkspaceLayout,
joinPathFragments,
names,
offsetFromRoot,
@ -282,16 +281,22 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
}
if (options.e2eTestRunner === 'cypress') {
const { cypressProjectGenerator } = ensurePackage<
const { configurationGenerator } = ensurePackage<
typeof import('@nx/cypress')
>('@nx/cypress', nxVersion);
const cypressTask = await cypressProjectGenerator(host, {
addProjectConfiguration(host, options.e2eProjectName, {
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
projectType: 'application',
targets: {},
tags: [],
implicitDependencies: [options.projectName],
});
const cypressTask = await configurationGenerator(host, {
...options,
name: options.e2eProjectName,
directory: options.e2eProjectRoot,
// the name and root are already normalized, instruct the generator to use them as is
projectNameAndRootFormat: 'as-provided',
project: options.projectName,
project: options.e2eProjectName,
devServerTarget: `${options.projectName}:serve`,
directory: 'src',
skipFormat: true,
});
tasks.push(cypressTask);
@ -299,7 +304,6 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
const { configurationGenerator: playwrightConfigGenerator } = ensurePackage<
typeof import('@nx/playwright')
>('@nx/playwright', nxVersion);
addProjectConfiguration(host, options.e2eProjectName, {
root: options.e2eProjectRoot,
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),