import { cleanupProject, createFile, ensureCypressInstallation, newProject, runCLI, runE2ETests, uniq, updateFile, updateJson, } from '../../utils'; import { join } from 'path'; describe('React Cypress Component Tests', () => { let projectName; const appName = uniq('cy-react-app'); const usedInAppLibName = uniq('cy-react-lib'); const buildableLibName = uniq('cy-react-buildable-lib'); beforeAll(async () => { process.env.NX_ADD_PLUGINS = 'false'; projectName = newProject({ name: uniq('cy-react'), packages: ['@nx/react'], }); ensureCypressInstallation(); runCLI( `generate @nx/react:app ${appName} --bundler=webpack --no-interactive` ); updateJson('nx.json', (json) => ({ ...json, generators: { ...json.generators, '@nx/react': { library: { unitTestRunner: 'jest', }, }, }, })); runCLI( `generate @nx/react:component fancy-cmp --project=${appName} --no-interactive` ); runCLI( `generate @nx/react:lib ${usedInAppLibName} --no-interactive --unitTestRunner=jest` ); runCLI( `generate @nx/react:component btn --project=${usedInAppLibName} --export --no-interactive` ); // makes sure custom webpack is loading createFile( `apps/${appName}/src/assets/demo.svg`, ` nrwl ` ); updateFile( `libs/${usedInAppLibName}/src/lib/btn/btn.tsx`, ` import styles from './btn.module.css'; /* eslint-disable-next-line */ export interface BtnProps { text: string } export function Btn(props: BtnProps) { return (

Welcome to Btn!

); } export default Btn; ` ); updateFile( `apps/${appName}/src/app/app.tsx`, ` // eslint-disable-next-line @typescript-eslint/no-unused-vars import styles from './app.module.css'; import logo from '../assets/demo.svg'; import { Btn } from '@${projectName}/${usedInAppLibName}'; export function App() { return ( <> logo ); } export default App;` ); runCLI( `generate @nx/react:lib ${buildableLibName} --buildable --no-interactive --unitTestRunner=jest` ); runCLI( `generate @nx/react:component input --project=${buildableLibName} --export --no-interactive` ); updateFile( `libs/${buildableLibName}/src/lib/input/input.tsx`, ` import styles from './input.module.css'; /* eslint-disable-next-line */ export interface InputProps { readOnly: boolean } export function Input(props: InputProps) { return ( ); } export default Input; ` ); createFile('libs/assets/data.json', JSON.stringify({ data: 'data' })); updateJson(join('apps', appName, 'project.json'), (config) => { config.targets['build'].options.assets.push({ glob: '**/*', input: 'libs/assets', output: 'assets', }); return config; }); }); afterAll(() => { cleanupProject(); delete process.env.NX_ADD_PLUGINS; }); it('should test app', () => { runCLI( `generate @nx/react:cypress-component-configuration --project=${appName} --generate-tests` ); if (runE2ETests()) { expect(runCLI(`component-test ${appName} --no-watch`)).toContain( 'All specs passed!' ); } }, 300_000); it('should successfully component test lib being used in app', () => { runCLI( `generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests` ); if (runE2ETests()) { expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain( 'All specs passed!' ); } }, 300_000); it('should successfully component test lib being used in app using babel compiler', () => { runCLI( `generate @nx/react:cypress-component-configuration --project=${usedInAppLibName} --generate-tests` ); updateFile(`libs/${usedInAppLibName}/cypress.config.ts`, (content) => { // apply babel compiler return content.replace( 'nxComponentTestingPreset(__filename)', 'nxComponentTestingPreset(__filename, {compiler: "babel"})' ); }); if (runE2ETests()) { expect(runCLI(`component-test ${usedInAppLibName} --no-watch`)).toContain( 'All specs passed!' ); } }, 300_000); it('should test buildable lib not being used in app', () => { createFile( `libs/${buildableLibName}/src/lib/input/input.cy.tsx`, ` import * as React from 'react' import Input from './input' describe(Input.name, () => { it('renders', () => { cy.mount() cy.get('label').should('have.css', 'color', 'rgb(0, 0, 0)'); }) it('should be read only', () => { cy.mount() cy.get('input').should('have.attr', 'readonly'); }) }); ` ); runCLI( `generate @nx/react:cypress-component-configuration --project=${buildableLibName} --generate-tests --build-target=${appName}:build` ); if (runE2ETests()) { expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( 'All specs passed!' ); } // add tailwind runCLI(`generate @nx/react:setup-tailwind --project=${buildableLibName}`); updateFile( `libs/${buildableLibName}/src/styles.css`, ` @tailwind components; @tailwind base; @tailwind utilities; ` ); updateFile( `libs/${buildableLibName}/src/lib/input/input.cy.tsx`, (content) => { // text-green-500 should now apply return content.replace('rgb(0, 0, 0)', 'rgb(34, 197, 94)'); } ); updateFile( `libs/${buildableLibName}/src/lib/input/input.tsx`, (content) => { return `import '../../styles.css'; ${content}`; } ); if (runE2ETests()) { expect(runCLI(`component-test ${buildableLibName} --no-watch`)).toContain( 'All specs passed!' ); } }, 300_000); it('should work with async webpack config', async () => { // TODO: (caleb) for whatever reason the MF webpack config + CT is running, but cypress is not starting up? // are they overriding some option on top of each other causing cypress to not see it's running? createFile( `apps/${appName}/webpack.config.js`, ` const { composePlugins, withNx } = require('@nx/webpack'); const { withReact } = require('@nx/react'); module.exports = composePlugins( withNx(), withReact(), async function (configuration) { await new Promise((res) => { setTimeout(() => { console.log('I am from the custom async Webpack config'); res(); }, 1000); }); return configuration; } ); ` ); updateJson(join('apps', appName, 'project.json'), (config) => { config.targets[ 'build' ].options.webpackConfig = `apps/${appName}/webpack.config.js`; return config; }); if (runE2ETests()) { const results = runCLI(`component-test ${appName}`); expect(results).toContain('I am from the custom async Webpack config'); expect(results).toContain('All specs passed!'); } }); // flaky bc of upstream issue https://github.com/cypress-io/cypress/issues/25913 it.skip('should CT vite projects importing other projects', () => { const viteLibName = uniq('vite-lib'); runCLI( `generate @nrwl/react:lib ${viteLibName} --bundler=vite --no-interactive` ); updateFile(`libs/${viteLibName}/src/lib/${viteLibName}.tsx`, () => { return `import { Btn } from '@${projectName}/${usedInAppLibName}'; export function MyComponent() { return ( <>

hello

); } export default MyComponent;`; }); runCLI( `generate @nrwl/react:cypress-component-configuration --project=${viteLibName} --generate-tests --bundler=vite --build-target=${appName}:build` ); if (runE2ETests()) { expect(runCLI(`component-test ${viteLibName}`)).toContain( 'All specs passed!' ); } }); });