feat(storybook): remove cypress options for e2e testing (#27850)
- feat(storybook): remove cypress options from configuration generator - feat(react): remove cypress options from storybook-configuration - feat(react): remove cypress options from stories generator - feat(react): remove component-cypress-spec generator - chore(storybook): restore @nx/cypress dep - feat(remix): remove cypress options from storybook - feat(angular): remove cypress options from storybook-configuration - feat(angular): remove cypress options from stories generator - feat(angular): remove component-cypress-spec generator - feat(vue): remove cypress options from stories generator <!-- 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` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> With Storybook Interaction Testing, there's no longer a need to setup Cypress to specifically test storybook instances ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Remove cypress options for creating an e2e project specifically for testing storybook instances. Use Storybook Interaction Testing instead ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
72cd1c15e6
commit
8290969cb7
@ -7028,14 +7028,6 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "component-cypress-spec",
|
||||
"path": "/nx-api/angular/generators/component-cypress-spec",
|
||||
"name": "component-cypress-spec",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "component-story",
|
||||
"path": "/nx-api/angular/generators/component-story",
|
||||
@ -9222,14 +9214,6 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "component-cypress-spec",
|
||||
"path": "/nx-api/react/generators/component-cypress-spec",
|
||||
"name": "component-cypress-spec",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "hook",
|
||||
"path": "/nx-api/react/generators/hook",
|
||||
|
||||
@ -181,15 +181,6 @@
|
||||
"path": "/nx-api/angular/generators/component",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/angular/generators/component-cypress-spec": {
|
||||
"description": "Creates a Cypress spec for a UI component that has a story.",
|
||||
"file": "generated/packages/angular/generators/component-cypress-spec.json",
|
||||
"hidden": true,
|
||||
"name": "component-cypress-spec",
|
||||
"originalFilePath": "/packages/angular/src/generators/component-cypress-spec/schema.json",
|
||||
"path": "/nx-api/angular/generators/component-cypress-spec",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/angular/generators/component-story": {
|
||||
"description": "Creates a stories.ts file for a component.",
|
||||
"file": "generated/packages/angular/generators/component-story.json",
|
||||
@ -2358,15 +2349,6 @@
|
||||
"path": "/nx-api/react/generators/stories",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react/generators/component-cypress-spec": {
|
||||
"description": "Create a Cypress spec for a UI component that has a story.",
|
||||
"file": "generated/packages/react/generators/component-cypress-spec.json",
|
||||
"hidden": false,
|
||||
"name": "component-cypress-spec",
|
||||
"originalFilePath": "/packages/react/src/generators/component-cypress-spec/schema.json",
|
||||
"path": "/nx-api/react/generators/component-cypress-spec",
|
||||
"type": "generator"
|
||||
},
|
||||
"/nx-api/react/generators/hook": {
|
||||
"description": "Create a hook.",
|
||||
"file": "generated/packages/react/generators/hook.json",
|
||||
|
||||
@ -176,15 +176,6 @@
|
||||
"path": "angular/generators/component",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Creates a Cypress spec for a UI component that has a story.",
|
||||
"file": "generated/packages/angular/generators/component-cypress-spec.json",
|
||||
"hidden": true,
|
||||
"name": "component-cypress-spec",
|
||||
"originalFilePath": "/packages/angular/src/generators/component-cypress-spec/schema.json",
|
||||
"path": "angular/generators/component-cypress-spec",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Creates a stories.ts file for a component.",
|
||||
"file": "generated/packages/angular/generators/component-story.json",
|
||||
@ -2332,15 +2323,6 @@
|
||||
"path": "react/generators/stories",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Create a Cypress spec for a UI component that has a story.",
|
||||
"file": "generated/packages/react/generators/component-cypress-spec.json",
|
||||
"hidden": false,
|
||||
"name": "component-cypress-spec",
|
||||
"originalFilePath": "/packages/react/src/generators/component-cypress-spec/schema.json",
|
||||
"path": "react/generators/component-cypress-spec",
|
||||
"type": "generator"
|
||||
},
|
||||
{
|
||||
"description": "Create a hook.",
|
||||
"file": "generated/packages/react/generators/hook.json",
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
{
|
||||
"name": "component-cypress-spec",
|
||||
"factory": "./src/generators/component-cypress-spec/component-cypress-spec",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxAngularComponentCypressSpecGenerator",
|
||||
"type": "object",
|
||||
"cli": "nx",
|
||||
"description": "Creates a Storybook Cypress spec for a UI component that has a story.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
|
||||
"properties": {
|
||||
"projectName": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": { "$source": "projectName" },
|
||||
"examples": ["ui-samples"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"projectPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the project.",
|
||||
"examples": ["libs/ui-samples"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentName": {
|
||||
"type": "string",
|
||||
"description": "Class name of the component.",
|
||||
"examples": ["AwesomeComponent"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentPath": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the component file from the project root.",
|
||||
"examples": ["awesome"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentFileName": {
|
||||
"type": "string",
|
||||
"description": "Component file name without the `.ts` extension.",
|
||||
"examples": ["awesome.component"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. By default, inferred from `projectName`."
|
||||
},
|
||||
"specDirectory": {
|
||||
"type": "string",
|
||||
"description": "Directory where to place the generated spec file. By default matches the value of the `componentPath` option."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"projectName",
|
||||
"projectPath",
|
||||
"componentName",
|
||||
"componentPath",
|
||||
"componentFileName"
|
||||
],
|
||||
"examplesFile": "## Examples\n\n{% tabs %}\n\n{% tab label=\"Basic Usage\" %}\n\nCreate a cypress spec for a component that is set up with Storybook.\n\n```bash\nnx g @nx/angular:component-cypress-spec --componentName=MyButtonComponent --componentPath=libs/ui/src/lib/button/button.component.ts --componentFileName=button.component --projectName=ui --projectPath=libs/ui\n```\n\n{% /tab %}\n\n{% /tabs %}\n",
|
||||
"presets": []
|
||||
},
|
||||
"description": "Creates a Cypress spec for a UI component that has a story.",
|
||||
"hidden": true,
|
||||
"implementation": "/packages/angular/src/generators/component-cypress-spec/component-cypress-spec.ts",
|
||||
"aliases": [],
|
||||
"path": "/packages/angular/src/generators/component-cypress-spec/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
@ -25,16 +25,6 @@
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure Cypress or not.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.stories.ts` files for components declared in this project or not.",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
{
|
||||
"name": "component-cypress-spec",
|
||||
"factory": "./src/generators/component-cypress-spec/component-cypress-spec#componentCypressGenerator",
|
||||
"schema": {
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "NxReactComponentCypressSpec",
|
||||
"title": "Create component Cypress spec",
|
||||
"description": "Create a Storybook Cypress spec for a UI component that has a story.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The project name for which to generate tests.",
|
||||
"examples": ["shared-ui-component"],
|
||||
"$default": { "$source": "projectName", "index": 0 },
|
||||
"x-prompt": "What's name of the project for which to generate tests?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentPath": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the component file from the library root?",
|
||||
"examples": ["lib/components"],
|
||||
"x-prompt": "What's path of the component relative to the project's lib root for which to generate a test?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript files rather than TypeScript files.",
|
||||
"default": false
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. By default, inferred from `project`."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"required": ["project", "componentPath"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Create a Cypress spec for a UI component that has a story.",
|
||||
"hidden": false,
|
||||
"implementation": "/packages/react/src/generators/component-cypress-spec/component-cypress-spec#componentCypressGenerator.ts",
|
||||
"aliases": [],
|
||||
"path": "/packages/react/src/generators/component-cypress-spec/schema.json",
|
||||
"type": "generator"
|
||||
}
|
||||
@ -17,16 +17,6 @@
|
||||
"x-prompt": "For which project do you want to generate stories?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.stories.ts` files for components declared in this project?",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript story files rather than TypeScript story files.",
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.stories.ts` files for components declared in this project?",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript story files rather than TypeScript story files.",
|
||||
|
||||
@ -25,16 +25,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -17,16 +17,6 @@
|
||||
"x-prompt": "For which project do you want to generate stories?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
|
||||
@ -347,7 +347,6 @@
|
||||
- [add-linting](/nx-api/angular/generators/add-linting)
|
||||
- [application](/nx-api/angular/generators/application)
|
||||
- [component](/nx-api/angular/generators/component)
|
||||
- [component-cypress-spec](/nx-api/angular/generators/component-cypress-spec)
|
||||
- [component-story](/nx-api/angular/generators/component-story)
|
||||
- [component-test](/nx-api/angular/generators/component-test)
|
||||
- [convert-to-application-executor](/nx-api/angular/generators/convert-to-application-executor)
|
||||
@ -613,7 +612,6 @@
|
||||
- [storybook-configuration](/nx-api/react/generators/storybook-configuration)
|
||||
- [component-story](/nx-api/react/generators/component-story)
|
||||
- [stories](/nx-api/react/generators/stories)
|
||||
- [component-cypress-spec](/nx-api/react/generators/component-cypress-spec)
|
||||
- [hook](/nx-api/react/generators/hook)
|
||||
- [host](/nx-api/react/generators/host)
|
||||
- [remote](/nx-api/react/generators/remote)
|
||||
|
||||
@ -15,10 +15,6 @@ const pages: Array<{ title: string; path: string }> = [
|
||||
title: '@nx/angular:component',
|
||||
path: '/packages/angular/generators/component',
|
||||
},
|
||||
{
|
||||
title: '@nx/angular:component-cypress-spec',
|
||||
path: '/packages/angular/generators/component-cypress-spec',
|
||||
},
|
||||
{
|
||||
title: '@nx/angular:component-story',
|
||||
path: '/packages/angular/generators/component-story',
|
||||
@ -298,10 +294,6 @@ const pages: Array<{ title: string; path: string }> = [
|
||||
title: '@nx/react:stories',
|
||||
path: '/packages/react/generators/stories',
|
||||
},
|
||||
{
|
||||
title: '@nx/react:component-cypress-spec',
|
||||
path: '/packages/react/generators/component-cypress-spec',
|
||||
},
|
||||
{ title: '@nx/react:hook', path: '/packages/react/generators/hook' },
|
||||
{ title: '@nx/react:host', path: '/packages/react/generators/host' },
|
||||
{ title: '@nx/react:remote', path: '/packages/react/generators/remote' },
|
||||
|
||||
@ -22,12 +22,6 @@
|
||||
"aliases": ["c"],
|
||||
"description": "Generate an Angular Component."
|
||||
},
|
||||
"component-cypress-spec": {
|
||||
"factory": "./src/generators/component-cypress-spec/component-cypress-spec",
|
||||
"schema": "./src/generators/component-cypress-spec/schema.json",
|
||||
"description": "Creates a Cypress spec for a UI component that has a story.",
|
||||
"hidden": true
|
||||
},
|
||||
"component-story": {
|
||||
"factory": "./src/generators/component-story/component-story",
|
||||
"schema": "./src/generators/component-story/schema.json",
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
export * from './src/generators/add-linting/add-linting';
|
||||
export * from './src/generators/application/application';
|
||||
export * from './src/generators/component-cypress-spec/component-cypress-spec';
|
||||
export * from './src/generators/component-story/component-story';
|
||||
export * from './src/generators/component/component';
|
||||
export * from './src/generators/directive/directive';
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`componentCypressSpec generator should generate .spec.ts when using cypress.json 1`] = `
|
||||
"describe('ng-app1', () => {
|
||||
beforeEach(() =>
|
||||
cy.visit(
|
||||
'/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;'
|
||||
)
|
||||
);
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('proj-test-button').should('exist');
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`componentCypressSpec generator should generate the component spec file 1`] = `
|
||||
"describe('ng-app1', () => {
|
||||
beforeEach(() =>
|
||||
cy.visit(
|
||||
'/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;'
|
||||
)
|
||||
);
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('proj-test-button').should('exist');
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
@ -1,112 +0,0 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import * as devkit from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { componentGenerator } from '../component/component';
|
||||
import * as storybookUtils from '../utils/storybook-ast/storybook-inputs';
|
||||
import { generateTestApplication } from '../utils/testing';
|
||||
import { componentCypressSpecGenerator } from './component-cypress-spec';
|
||||
import { E2eTestRunner } from '../../utils/test-runners';
|
||||
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
|
||||
describe('componentCypressSpec generator', () => {
|
||||
let tree: Tree;
|
||||
const appName = 'ng-app1';
|
||||
const specFile = `${appName}-e2e/src/e2e/test-button/test-button.component.cy.ts`;
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await generateTestApplication(tree, {
|
||||
name: appName,
|
||||
skipFormat: true,
|
||||
e2eTestRunner: E2eTestRunner.Cypress,
|
||||
});
|
||||
await componentGenerator(tree, {
|
||||
name: 'test-button',
|
||||
directory: `${appName}/src/app/test-button`,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
tree.write(
|
||||
`${appName}/src/app/test-button/test-button.component.ts`,
|
||||
`import { Component, Input } from '@angular/core';
|
||||
|
||||
export type ButtonStyle = 'default' | 'primary' | 'accent';
|
||||
|
||||
@Component({
|
||||
selector: 'proj-test-button',
|
||||
templateUrl: './test-button.component.html',
|
||||
styleUrls: ['./test-button.component.css']
|
||||
})
|
||||
export class TestButtonComponent {
|
||||
@Input('buttonType') type = 'button';
|
||||
@Input() style: ButtonStyle = 'default';
|
||||
@Input() age?: number;
|
||||
@Input() isOn = false;
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should not generate the component spec file when it already exists', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
jest.spyOn(storybookUtils, 'getComponentProps');
|
||||
jest.spyOn(devkit, 'generateFiles');
|
||||
tree.write(specFile, '');
|
||||
|
||||
await componentCypressSpecGenerator(tree, {
|
||||
componentFileName: 'test-button.component',
|
||||
componentName: 'TestButtonComponent',
|
||||
componentPath: `test-button`,
|
||||
projectPath: `${appName}/src/app`,
|
||||
projectName: appName,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
expect(storybookUtils.getComponentProps).not.toHaveBeenCalled();
|
||||
expect(devkit.generateFiles).not.toHaveBeenCalled();
|
||||
expect(tree.read(specFile).toString()).toBe('');
|
||||
});
|
||||
|
||||
it('should generate the component spec file', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
await componentCypressSpecGenerator(tree, {
|
||||
componentFileName: 'test-button.component',
|
||||
componentName: 'TestButtonComponent',
|
||||
componentPath: `test-button`,
|
||||
projectPath: `${appName}/src/app`,
|
||||
projectName: appName,
|
||||
});
|
||||
|
||||
expect(tree.exists(specFile)).toBe(true);
|
||||
const specFileContent = tree.read(specFile).toString();
|
||||
expect(specFileContent).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should generate .spec.ts when using cypress.json', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(9);
|
||||
const v9SpecFile = `${appName}-e2e/src/integration/test-button/test-button.component.spec.ts`;
|
||||
tree.delete(`${appName}-e2e/cypress.config.ts`);
|
||||
tree.write(`${appName}-e2e/cypress.json`, `{}`);
|
||||
|
||||
await componentCypressSpecGenerator(tree, {
|
||||
componentFileName: 'test-button.component',
|
||||
componentName: 'TestButtonComponent',
|
||||
componentPath: `test-button`,
|
||||
projectPath: `${appName}/src/app`,
|
||||
projectName: appName,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
expect(tree.exists(v9SpecFile)).toBe(true);
|
||||
const specFileContent = tree.read(v9SpecFile).toString();
|
||||
expect(specFileContent).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@ -1,72 +0,0 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { getComponentProps } from '../utils/storybook-ast/storybook-inputs';
|
||||
import { getArgsDefaultValue } from './lib/get-args-default-value';
|
||||
import { getComponentSelector } from './lib/get-component-selector';
|
||||
import type { ComponentCypressSpecGeneratorOptions } from './schema';
|
||||
|
||||
export async function componentCypressSpecGenerator(
|
||||
tree: Tree,
|
||||
options: ComponentCypressSpecGeneratorOptions
|
||||
): Promise<void> {
|
||||
const {
|
||||
cypressProject,
|
||||
projectName,
|
||||
projectPath,
|
||||
componentPath,
|
||||
componentFileName,
|
||||
componentName,
|
||||
specDirectory,
|
||||
} = options;
|
||||
const e2eProjectName = cypressProject || `${projectName}-e2e`;
|
||||
const { sourceRoot, root } = readProjectConfiguration(tree, e2eProjectName);
|
||||
const isCypressV10 = tree.exists(
|
||||
joinPathFragments(root, 'cypress.config.ts')
|
||||
);
|
||||
const e2eLibIntegrationFolderPath = joinPathFragments(
|
||||
sourceRoot,
|
||||
isCypressV10 ? 'e2e' : 'integration'
|
||||
);
|
||||
|
||||
const templatesDir = joinPathFragments(__dirname, 'files');
|
||||
const destinationDir = joinPathFragments(
|
||||
e2eLibIntegrationFolderPath,
|
||||
specDirectory ?? componentPath
|
||||
);
|
||||
const storyFile = joinPathFragments(
|
||||
destinationDir,
|
||||
`${componentFileName}.${isCypressV10 ? 'cy' : 'spec'}.ts`
|
||||
);
|
||||
|
||||
if (tree.exists(storyFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fullComponentPath = joinPathFragments(
|
||||
projectPath,
|
||||
componentPath,
|
||||
`${componentFileName}.ts`
|
||||
);
|
||||
const props = getComponentProps(tree, fullComponentPath, getArgsDefaultValue);
|
||||
const componentSelector = getComponentSelector(tree, fullComponentPath);
|
||||
|
||||
generateFiles(tree, templatesDir, destinationDir, {
|
||||
projectName,
|
||||
componentFileName,
|
||||
componentName,
|
||||
componentSelector,
|
||||
props,
|
||||
fileExt: isCypressV10 ? 'cy.ts' : 'spec.ts',
|
||||
});
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
|
||||
export default componentCypressSpecGenerator;
|
||||
@ -1,17 +0,0 @@
|
||||
describe('<%=projectName%>', () => {
|
||||
beforeEach(() =>
|
||||
cy.visit(
|
||||
'/iframe.html?id=<%= componentName.toLowerCase() %>--primary<% if ( props && props.length > 0 ) { %>&args=<% } %><%
|
||||
for(let prop of props) {
|
||||
%><%=prop.name%><%
|
||||
if(prop.defaultValue !== undefined && (prop.defaultValue || prop.defaultValue === false)) {
|
||||
%>:<%=prop.defaultValue%><%
|
||||
} %>;<%
|
||||
}%>'
|
||||
)
|
||||
);
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('<%=componentSelector%>').should('exist');
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
import type { PropertyDeclaration } from 'typescript';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
export function getArgsDefaultValue(
|
||||
property: PropertyDeclaration
|
||||
): string | undefined {
|
||||
if (!property.initializer) {
|
||||
return undefined;
|
||||
}
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
switch (property.initializer.kind) {
|
||||
case tsModule.SyntaxKind.StringLiteral:
|
||||
const returnString = property.initializer.getText().slice(1, -1);
|
||||
return returnString.replace(/\s/g, '+');
|
||||
case tsModule.SyntaxKind.NumericLiteral:
|
||||
case tsModule.SyntaxKind.TrueKeyword:
|
||||
case tsModule.SyntaxKind.FalseKeyword:
|
||||
return property.initializer.getText();
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
import { findNodes } from '@nx/js';
|
||||
import type { PropertyAssignment } from 'typescript';
|
||||
|
||||
import {
|
||||
getDecoratorMetadata,
|
||||
getTsSourceFile,
|
||||
} from '../../../utils/nx-devkit/ast-utils';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
export function getComponentSelector(tree: Tree, path: string): string {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
const file = getTsSourceFile(tree, path);
|
||||
|
||||
const componentDecorators = getDecoratorMetadata(
|
||||
file,
|
||||
'Component',
|
||||
'@angular/core'
|
||||
);
|
||||
if (componentDecorators.length === 0) {
|
||||
throw new Error(`No @Component decorator in ${path}.`);
|
||||
}
|
||||
const componentDecorator = componentDecorators[0];
|
||||
const selectorNode = <PropertyAssignment>(
|
||||
findNodes(componentDecorator, tsModule.SyntaxKind.PropertyAssignment).find(
|
||||
(node: PropertyAssignment) => node.name.getText() === 'selector'
|
||||
)
|
||||
);
|
||||
if (!selectorNode) {
|
||||
throw new Error(`No selector defined for the component in ${path}.`);
|
||||
}
|
||||
|
||||
return selectorNode.initializer.getText().slice(1, -1);
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
export interface ComponentCypressSpecGeneratorOptions {
|
||||
projectName: string;
|
||||
projectPath: string;
|
||||
componentName: string;
|
||||
componentPath: string;
|
||||
componentFileName: string;
|
||||
cypressProject?: string;
|
||||
specDirectory?: string;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxAngularComponentCypressSpecGenerator",
|
||||
"type": "object",
|
||||
"cli": "nx",
|
||||
"description": "Creates a Storybook Cypress spec for a UI component that has a story.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
|
||||
"properties": {
|
||||
"projectName": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": {
|
||||
"$source": "projectName"
|
||||
},
|
||||
"examples": ["ui-samples"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"projectPath": {
|
||||
"type": "string",
|
||||
"description": "Path to the project.",
|
||||
"examples": ["libs/ui-samples"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentName": {
|
||||
"type": "string",
|
||||
"description": "Class name of the component.",
|
||||
"examples": ["AwesomeComponent"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentPath": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the component file from the project root.",
|
||||
"examples": ["awesome"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentFileName": {
|
||||
"type": "string",
|
||||
"description": "Component file name without the `.ts` extension.",
|
||||
"examples": ["awesome.component"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. By default, inferred from `projectName`."
|
||||
},
|
||||
"specDirectory": {
|
||||
"type": "string",
|
||||
"description": "Directory where to place the generated spec file. By default matches the value of the `componentPath` option."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"projectName",
|
||||
"projectPath",
|
||||
"componentName",
|
||||
"componentPath",
|
||||
"componentFileName"
|
||||
],
|
||||
"examplesFile": "../../../docs/component-cypress-spec-examples.md"
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
import type { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import { readProjectConfiguration } from '@nx/devkit';
|
||||
|
||||
export function getE2EProject(
|
||||
tree: Tree,
|
||||
e2eProjectName: string
|
||||
): ProjectConfiguration {
|
||||
let e2eProject: ProjectConfiguration;
|
||||
try {
|
||||
e2eProject = readProjectConfiguration(tree, e2eProjectName);
|
||||
} catch {
|
||||
e2eProject = undefined;
|
||||
}
|
||||
|
||||
return e2eProject;
|
||||
}
|
||||
@ -3,6 +3,4 @@ export interface StoriesGeneratorOptions {
|
||||
interactionTests?: boolean;
|
||||
skipFormat?: boolean;
|
||||
ignorePaths?: string[];
|
||||
cypressProject?: string;
|
||||
generateCypressSpecs?: boolean;
|
||||
}
|
||||
|
||||
@ -25,16 +25,6 @@
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { componentGenerator } from '../component/component';
|
||||
@ -9,21 +8,11 @@ import { generateTestApplication } from '../utils/testing';
|
||||
import { angularStoriesGenerator } from './stories';
|
||||
import { stripIndents } from '@nx/devkit';
|
||||
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
|
||||
describe('angularStories generator: applications', () => {
|
||||
let tree: Tree;
|
||||
const appName = 'test-app';
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await generateTestApplication(tree, {
|
||||
name: appName,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { writeJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
@ -13,20 +12,8 @@ import {
|
||||
} from '../utils/testing';
|
||||
import { angularStoriesGenerator } from './stories';
|
||||
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
|
||||
describe('angularStories generator: libraries', () => {
|
||||
const libName = 'test-ui-lib';
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(() => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
});
|
||||
|
||||
describe('Stories for empty Angular library', () => {
|
||||
let tree: Tree;
|
||||
|
||||
@ -4,12 +4,10 @@ import {
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import componentCypressSpecGenerator from '../component-cypress-spec/component-cypress-spec';
|
||||
import componentStoryGenerator from '../component-story/component-story';
|
||||
import type { ComponentInfo } from '../utils/storybook-ast/component-info';
|
||||
import {
|
||||
@ -17,7 +15,6 @@ import {
|
||||
getStandaloneComponentsInfo,
|
||||
} from '../utils/storybook-ast/component-info';
|
||||
import { getProjectEntryPoints } from '../utils/storybook-ast/entry-point';
|
||||
import { getE2EProject } from './lib/get-e2e-project';
|
||||
import { getModuleFilePaths } from '../utils/storybook-ast/module-info';
|
||||
import type { StoriesGeneratorOptions } from './schema';
|
||||
import { minimatch } from 'minimatch';
|
||||
@ -27,8 +24,6 @@ export async function angularStoriesGenerator(
|
||||
tree: Tree,
|
||||
options: StoriesGeneratorOptions
|
||||
): Promise<GeneratorCallback> {
|
||||
const e2eProjectName = options.cypressProject ?? `${options.name}-e2e`;
|
||||
const e2eProject = getE2EProject(tree, e2eProjectName);
|
||||
const entryPoints = getProjectEntryPoints(tree, options.name);
|
||||
const componentsInfo: ComponentInfo[] = [];
|
||||
for (const entryPoint of entryPoints) {
|
||||
@ -39,12 +34,6 @@ export async function angularStoriesGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
if (options.generateCypressSpecs && !e2eProject) {
|
||||
logger.info(
|
||||
`There was no e2e project "${e2eProjectName}" found, so cypress specs will not be generated. Pass "--cypressProject" to specify a different e2e project name.`
|
||||
);
|
||||
}
|
||||
|
||||
const componentInfos = componentsInfo.filter(
|
||||
(f) =>
|
||||
!options.ignorePaths?.some((pattern) => {
|
||||
@ -73,19 +62,6 @@ export async function angularStoriesGenerator(
|
||||
interactionTests: options.interactionTests ?? true,
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
if (options.generateCypressSpecs && e2eProject) {
|
||||
await componentCypressSpecGenerator(tree, {
|
||||
projectName: options.name,
|
||||
projectPath: info.moduleFolderPath,
|
||||
cypressProject: options.cypressProject,
|
||||
componentName: info.name,
|
||||
componentPath: info.path,
|
||||
componentFileName: info.componentFileName,
|
||||
specDirectory: joinPathFragments(info.entryPointName, info.path),
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { ensurePackage, readProjectConfiguration } from '@nx/devkit';
|
||||
import { nxVersion } from '../../../utils/versions';
|
||||
import { readProjectConfiguration } from '@nx/devkit';
|
||||
import { angularStoriesGenerator } from '../../stories/stories';
|
||||
import type { StorybookConfigurationOptions } from '../schema';
|
||||
|
||||
@ -9,21 +8,9 @@ export async function generateStories(
|
||||
options: StorybookConfigurationOptions
|
||||
) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
ensurePackage('@nx/cypress', nxVersion);
|
||||
const { getE2eProjectName } = <
|
||||
typeof import('@nx/cypress/src/utils/project-name')
|
||||
>require('@nx/cypress/src/utils/project-name');
|
||||
const e2eProjectName = getE2eProjectName(
|
||||
options.project,
|
||||
project.root,
|
||||
options.cypressDirectory
|
||||
);
|
||||
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: options.project,
|
||||
generateCypressSpecs:
|
||||
options.configureCypress && options.generateCypressSpecs,
|
||||
cypressProject: e2eProjectName,
|
||||
ignorePaths: options.ignorePaths,
|
||||
interactionTests: options.interactionTests,
|
||||
skipFormat: true,
|
||||
|
||||
@ -12,9 +12,7 @@ export async function generateStorybookConfiguration(
|
||||
return await configurationGenerator(tree, {
|
||||
project: options.project,
|
||||
uiFramework: '@storybook/angular',
|
||||
configureCypress: options.configureCypress,
|
||||
linter: options.linter,
|
||||
cypressDirectory: options.cypressDirectory,
|
||||
tsConfiguration: options.tsConfiguration,
|
||||
interactionTests: options.interactionTests,
|
||||
configureStaticServe: options.configureStaticServe,
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import type { StorybookConfigurationOptions } from '../schema';
|
||||
|
||||
export function validateOptions(options: StorybookConfigurationOptions): void {
|
||||
if (options.generateCypressSpecs && !options.generateStories) {
|
||||
throw new Error(
|
||||
'Cannot set generateCypressSpecs to true when generateStories is set to false.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,4 @@ export interface StorybookConfigurationOptions {
|
||||
skipFormat?: boolean;
|
||||
ignorePaths?: string[];
|
||||
interactionTests?: boolean;
|
||||
configureCypress?: boolean;
|
||||
generateCypressSpecs?: boolean;
|
||||
cypressDirectory?: string;
|
||||
}
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure Cypress or not.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.stories.ts` files for components declared in this project or not.",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { readJson, writeJson } from '@nx/devkit';
|
||||
import { Linter } from '@nx/eslint/src/generators/utils/linter';
|
||||
@ -11,10 +10,7 @@ import {
|
||||
import type { StorybookConfigurationOptions } from './schema';
|
||||
import { storybookConfigurationGenerator } from './storybook-configuration';
|
||||
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
// nested code imports graph from the repo, which might have innacurate graph version
|
||||
// nested code imports graph from the repo, which might have inaccurate graph version
|
||||
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||
createProjectGraphAsync: jest
|
||||
@ -36,12 +32,8 @@ function listFiles(tree: Tree): string[] {
|
||||
describe('StorybookConfiguration generator', () => {
|
||||
let tree: Tree;
|
||||
const libName = 'test-ui-lib';
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
tree = await createStorybookTestWorkspaceForLib(libName);
|
||||
|
||||
jest.resetModules();
|
||||
|
||||
@ -8,16 +8,13 @@ import { updateAppEditorTsConfigExcludedFiles } from '../utils/update-app-editor
|
||||
import { assertCompatibleStorybookVersion } from './lib/assert-compatible-storybook-version';
|
||||
import { generateStories } from './lib/generate-stories';
|
||||
import { generateStorybookConfiguration } from './lib/generate-storybook-configuration';
|
||||
import { validateOptions } from './lib/validate-options';
|
||||
import type { StorybookConfigurationOptions } from './schema';
|
||||
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
export async function storybookConfigurationGenerator(
|
||||
tree: Tree,
|
||||
options: StorybookConfigurationOptions
|
||||
): Promise<GeneratorCallback> {
|
||||
assertCompatibleStorybookVersion();
|
||||
validateOptions(options);
|
||||
|
||||
const storybookGeneratorInstallTask = await generateStorybookConfiguration(
|
||||
tree,
|
||||
|
||||
@ -54,12 +54,6 @@
|
||||
"description": "Create stories/specs for all components declared in an app or library.",
|
||||
"hidden": false
|
||||
},
|
||||
"component-cypress-spec": {
|
||||
"factory": "./src/generators/component-cypress-spec/component-cypress-spec#componentCypressGenerator",
|
||||
"schema": "./src/generators/component-cypress-spec/schema.json",
|
||||
"description": "Create a Cypress spec for a UI component that has a story.",
|
||||
"hidden": false
|
||||
},
|
||||
"hook": {
|
||||
"factory": "./src/generators/hook/hook",
|
||||
"schema": "./src/generators/hook/schema.json",
|
||||
|
||||
@ -14,7 +14,6 @@ export { reactDomVersion, reactVersion } from './src/utils/versions';
|
||||
export { applicationGenerator } from './src/generators/application/application';
|
||||
export { componentGenerator } from './src/generators/component/component';
|
||||
export { hookGenerator } from './src/generators/hook/hook';
|
||||
export { componentCypressGenerator } from './src/generators/component-cypress-spec/component-cypress-spec';
|
||||
export { componentStoryGenerator } from './src/generators/component-story/component-story';
|
||||
export { libraryGenerator } from './src/generators/library/library';
|
||||
export { reactInitGenerator } from './src/generators/init/init';
|
||||
|
||||
@ -1,235 +0,0 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
import { formatFile } from '../../utils/format-file';
|
||||
import applicationGenerator from '../application/application';
|
||||
import libraryGenerator from '../library/library';
|
||||
import componentCypressSpecGenerator from './component-cypress-spec';
|
||||
|
||||
describe('react:component-cypress-spec', () => {
|
||||
let appTree: Tree;
|
||||
|
||||
[
|
||||
{
|
||||
plainJS: false,
|
||||
testCmpSrcWithProps: `import React from 'react';
|
||||
|
||||
import './test.scss';
|
||||
|
||||
export interface TestProps {
|
||||
name: string;
|
||||
displayAge: boolean;
|
||||
}
|
||||
|
||||
export const Test = (props: TestProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to test component, {props.name}</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
`,
|
||||
testCmpSrcWithoutProps: `import React from 'react';
|
||||
|
||||
import './test.scss';
|
||||
|
||||
export const Test = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to test component</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
`,
|
||||
},
|
||||
{
|
||||
plainJS: true,
|
||||
testCmpSrcWithProps: `import React from 'react';
|
||||
|
||||
import './test.scss';
|
||||
export const Test = (props: TestProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to test component, {props.name}</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
`,
|
||||
testCmpSrcWithoutProps: `import React from 'react';
|
||||
import './test.scss';
|
||||
export const Test = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to test component</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Test;
|
||||
`,
|
||||
},
|
||||
].forEach((testConfig) => {
|
||||
let fileCmpExt = testConfig.plainJS ? 'js' : 'tsx';
|
||||
let fileExt = testConfig.plainJS ? 'js' : 'ts';
|
||||
|
||||
describe(`using ${
|
||||
testConfig.plainJS ? 'plain JS' : 'TypeScript'
|
||||
} setup`, () => {
|
||||
let cmpPath = `test-ui-lib/src/lib/test-ui-lib.${fileCmpExt}`;
|
||||
let cypressStorySpecFilePath = `test-ui-lib-e2e/src/integration/test-ui-lib/test-ui-lib.spec.${fileExt}`;
|
||||
|
||||
if (!testConfig.plainJS) {
|
||||
// hacky, but we should do this check only if we run with TypeScript,
|
||||
// detecting component props in plain JS is "not possible"
|
||||
describe('component with properties', () => {
|
||||
beforeEach(async () => {
|
||||
appTree = await createTestUILib('test-ui-lib', testConfig.plainJS);
|
||||
|
||||
appTree.write(cmpPath, testConfig.testCmpSrcWithProps);
|
||||
|
||||
await componentCypressSpecGenerator(appTree, {
|
||||
componentPath: `lib/test-ui-lib.${fileCmpExt}`,
|
||||
project: 'test-ui-lib',
|
||||
js: testConfig.plainJS,
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly set up the spec', () => {
|
||||
expect(
|
||||
formatFile`${appTree.read(cypressStorySpecFilePath, 'utf-8')}`
|
||||
)
|
||||
.toContain(formatFile`describe('test-ui-lib: Test component', () => {
|
||||
beforeEach(() => cy.visit('/iframe.html?id=test--primary&args=name;displayAge:false;'));
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('h1').should('contain', 'Welcome to Test!');
|
||||
});
|
||||
})
|
||||
`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('component without properties', () => {
|
||||
beforeEach(async () => {
|
||||
appTree = await createTestUILib('test-ui-lib', testConfig.plainJS);
|
||||
|
||||
appTree.write(cmpPath, testConfig.testCmpSrcWithoutProps);
|
||||
|
||||
await componentCypressSpecGenerator(appTree, {
|
||||
componentPath: `lib/test-ui-lib.${fileCmpExt}`,
|
||||
project: 'test-ui-lib',
|
||||
js: testConfig.plainJS,
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly set up the spec', () => {
|
||||
expect(formatFile`${appTree.read(cypressStorySpecFilePath, 'utf-8')}`)
|
||||
.toContain(formatFile`describe('test-ui-lib: Test component', () => {
|
||||
beforeEach(() => cy.visit('/iframe.html?id=test--primary'));
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('h1').should('contain', 'Welcome to Test!');
|
||||
});
|
||||
});
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should target the correct cypress suite', async () => {
|
||||
appTree = await createTestUILib('test-ui-lib');
|
||||
await applicationGenerator(appTree, {
|
||||
e2eTestRunner: 'none',
|
||||
linter: Linter.EsLint,
|
||||
name: `other-e2e`,
|
||||
skipFormat: true,
|
||||
style: 'css',
|
||||
unitTestRunner: 'none',
|
||||
});
|
||||
// since other-e2e isn't a real cypress project we mock the v10 cypress config
|
||||
appTree.write('other-e2e/cypress.config.ts', `export default {}`);
|
||||
await componentCypressSpecGenerator(appTree, {
|
||||
componentPath: `lib/test-ui-lib.tsx`,
|
||||
project: 'test-ui-lib',
|
||||
cypressProject: 'other-e2e',
|
||||
});
|
||||
expect(
|
||||
appTree.exists('other-e2e/src/e2e/test-ui-lib/test-ui-lib.cy.ts')
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
appTree.exists('test-ui-lib/src/e2e/test-ui-lib/test-ui-lib.cy.ts')
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should generate a .spec.ts file with cypress.json', async () => {
|
||||
appTree = await createTestUILib('test-ui-lib');
|
||||
await applicationGenerator(appTree, {
|
||||
e2eTestRunner: 'none',
|
||||
linter: Linter.EsLint,
|
||||
name: `other-e2e`,
|
||||
skipFormat: true,
|
||||
style: 'css',
|
||||
unitTestRunner: 'none',
|
||||
});
|
||||
appTree.delete(`other-e2e/cypress.config.ts`);
|
||||
appTree.write(`other-e2e/cypress.json`, '{}');
|
||||
await componentCypressSpecGenerator(appTree, {
|
||||
componentPath: `lib/test-ui-lib.tsx`,
|
||||
project: 'test-ui-lib',
|
||||
cypressProject: 'other-e2e',
|
||||
});
|
||||
expect(
|
||||
appTree.exists(
|
||||
'other-e2e/src/integration/test-ui-lib/test-ui-lib.spec.ts'
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
appTree.exists(
|
||||
'test-ui-lib/src/integration/test-ui-lib/test-ui-lib.spec.ts'
|
||||
)
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
export async function createTestUILib(
|
||||
libName: string,
|
||||
plainJS = false
|
||||
): Promise<Tree> {
|
||||
let appTree = createTreeWithEmptyWorkspace();
|
||||
await libraryGenerator(appTree, {
|
||||
name: libName,
|
||||
linter: Linter.EsLint,
|
||||
js: plainJS,
|
||||
component: true,
|
||||
skipFormat: true,
|
||||
skipTsConfig: false,
|
||||
style: 'css',
|
||||
unitTestRunner: 'jest',
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
});
|
||||
|
||||
// create some Nx app that we'll use to generate the cypress
|
||||
// spec into it. We don't need a real Cypress setup
|
||||
await applicationGenerator(appTree, {
|
||||
js: plainJS,
|
||||
e2eTestRunner: 'none',
|
||||
linter: Linter.EsLint,
|
||||
name: `${libName}-e2e`,
|
||||
skipFormat: true,
|
||||
style: 'css',
|
||||
unitTestRunner: 'none',
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
});
|
||||
|
||||
return appTree;
|
||||
}
|
||||
@ -1,184 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { basename, join } from 'path';
|
||||
import type * as ts from 'typescript';
|
||||
import {
|
||||
findExportDeclarationsForJsx,
|
||||
getComponentNode,
|
||||
parseComponentPropsInfo,
|
||||
} from '../../utils/ast-utils';
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
export interface CreateComponentSpecFileSchema {
|
||||
project: string;
|
||||
componentPath: string;
|
||||
js?: boolean;
|
||||
cypressProject?: string;
|
||||
skipFormat?: boolean;
|
||||
}
|
||||
|
||||
export async function componentCypressGenerator(
|
||||
host: Tree,
|
||||
schema: CreateComponentSpecFileSchema
|
||||
) {
|
||||
createComponentSpecFile(host, schema);
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: candidate to refactor with the angular component story
|
||||
export function getArgsDefaultValue(property: ts.SyntaxKind): string {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
const typeNameToDefault: Record<number, any> = {
|
||||
[tsModule.SyntaxKind.StringKeyword]: '',
|
||||
[tsModule.SyntaxKind.NumberKeyword]: 0,
|
||||
[tsModule.SyntaxKind.BooleanKeyword]: false,
|
||||
};
|
||||
|
||||
const resolvedValue = typeNameToDefault[property];
|
||||
if (resolvedValue === undefined) {
|
||||
return '';
|
||||
} else if (typeof resolvedValue === 'string') {
|
||||
return resolvedValue.replace(/\s/g, '+');
|
||||
} else {
|
||||
return resolvedValue;
|
||||
}
|
||||
}
|
||||
|
||||
export function createComponentSpecFile(
|
||||
tree: Tree,
|
||||
{ project, componentPath, js, cypressProject }: CreateComponentSpecFileSchema
|
||||
) {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
const e2eProjectName = cypressProject || `${project}-e2e`;
|
||||
const projects = getProjects(tree);
|
||||
const e2eProject = projects.get(e2eProjectName);
|
||||
// cypress >= v10 will have a cypress.config.ts < v10 will have a cypress.json
|
||||
const isCypressV10 = tree.exists(join(e2eProject.root, 'cypress.config.ts'));
|
||||
|
||||
const e2eLibIntegrationFolderPath = join(
|
||||
e2eProject.sourceRoot,
|
||||
isCypressV10 ? 'e2e' : 'integration'
|
||||
);
|
||||
|
||||
const proj = projects.get(project);
|
||||
const componentFilePath = joinPathFragments(proj.sourceRoot, componentPath);
|
||||
const componentName = componentFilePath
|
||||
.slice(componentFilePath.lastIndexOf('/') + 1)
|
||||
.replace('.tsx', '')
|
||||
.replace('.jsx', '')
|
||||
.replace('.js', '');
|
||||
|
||||
const contents = tree.read(componentFilePath, 'utf-8');
|
||||
if (contents === null) {
|
||||
throw new Error(`Failed to read ${componentFilePath}`);
|
||||
}
|
||||
|
||||
const sourceFile = tsModule.createSourceFile(
|
||||
componentFilePath,
|
||||
contents,
|
||||
tsModule.ScriptTarget.Latest,
|
||||
true
|
||||
);
|
||||
|
||||
const cmpDeclaration = getComponentNode(sourceFile);
|
||||
if (!cmpDeclaration) {
|
||||
const componentNodes = findExportDeclarationsForJsx(sourceFile);
|
||||
if (componentNodes?.length) {
|
||||
componentNodes.forEach((declaration) => {
|
||||
findPropsAndGenerateFileForCypress(
|
||||
tree,
|
||||
sourceFile,
|
||||
declaration,
|
||||
e2eLibIntegrationFolderPath,
|
||||
componentName,
|
||||
project,
|
||||
js,
|
||||
true
|
||||
);
|
||||
});
|
||||
} else {
|
||||
throw new Error(
|
||||
`Could not find any React component in file ${componentFilePath}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
findPropsAndGenerateFileForCypress(
|
||||
tree,
|
||||
sourceFile,
|
||||
cmpDeclaration,
|
||||
e2eLibIntegrationFolderPath,
|
||||
componentName,
|
||||
project,
|
||||
js
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function findPropsAndGenerateFileForCypress(
|
||||
tree: Tree,
|
||||
sourceFile: ts.SourceFile,
|
||||
cmpDeclaration: ts.Node,
|
||||
e2eLibIntegrationFolderPath: string,
|
||||
componentName: string,
|
||||
project: string,
|
||||
js: boolean,
|
||||
fromNodeArray?: boolean
|
||||
) {
|
||||
const info = parseComponentPropsInfo(sourceFile, cmpDeclaration);
|
||||
|
||||
let props: {
|
||||
name: string;
|
||||
defaultValue: any;
|
||||
}[] = [];
|
||||
|
||||
if (info) {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
|
||||
props = info.props.map((member) => {
|
||||
return {
|
||||
name: (member.name as ts.Identifier).text,
|
||||
defaultValue: tsModule.isBindingElement(member)
|
||||
? getArgsDefaultValue(member.kind)
|
||||
: getArgsDefaultValue(member.type.kind),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const isCypressV10 = basename(e2eLibIntegrationFolderPath) === 'e2e';
|
||||
const cyFilePrefix = isCypressV10 ? 'cy' : 'spec';
|
||||
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, './files'),
|
||||
`${e2eLibIntegrationFolderPath}/${
|
||||
fromNodeArray
|
||||
? componentName + '--' + (cmpDeclaration as any).name.text
|
||||
: componentName
|
||||
}`,
|
||||
{
|
||||
projectName: project,
|
||||
componentName,
|
||||
componentSelector: (cmpDeclaration as any).name.text,
|
||||
props,
|
||||
fileExt: js ? `${cyFilePrefix}.js` : `${cyFilePrefix}.ts`,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default componentCypressGenerator;
|
||||
@ -1,13 +0,0 @@
|
||||
describe('<%=projectName%>: <%= componentSelector %> component', () => {
|
||||
beforeEach(() => cy.visit('/iframe.html?id=<%= componentSelector.toLowerCase() %>--primary<% if ( props && props.length > 0 ) { %>&args=<% } %><%
|
||||
for(let prop of props) {
|
||||
%><%=prop.name%><%
|
||||
if(prop.defaultValue !== undefined && (prop.defaultValue || prop.defaultValue === false)) {
|
||||
%>:<%=prop.defaultValue%><%
|
||||
} %>;<%
|
||||
}%>'));
|
||||
|
||||
it('should render the component', () => {
|
||||
cy.get('h1').should('contain', 'Welcome to <%=componentSelector%>!');
|
||||
});
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"cli": "nx",
|
||||
"$id": "NxReactComponentCypressSpec",
|
||||
"title": "Create component Cypress spec",
|
||||
"description": "Create a Storybook Cypress spec for a UI component that has a story.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The project name for which to generate tests.",
|
||||
"examples": ["shared-ui-component"],
|
||||
"$default": {
|
||||
"$source": "projectName",
|
||||
"index": 0
|
||||
},
|
||||
"x-prompt": "What's name of the project for which to generate tests?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"componentPath": {
|
||||
"type": "string",
|
||||
"description": "Relative path to the component file from the library root?",
|
||||
"examples": ["lib/components"],
|
||||
"x-prompt": "What's path of the component relative to the project's lib root for which to generate a test?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript files rather than TypeScript files.",
|
||||
"default": false
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. By default, inferred from `project`."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
}
|
||||
},
|
||||
"required": ["project", "componentPath"]
|
||||
}
|
||||
@ -17,16 +17,6 @@
|
||||
"x-prompt": "For which project do you want to generate stories?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
import applicationGenerator from '../application/application';
|
||||
import storiesGenerator from './stories';
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
|
||||
describe('react:stories for applications', () => {
|
||||
let appTree: Tree;
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
appTree = await createTestUIApp('test-ui-app');
|
||||
|
||||
// create another component
|
||||
|
||||
@ -3,7 +3,6 @@ import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
import { Tree } from '@nx/devkit';
|
||||
import storiesGenerator from './stories';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import applicationGenerator from '../application/application';
|
||||
import { Linter } from '@nx/eslint';
|
||||
import libraryGenerator from '../library/library';
|
||||
|
||||
@ -244,18 +243,5 @@ export async function createTestUILib(
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
});
|
||||
|
||||
// create some Nx app that we'll use to generate the cypress
|
||||
// spec into it. We don't need a real Cypress setup
|
||||
|
||||
await applicationGenerator(appTree, {
|
||||
e2eTestRunner: 'none',
|
||||
linter: Linter.EsLint,
|
||||
skipFormat: true,
|
||||
style: 'css',
|
||||
unitTestRunner: 'none',
|
||||
name: `${libName}-e2e`,
|
||||
js: plainJS,
|
||||
projectNameAndRootFormat: 'as-provided',
|
||||
});
|
||||
return appTree;
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import componentStoryGenerator from '../component-story/component-story';
|
||||
import componentCypressSpecGenerator from '../component-cypress-spec/component-cypress-spec';
|
||||
import {
|
||||
findExportDeclarationsForJsx,
|
||||
getComponentNode,
|
||||
@ -11,7 +10,6 @@ import {
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
@ -30,8 +28,6 @@ export interface StorybookStoriesSchema {
|
||||
js?: boolean;
|
||||
ignorePaths?: string[];
|
||||
skipFormat?: boolean;
|
||||
cypressProject?: string;
|
||||
generateCypressSpecs?: boolean;
|
||||
}
|
||||
|
||||
export async function projectRootPath(
|
||||
@ -88,8 +84,6 @@ export async function createAllStories(
|
||||
js: boolean,
|
||||
projects: Map<string, ProjectConfiguration>,
|
||||
projectConfiguration: ProjectConfiguration,
|
||||
generateCypressSpecs?: boolean,
|
||||
cypressProject?: string,
|
||||
ignorePaths?: string[]
|
||||
) {
|
||||
const { isTheFileAStory } = await import('@nx/storybook/src/utils/utilities');
|
||||
@ -123,15 +117,6 @@ export async function createAllStories(
|
||||
}
|
||||
});
|
||||
|
||||
const e2eProjectName = cypressProject || `${projectName}-e2e`;
|
||||
const e2eProject = projects.get(e2eProjectName);
|
||||
|
||||
if (generateCypressSpecs && !e2eProject) {
|
||||
logger.info(
|
||||
`There was no e2e project "${e2eProjectName}" found, so cypress specs will not be generated. Pass "--cypressProject" to specify a different e2e project name`
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
componentPaths.map(async (componentPath) => {
|
||||
const relativeCmpDir = componentPath.replace(join(sourceRoot, '/'), '');
|
||||
@ -146,16 +131,6 @@ export async function createAllStories(
|
||||
skipFormat: true,
|
||||
interactionTests,
|
||||
});
|
||||
|
||||
if (generateCypressSpecs && e2eProject) {
|
||||
await componentCypressSpecGenerator(tree, {
|
||||
project: projectName,
|
||||
componentPath: relativeCmpDir,
|
||||
js,
|
||||
cypressProject,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -174,8 +149,6 @@ export async function storiesGenerator(
|
||||
schema.js,
|
||||
projects,
|
||||
projectConfiguration,
|
||||
schema.generateCypressSpecs,
|
||||
schema.cypressProject,
|
||||
schema.ignorePaths
|
||||
);
|
||||
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||
import { logger, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
@ -7,9 +5,7 @@ import applicationGenerator from '../application/application';
|
||||
import componentGenerator from '../component/component';
|
||||
import libraryGenerator from '../library/library';
|
||||
import storybookConfigurationGenerator from './configuration';
|
||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
|
||||
// nested code imports graph from the repo, which might have innacurate graph version
|
||||
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||
@ -20,11 +16,8 @@ jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||
|
||||
describe('react:storybook-configuration', () => {
|
||||
let appTree;
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
jest.spyOn(logger, 'warn').mockImplementation(() => {});
|
||||
jest.spyOn(logger, 'debug').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
@ -14,23 +14,11 @@ import {
|
||||
import { nxVersion, reactViteVersion } from '../../utils/versions';
|
||||
|
||||
async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
ensurePackage('@nx/cypress', nxVersion);
|
||||
const { getE2eProjectName } = await import(
|
||||
'@nx/cypress/src/utils/project-name'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(host, schema.project);
|
||||
const cypressProject = getE2eProjectName(
|
||||
schema.project,
|
||||
projectConfig.root,
|
||||
schema.cypressDirectory
|
||||
);
|
||||
|
||||
await storiesGenerator(host, {
|
||||
project: schema.project,
|
||||
generateCypressSpecs:
|
||||
schema.configureCypress && schema.generateCypressSpecs,
|
||||
js: schema.js,
|
||||
cypressProject,
|
||||
ignorePaths: schema.ignorePaths,
|
||||
skipFormat: true,
|
||||
interactionTests: schema.interactionTests ?? true,
|
||||
@ -85,10 +73,8 @@ export async function storybookConfigurationGeneratorInternal(
|
||||
|
||||
const installTask = await configurationGenerator(host, {
|
||||
project: schema.project,
|
||||
configureCypress: schema.configureCypress,
|
||||
js: schema.js,
|
||||
linter: schema.linter,
|
||||
cypressDirectory: schema.cypressDirectory,
|
||||
tsConfiguration: schema.tsConfiguration ?? true, // default is true
|
||||
interactionTests: schema.interactionTests ?? true, // default is true
|
||||
configureStaticServe: schema.configureStaticServe,
|
||||
|
||||
@ -9,8 +9,5 @@ export interface StorybookConfigureSchema {
|
||||
linter?: Linter | LinterType;
|
||||
ignorePaths?: string[];
|
||||
configureStaticServe?: boolean;
|
||||
configureCypress?: boolean;
|
||||
generateCypressSpecs?: boolean;
|
||||
cypressDirectory?: string;
|
||||
addPlugin?: boolean;
|
||||
}
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.stories.ts` files for components declared in this project?",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript story files rather than TypeScript story files.",
|
||||
|
||||
@ -3,13 +3,10 @@ import { Linter, LinterType } from '@nx/eslint';
|
||||
export interface StorybookConfigurationSchema {
|
||||
project: string;
|
||||
interactionTests?: boolean;
|
||||
configureCypress: boolean;
|
||||
generateStories?: boolean;
|
||||
generateCypressSpecs?: boolean;
|
||||
js?: boolean;
|
||||
tsConfiguration?: boolean;
|
||||
linter?: Linter | LinterType;
|
||||
cypressDirectory?: string;
|
||||
ignorePaths?: string[];
|
||||
configureTestRunner?: boolean;
|
||||
configureStaticServe?: boolean;
|
||||
|
||||
@ -26,11 +26,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.stories.ts` files for components declared in this project?",
|
||||
@ -38,11 +33,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times.",
|
||||
@ -50,11 +40,6 @@
|
||||
"default": true,
|
||||
"x-priority": "important"
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
"description": "Generate JavaScript story files rather than TypeScript story files.",
|
||||
|
||||
@ -22,7 +22,6 @@ describe('Storybook Configuration', () => {
|
||||
// ACT
|
||||
await storybookConfigurationGenerator(tree, {
|
||||
project: 'storybook-test',
|
||||
configureCypress: false,
|
||||
configureStaticServe: false,
|
||||
generateStories: true,
|
||||
addPlugin: true,
|
||||
|
||||
@ -17,7 +17,7 @@ import { nxVersion, storybookVersion } from '../../utils/versions';
|
||||
import configurationGenerator from './configuration';
|
||||
import * as variousProjects from './test-configs/various-projects.json';
|
||||
|
||||
// nested code imports graph from the repo, which might have innacurate graph version
|
||||
// nested code imports graph from the repo, which might have inaccurate graph version
|
||||
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||
createProjectGraphAsync: jest
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
|
||||
import { cypressProjectGenerator } from '../cypress-project/cypress-project';
|
||||
import { StorybookConfigureSchema } from './schema';
|
||||
import { initGenerator } from '../init/init';
|
||||
|
||||
@ -29,7 +28,6 @@ import {
|
||||
findMetroConfig,
|
||||
findNextConfig,
|
||||
findViteConfig,
|
||||
getE2EProjectName,
|
||||
projectIsRootProjectInStandaloneWorkspace,
|
||||
updateLintConfig,
|
||||
} from './lib/util-functions';
|
||||
@ -197,29 +195,6 @@ export async function configurationGeneratorInternal(
|
||||
devDeps['storybook'] = storybookVersion;
|
||||
}
|
||||
|
||||
// TODO(katerina): Nx 19 -> remove Cypress
|
||||
if (schema.configureCypress) {
|
||||
const e2eProject = await getE2EProjectName(tree, schema.project);
|
||||
if (!e2eProject) {
|
||||
const cypressTask = await cypressProjectGenerator(tree, {
|
||||
name: schema.project,
|
||||
js: schema.js,
|
||||
linter: schema.linter,
|
||||
directory: schema.cypressDirectory,
|
||||
standaloneConfig: schema.standaloneConfig,
|
||||
ciTargetName: schema.configureStaticServe
|
||||
? 'static-storybook'
|
||||
: undefined,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(cypressTask);
|
||||
} else {
|
||||
logger.warn(
|
||||
`There is already an e2e project setup for ${schema.project}, called ${e2eProject}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.tsConfiguration) {
|
||||
devDeps['ts-node'] = tsNodeVersion;
|
||||
}
|
||||
|
||||
@ -681,36 +681,6 @@ export function rootFileIsTs(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getE2EProjectName(
|
||||
tree: Tree,
|
||||
mainProject: string
|
||||
): Promise<string | undefined> {
|
||||
let e2eProject: string;
|
||||
const graph = await createProjectGraphAsync();
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nx/cypress:cypress',
|
||||
(options, projectName) => {
|
||||
if (e2eProject) {
|
||||
return;
|
||||
}
|
||||
if (options['devServerTarget']) {
|
||||
const { project, target } = parseTargetString(
|
||||
options['devServerTarget'],
|
||||
graph
|
||||
);
|
||||
if (
|
||||
(project === mainProject && target === 'serve') ||
|
||||
(project === mainProject && target === 'storybook')
|
||||
) {
|
||||
e2eProject = projectName;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
return e2eProject;
|
||||
}
|
||||
|
||||
export function findViteConfig(
|
||||
tree: Tree,
|
||||
projectRoot: string
|
||||
|
||||
@ -11,14 +11,6 @@ export interface StorybookConfigureSchema {
|
||||
standaloneConfig?: boolean;
|
||||
configureStaticServe?: boolean;
|
||||
skipFormat?: boolean;
|
||||
/**
|
||||
* @deprecated Use interactionTests instead. This option will be removed in v20.
|
||||
*/
|
||||
configureCypress?: boolean;
|
||||
/**
|
||||
* @deprecated Use interactionTests instead. This option will be removed in v20.
|
||||
*/
|
||||
cypressDirectory?: string;
|
||||
addPlugin?: boolean;
|
||||
|
||||
/**
|
||||
|
||||
@ -25,16 +25,6 @@
|
||||
"alias": ["configureTestRunner"],
|
||||
"default": true
|
||||
},
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -17,16 +17,6 @@
|
||||
"x-prompt": "For which project do you want to generate stories?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v20."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
|
||||
@ -21,8 +21,6 @@ export interface StorybookStoriesSchema {
|
||||
js?: boolean;
|
||||
ignorePaths?: string[];
|
||||
skipFormat?: boolean;
|
||||
cypressProject?: string;
|
||||
generateCypressSpecs?: boolean;
|
||||
}
|
||||
|
||||
export async function createAllStories(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user