diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index 81e944090b214..394b72f2bdedc 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -5183,6 +5183,14 @@ "children": [], "isExternal": false, "disableCollapsible": false + }, + { + "id": "cypress-component-configuration", + "path": "/packages/next/generators/cypress-component-configuration", + "name": "cypress-component-configuration", + "children": [], + "isExternal": false, + "disableCollapsible": false } ], "isExternal": false, diff --git a/docs/generated/manifests/packages.json b/docs/generated/manifests/packages.json index 531b9847bcb20..4989c4f058ddb 100644 --- a/docs/generated/manifests/packages.json +++ b/docs/generated/manifests/packages.json @@ -1380,6 +1380,15 @@ "originalFilePath": "/packages/next/src/generators/custom-server/schema.json", "path": "/packages/next/generators/custom-server", "type": "generator" + }, + "/packages/next/generators/cypress-component-configuration": { + "description": "cypress-component-configuration generator", + "file": "generated/packages/next/generators/cypress-component-configuration.json", + "hidden": false, + "name": "cypress-component-configuration", + "originalFilePath": "/packages/next/src/generators/cypress-component-configuration/schema.json", + "path": "/packages/next/generators/cypress-component-configuration", + "type": "generator" } }, "path": "/packages/next" diff --git a/docs/generated/packages-metadata.json b/docs/generated/packages-metadata.json index 7d134e5ccb70a..550955329139f 100644 --- a/docs/generated/packages-metadata.json +++ b/docs/generated/packages-metadata.json @@ -1361,6 +1361,15 @@ "originalFilePath": "/packages/next/src/generators/custom-server/schema.json", "path": "next/generators/custom-server", "type": "generator" + }, + { + "description": "cypress-component-configuration generator", + "file": "generated/packages/next/generators/cypress-component-configuration.json", + "hidden": false, + "name": "cypress-component-configuration", + "originalFilePath": "/packages/next/src/generators/cypress-component-configuration/schema.json", + "path": "next/generators/cypress-component-configuration", + "type": "generator" } ], "githubRoot": "https://github.com/nrwl/nx/blob/master", diff --git a/docs/generated/packages/next/generators/cypress-component-configuration.json b/docs/generated/packages/next/generators/cypress-component-configuration.json new file mode 100644 index 0000000000000..5d60d013595df --- /dev/null +++ b/docs/generated/packages/next/generators/cypress-component-configuration.json @@ -0,0 +1,53 @@ +{ + "name": "cypress-component-configuration", + "factory": "./src/generators/cypress-component-configuration/cypress-component-configuration", + "schema": { + "$schema": "http://json-schema.org/schema", + "cli": "nx", + "$id": "CypressComponentConfiguration", + "title": "NextJS Component Testing Configuration", + "description": "Add Cypress Componet Testing to an existing NextJS project.", + "type": "object", + "examples": [ + { + "command": "nx g @nrwl/next:cypress-component-configuration --project=my-next-project", + "description": "Add component testing to your Next project" + }, + { + "command": "nx g @nrwl/next:cypress-component-configuration --project=my-next-project --generate-tests", + "description": "Add component testing to your Next project and generate component tests for your existing components" + } + ], + "properties": { + "project": { + "type": "string", + "description": "The name of the project to add cypress component testing configuration to", + "x-dropdown": "projects", + "x-prompt": "What project should we add Cypress component testing to?", + "x-priority": "important" + }, + "generateTests": { + "type": "boolean", + "description": "Generate default component tests for existing components in the project", + "x-prompt": "Automatically generate tests for components declared in this project?", + "default": false, + "x-priority": "important" + }, + "skipFormat": { + "type": "boolean", + "description": "Skip formatting files", + "default": false, + "x-priority": "internal" + } + }, + "required": ["project"], + "examplesFile": "{% callout type=\"caution\" title=\"Can I use component testing?\" %}\nNext component testing with Nx requires **Cypress version 10.7.0** and up.\n\nYou can migrate with to v10 via the [migrate-to-cypress-11 generator](/packages/cypress/generators/migrate-to-cypress-11).\n\nThis generator is for Cypress based component testing.\n\nIf you want to test components via Storybook with Cypress, then check out the [storybook-configuration generator docs](/packages/react/generators/storybook-configuration)\n{% /callout %}\n\nThis generator is designed to get your Next project up and running with Cypress Component Testing.\n\n```shell\nnx g @nrwl/next:cypress-component-configuration --project=my-cool-next-project\n```\n\nRunning this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces.\n\n```ts {% fileName=\"cypress.config.ts\" %}\nimport { defineConfig } from 'cypress';\nimport { nxComponentTestingPreset } from '@nrwl/next/plugins/component-testing';\n\nexport default defineConfig({\n component: nxComponentTestingPreset(__filename),\n});\n```\n\nHere is an example on how to add custom options to the configuration\n\n```ts {% fileName=\"cypress.config.ts\" %}\nimport { defineConfig } from 'cypress';\nimport { nxComponentTestingPreset } from '@nrwl/next/plugins/component-testing';\n\nexport default defineConfig({\n component: {\n ...nxComponentTestingPreset(__filename),\n // extra options here\n },\n});\n```\n\n```shell\nnx g @nrwl/next:cypress-component-project --project=my-cool-next-project\n```\n\n## Auto Generating Tests\n\nYou can optionally use the `--generate-tests` flag to generate a test file for each component in your project.\n\n```shell\nnx g @nrwl/next:cypress-component-configuration --project=my-cool-next-project --generate-tests\n```\n\n## Running Component Tests\n\nA new `component-test` target will be added to the specified project to run your component tests.\n\n```shell\nnx g component-test my-cool-next-project\n```\n\nHere is an example of the project configuration that is generated.\n\n```json {% fileName=\"project.json\" %}\n{\n \"targets\" {\n \"component-test\": {\n \"executor\": \"@nrwl/cypress:cypress\",\n \"options\": {\n \"cypressConfig\": \"/cypress.config.ts\",\n \"testingType\": \"component\",\n \"skipServe\": true\n }\n }\n }\n}\n```\n\nNx also supports [Angular component testing](/packages/angular/generators/cypress-component-configuration).\n", + "presets": [] + }, + "description": "cypress-component-configuration generator", + "implementation": "/packages/next/src/generators/cypress-component-configuration/cypress-component-configuration.ts", + "aliases": [], + "hidden": false, + "path": "/packages/next/src/generators/cypress-component-configuration/schema.json", + "type": "generator" +} diff --git a/e2e/next/src/next-component-tests.test.ts b/e2e/next/src/next-component-tests.test.ts new file mode 100644 index 0000000000000..98de2658362c2 --- /dev/null +++ b/e2e/next/src/next-component-tests.test.ts @@ -0,0 +1,190 @@ +import { + createFile, + newProject, + runCLI, + uniq, + updateFile, + runCypressTests, + updateJson, +} from '@nrwl/e2e/utils'; + +describe('NextJs Component Testing', () => { + beforeAll(() => { + newProject({ + name: uniq('next-ct'), + }); + }); + + it('should test a NextJs app', () => { + const appName = uniq('next-app'); + createAppWithCt(appName); + if (runCypressTests()) { + expect(runCLI(`component-test ${appName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + addTailwindToApp(appName); + if (runCypressTests()) { + expect(runCLI(`component-test ${appName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + }); + + it('should test a NextJs lib', async () => { + const libName = uniq('next-lib'); + createLibWithCt(libName, false); + if (runCypressTests()) { + expect(runCLI(`component-test ${libName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + addTailwindToLib(libName); + if (runCypressTests()) { + expect(runCLI(`component-test ${libName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + }); + + it('should test a NextJs buildable lib', async () => { + const buildableLibName = uniq('next-buildable-lib'); + createLibWithCt(buildableLibName, true); + if (runCypressTests()) { + expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + + addTailwindToLib(buildableLibName); + if (runCypressTests()) { + expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( + 'All specs passed!' + ); + } + }); +}); + +function createAppWithCt(appName: string) { + runCLI(`generate @nrwl/next:app ${appName} --no-interactive`); + runCLI( + `generate @nrwl/next:component button --project=${appName} --directory=components --flat --no-interactive` + ); + createFile( + `apps/${appName}/public/data.json`, + JSON.stringify({ message: 'loaded from app data.json' }) + ); + + updateFile(`apps/${appName}/components/button.tsx`, (content) => { + return `import { useEffect, useState } from 'react'; + +export interface ButtonProps { + text: string; +} + +const data = fetch('/data.json').then((r) => r.json()); +export default function Button(props: ButtonProps) { + const [state, setState] = useState>(); + useEffect(() => { + data.then(setState); + }, []); + return ( + <> + {state &&
{JSON.stringify(state, null, 2)}
} +

Button

+ + + ); +} +`; + }); + + runCLI( + `generate @nrwl/next:cypress-component-configuration --project=${appName} --generate-tests --no-interactive` + ); +} + +function addTailwindToApp(appName: string) { + runCLI( + `generate @nrwl/react:setup-tailwind --project=${appName} --no-interactive` + ); + updateFile(`apps/${appName}/cypress/support/component.ts`, (content) => { + return `${content} +import '../../pages/styles.css'`; + }); + + updateFile(`apps/${appName}/components/button.cy.tsx`, (content) => { + return `import * as React from 'react'; +import Button from './button'; + +describe(Button.name, () => { + it('renders', () => { + cy.mount( +} + +export default Button; +`; + }); + + runCLI( + `generate @nrwl/next:cypress-component-configuration --project=${libName} --generate-tests --no-interactive` + ); +} +function addTailwindToLib(libName: string) { + createFile(`libs/${libName}/src/lib/styles.css`, ``); + runCLI( + `generate @nrwl/react:setup-tailwind --project=${libName} --no-interactive` + ); + updateFile(`libs/${libName}/src/lib/button.cy.tsx`, (content) => { + return `import * as React from 'react'; +import Button from './button'; + +describe(Button.name, () => { + it('renders', () => { + cy.mount(