Skip to content

Commit

Permalink
feat(testing): support NextJs Component Testing
Browse files Browse the repository at this point in the history
wip

wip

wip

wip

wipp

wip

working
  • Loading branch information
barbados-clemens committed Apr 18, 2023
1 parent 2dd59c3 commit 85e2c36
Show file tree
Hide file tree
Showing 22 changed files with 1,007 additions and 61 deletions.
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -5199,6 +5199,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,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"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": "",
"type": "object",
"examples": [
{
"command": "nx g @nrwl/next:cypress-component-configuration --project=my-react-project",
"description": "Add component testing to your Next project"
},
{
"command": "nx g @nrwl/next:cypress-component-configuration --project=my-react-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\": \"<path-to-project-root>/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"
}
190 changes: 190 additions & 0 deletions e2e/next/src/next-component-tests.test.ts
Original file line number Diff line number Diff line change
@@ -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<Record<string, any>>();
useEffect(() => {
data.then(setState);
}, []);
return (
<>
{state && <pre>{JSON.stringify(state, null, 2)}</pre>}
<p className="text-blue-600">Button</p>
<button className="text-white bg-black p-4">{props.text}</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(<Button text={'test'} />);
});
it('should apply tailwind', () => {
cy.mount(<Button text={'tailwind'} />);
// should have tailwind styles
cy.get('p').should('have.css', 'color', 'rgb(37, 99, 235)');
});
});
`;
});
}

function createLibWithCt(libName: string, buildable: boolean) {
runCLI(
`generate @nrwl/next:lib ${libName} --buildable=${buildable} --no-interactive`
);

runCLI(
`generate @nrwl/next:component button --project=${libName} --flat --export --no-interactive`
);
updateFile(`libs/${libName}/src/lib/button.tsx`, (content) => {
return `import { useEffect, useState } from 'react';
export interface ButtonProps {
text: string
}
export function Button(props: ButtonProps) {
return <button className="text-white bg-black p-4">{props.text}</button>
}
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(<Button text={'test'} />);
});
it('should apply tailwind', () => {
cy.mount(<Button text={'tailwind'} />);
// should have tailwind styles
cy.get('button').should('have.css', 'color', 'rgb(255, 255, 255)');
});
});
`;
});
updateFile(`libs/${libName}/cypress/support/styles.ct.css`, (content) => {
return `${content}
@tailwind base;
@tailwind components;
@tailwind utilities;
`;
});
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference types="cypress" />

// ***********************************************
// This example commands.ts shows you how to
// create various custom commands and overwrite
Expand All @@ -9,18 +10,25 @@
// https://on.cypress.io/custom-commands
// ***********************************************

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace Cypress {
namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
// TODO: define a mount command
// mount: typeof mount;
}
}
}

// Cypress.Commands.add('mount', mount)

//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "<%= offsetFromRoot %>../dist/out-tsc",
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
"module": "commonjs",
"types": ["cypress", "node"]
},
Expand Down
Loading

0 comments on commit 85e2c36

Please sign in to comment.