<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> ## Current Behavior <!-- This is the behavior we have today --> The `@nx/storybook:cypress-project` generator expects there to be a defined target for e2e target. However, since the introduction of inference, this may not be the case. This results in the generator erroring ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> The `@nx/storybook:cypress-project` generator should handle inference and generate an appropriate cypress.config.ts file ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #21770
This commit is contained in:
parent
99543b5a01
commit
61129f49c2
@ -2841,7 +2841,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/storybook/generators/cypress-project": {
|
||||
"description": "Add cypress e2e app to test a UI library that is set up for Storybook.",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"file": "generated/packages/storybook/generators/cypress-project.json",
|
||||
"hidden": false,
|
||||
"name": "cypress-project",
|
||||
|
||||
@ -2811,7 +2811,7 @@
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Add cypress e2e app to test a UI library that is set up for Storybook.",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"file": "generated/packages/storybook/generators/cypress-project.json",
|
||||
"hidden": false,
|
||||
"name": "cypress-project",
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"$id": "cypress-configure",
|
||||
"title": "Cypress Configuration",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"x-deprecated": "Use 'interactionTests' instead when running '@nx/storybook:configuration'. This generator will be removed in v21.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
@ -53,7 +54,8 @@
|
||||
"required": ["name"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Add cypress e2e app to test a UI library that is set up for Storybook.",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"x-deprecated": "Deprecated: Use 'interactionTests' instead when running '@nx/storybook:configuration'. This generator will be removed in v21.",
|
||||
"hidden": false,
|
||||
"implementation": "/packages/storybook/src/generators/cypress-project/cypress-project.ts",
|
||||
"aliases": [],
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
"cypress-project": {
|
||||
"factory": "./src/generators/cypress-project/cypress-project",
|
||||
"schema": "./src/generators/cypress-project/schema.json",
|
||||
"description": "Add cypress e2e app to test a UI library that is set up for Storybook.",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"x-deprecated": "Deprecated: Use 'interactionTests' instead when running '@nx/storybook:configuration'. This generator will be removed in v21.",
|
||||
"hidden": false
|
||||
},
|
||||
"migrate-7": {
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset';
|
||||
import {
|
||||
NxCypressE2EPresetOptions,
|
||||
nxE2EPreset,
|
||||
} from '@nx/cypress/plugins/cypress-preset';
|
||||
|
||||
export function nxE2EStorybookPreset(filePath: string) {
|
||||
export function nxE2EStorybookPreset(
|
||||
filePath: string,
|
||||
options?: NxCypressE2EPresetOptions
|
||||
) {
|
||||
return {
|
||||
...nxE2EPreset(filePath),
|
||||
...nxE2EPreset(filePath, options),
|
||||
baseUrl: 'http://localhost:4400',
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,22 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`@nx/storybook:cypress-project should generate a correct cypress.config.ts file when using inferred plugins 1`] = `
|
||||
"import { defineConfig } from 'cypress';
|
||||
import { nxE2EStorybookPreset } from '@nx/storybook/presets/cypress';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EStorybookPreset(__dirname, {
|
||||
cypressDir: 'src',
|
||||
webServerCommands: {
|
||||
default: 'npx nx run test-ui-lib:storybook',
|
||||
ci: 'npx nx run test-ui-lib:storybook:ci',
|
||||
},
|
||||
ciWebServerCommand: 'npx nx run test-ui-lib:storybook:ci',
|
||||
}),
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`@nx/storybook:cypress-project should generate files 1`] = `
|
||||
"import { defineConfig } from 'cypress';
|
||||
import { nxE2EStorybookPreset } from '@nx/storybook/presets/cypress';
|
||||
|
||||
@ -1,4 +1,16 @@
|
||||
import { readJson, readProjectConfiguration, Tree } from '@nx/devkit';
|
||||
jest.mock('nx/src/project-graph/plugins/loader', () => ({
|
||||
...jest.requireActual('nx/src/project-graph/plugins/loader'),
|
||||
loadNxPlugin: jest.fn().mockImplementation(() => {
|
||||
return [Promise.resolve({}), () => {}];
|
||||
}),
|
||||
}));
|
||||
import {
|
||||
readJson,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
@ -60,4 +72,26 @@ describe('@nx/storybook:cypress-project', () => {
|
||||
tree.exists('apps/one/two/test-ui-lib-e2e/cypress.config.ts')
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should generate a correct cypress.config.ts file when using inferred plugins', async () => {
|
||||
// ARRANGE
|
||||
const nxJson = readNxJson(tree);
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push('@nx/cypress/plugin');
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
// ACT
|
||||
await cypressProjectGenerator(tree, {
|
||||
name: 'test-ui-lib',
|
||||
linter: Linter.EsLint,
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
expect(tree.exists('apps/test-ui-lib-e2e/cypress.config.ts')).toBeTruthy();
|
||||
const cypressConfig = tree.read(
|
||||
'apps/test-ui-lib-e2e/cypress.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(cypressConfig).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@ -5,7 +5,9 @@ import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
readJson,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
@ -43,6 +45,9 @@ export async function cypressProjectGeneratorInternal(
|
||||
tree: Tree,
|
||||
schema: CypressConfigureSchema
|
||||
) {
|
||||
logger.warn(
|
||||
`Use 'interactionTests' instead when running '@nx/storybook:configuration'. This generator will be removed in v21.`
|
||||
);
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/cypress')
|
||||
>('@nx/cypress', nxVersion);
|
||||
@ -73,7 +78,6 @@ export async function cypressProjectGeneratorInternal(
|
||||
project: projectName,
|
||||
js: schema.js,
|
||||
linter: schema.linter,
|
||||
directory: projectRoot,
|
||||
devServerTarget: `${schema.name}:storybook`,
|
||||
skipFormat: true,
|
||||
});
|
||||
@ -84,12 +88,28 @@ export async function cypressProjectGeneratorInternal(
|
||||
schema.directory
|
||||
);
|
||||
removeUnneededFiles(tree, generatedCypressProjectName, schema.js);
|
||||
addBaseUrlToCypressConfig(tree, generatedCypressProjectName);
|
||||
updateAngularJsonBuilder(tree, {
|
||||
e2eProjectName: generatedCypressProjectName,
|
||||
targetProjectName: schema.name,
|
||||
ciTargetName: schema.ciTargetName,
|
||||
});
|
||||
|
||||
const project = readProjectConfiguration(tree, generatedCypressProjectName);
|
||||
if (project.targets.e2e && project.targets.e2e.options) {
|
||||
addBaseUrlToCypressConfig(tree, generatedCypressProjectName, project.root);
|
||||
updateAngularJsonBuilder(tree, {
|
||||
e2eProjectName: generatedCypressProjectName,
|
||||
targetProjectName: schema.name,
|
||||
ciTargetName: schema.ciTargetName,
|
||||
});
|
||||
} else if (hasCypressPlugin(tree)) {
|
||||
generateCypressConfigForInferredPlugin(
|
||||
tree,
|
||||
generatedCypressProjectName,
|
||||
project.root,
|
||||
schema.name,
|
||||
schema.ciTargetName
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unable to generate Cypress Project for Storybook project. Please report this issue at https://github.com/nrwl/nx/issues/new/choose`
|
||||
);
|
||||
}
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
@ -98,10 +118,19 @@ export async function cypressProjectGeneratorInternal(
|
||||
return cypressTask;
|
||||
}
|
||||
|
||||
function hasCypressPlugin(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
return nxJson.plugins?.some((p) =>
|
||||
typeof p === 'string'
|
||||
? p === '@nx/cypress/plugin'
|
||||
: p.plugin === '@nx/cypress/plugin'
|
||||
);
|
||||
}
|
||||
|
||||
function removeUnneededFiles(tree: Tree, projectName: string, js: boolean) {
|
||||
const { sourceRoot, root } = readProjectConfiguration(tree, projectName);
|
||||
const fileType = js ? 'js' : 'ts';
|
||||
if (tree.exists(join(root, 'cypress.config.ts'))) {
|
||||
if (tree.exists(joinPathFragments(root, 'cypress.config.ts'))) {
|
||||
safeFileDelete(tree, `${sourceRoot}/e2e/app.cy.${fileType}`);
|
||||
safeFileDelete(tree, `${sourceRoot}/support/app.po.${fileType}`);
|
||||
} else {
|
||||
@ -110,10 +139,44 @@ function removeUnneededFiles(tree: Tree, projectName: string, js: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
function addBaseUrlToCypressConfig(tree: Tree, projectName: string) {
|
||||
const projectRoot = readProjectConfiguration(tree, projectName).root;
|
||||
const cypressJson = join(projectRoot, 'cypress.json');
|
||||
const cypressTs = join(projectRoot, 'cypress.config.ts');
|
||||
function generateCypressConfigForInferredPlugin(
|
||||
tree: Tree,
|
||||
projectName: string,
|
||||
projectRoot: string,
|
||||
targetProjectName: string,
|
||||
ciTargetName?: string
|
||||
) {
|
||||
const cypressJson = joinPathFragments(projectRoot, 'cypress.json');
|
||||
const cypressTs = joinPathFragments(projectRoot, 'cypress.config.ts');
|
||||
|
||||
if (tree.exists(cypressJson)) {
|
||||
tree.delete(cypressJson);
|
||||
}
|
||||
if (tree.exists(cypressTs)) {
|
||||
// cypress >= v10
|
||||
tree.delete(cypressTs);
|
||||
generateFiles(
|
||||
tree,
|
||||
join(__dirname, 'files', 'inferred-target'),
|
||||
projectRoot,
|
||||
{
|
||||
defaultWebServerCommand: `npx nx run ${targetProjectName}:storybook`,
|
||||
ciWebServerCommand: ciTargetName
|
||||
? `npx nx run ${targetProjectName}:${ciTargetName}:ci`
|
||||
: `npx nx run ${targetProjectName}:storybook:ci`,
|
||||
tpl: '',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function addBaseUrlToCypressConfig(
|
||||
tree: Tree,
|
||||
projectName: string,
|
||||
projectRoot: string
|
||||
) {
|
||||
const cypressJson = joinPathFragments(projectRoot, 'cypress.json');
|
||||
const cypressTs = joinPathFragments(projectRoot, 'cypress.config.ts');
|
||||
// TODO(caleb): remove this when cypress < v10 is deprecated
|
||||
|
||||
if (tree.exists(cypressJson)) {
|
||||
@ -125,9 +188,14 @@ function addBaseUrlToCypressConfig(tree: Tree, projectName: string) {
|
||||
} else if (tree.exists(cypressTs)) {
|
||||
// cypress >= v10
|
||||
tree.delete(cypressTs);
|
||||
generateFiles(tree, join(__dirname, 'files'), projectRoot, {
|
||||
tpl: '',
|
||||
});
|
||||
generateFiles(
|
||||
tree,
|
||||
join(__dirname, 'files', 'explicit-target'),
|
||||
projectRoot,
|
||||
{
|
||||
tpl: '',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +208,7 @@ function updateAngularJsonBuilder(
|
||||
}
|
||||
) {
|
||||
const project = readProjectConfiguration(tree, opts.e2eProjectName);
|
||||
const e2eTarget = project.targets.e2e;
|
||||
const e2eTarget = project.targets.e2e ?? {};
|
||||
project.targets.e2e = {
|
||||
...e2eTarget,
|
||||
options: <any>{
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EStorybookPreset } from '@nx/storybook/presets/cypress';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EStorybookPreset(__dirname, {
|
||||
cypressDir: 'src',
|
||||
webServerCommands: {
|
||||
default: "<%= defaultWebServerCommand %>",
|
||||
ci: "<%= ciWebServerCommand %>"
|
||||
},
|
||||
ciWebServerCommand: "<%= ciWebServerCommand %>"
|
||||
}),
|
||||
});
|
||||
@ -4,6 +4,7 @@
|
||||
"$id": "cypress-configure",
|
||||
"title": "Cypress Configuration",
|
||||
"description": "Add cypress E2E app to test a ui library that is set up for Storybook.",
|
||||
"x-deprecated": "Use 'interactionTests' instead when running '@nx/storybook:configuration'. This generator will be removed in v21.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user