From 5f86929f34373b390ea6f93ca335082e9d6dc19e Mon Sep 17 00:00:00 2001 From: Emily Xiong Date: Fri, 14 Apr 2023 16:43:58 -0400 Subject: [PATCH] chore(react): improve react e2e tests speed (#15997) --- .github/workflows/e2e-matrix.yml | 15 +- .github/workflows/e2e-windows.yml | 15 +- CODEOWNERS | 3 +- e2e/{react => react-core}/jest.config.ts | 2 +- e2e/{react => react-core}/project.json | 4 +- e2e/{react => react-core}/src/logo.svg | 0 .../src/react-module-federation.test.ts} | 4 +- .../src/react-package.test.ts | 127 -------- e2e/{react => react-core}/src/react.test.ts | 275 ++++++++++-------- e2e/{react => react-core}/tsconfig.json | 0 e2e/{react => react-core}/tsconfig.spec.json | 0 e2e/react-extensions/jest.config.ts | 11 + e2e/react-extensions/project.json | 11 + .../src/cypress-component-tests.test.ts | 0 e2e/react-extensions/src/react-vite.test.ts | 140 +++++++++ e2e/react-extensions/tsconfig.json | 13 + e2e/react-extensions/tsconfig.spec.json | 20 ++ e2e/react/src/react-misc.test.ts | 49 ---- 18 files changed, 377 insertions(+), 312 deletions(-) rename e2e/{react => react-core}/jest.config.ts (89%) rename e2e/{react => react-core}/project.json (76%) rename e2e/{react => react-core}/src/logo.svg (100%) rename e2e/{react/src/react.module-federation.test.ts => react-core/src/react-module-federation.test.ts} (98%) rename e2e/{react => react-core}/src/react-package.test.ts (64%) rename e2e/{react => react-core}/src/react.test.ts (56%) rename e2e/{react => react-core}/tsconfig.json (100%) rename e2e/{react => react-core}/tsconfig.spec.json (100%) create mode 100644 e2e/react-extensions/jest.config.ts create mode 100644 e2e/react-extensions/project.json rename e2e/{react => react-extensions}/src/cypress-component-tests.test.ts (100%) create mode 100644 e2e/react-extensions/src/react-vite.test.ts create mode 100644 e2e/react-extensions/tsconfig.json create mode 100644 e2e/react-extensions/tsconfig.spec.json delete mode 100644 e2e/react/src/react-misc.test.ts diff --git a/.github/workflows/e2e-matrix.yml b/.github/workflows/e2e-matrix.yml index 8c936d4396..bbcf3e9adb 100644 --- a/.github/workflows/e2e-matrix.yml +++ b/.github/workflows/e2e-matrix.yml @@ -118,7 +118,8 @@ jobs: - e2e-nx-misc - e2e-nx-plugin - e2e-nx-run - - e2e-react + - e2e-react-core + - e2e-react-extensions - e2e-react-native - e2e-web - e2e-rollup @@ -172,7 +173,9 @@ jobs: codeowners: 'S04SYHYKGNP' - project: e2e-nx-run codeowners: 'S04SYHYKGNP' - - project: e2e-react + - project: e2e-react-core + codeowners: 'S04TNCNJG5N' + - project: e2e-react-extensions codeowners: 'S04TNCNJG5N' - project: e2e-react-native codeowners: 'S04TNCNJG5N' @@ -232,7 +235,9 @@ jobs: - node_version: 16 project: e2e-lerna-smoke-tests - node_version: 16 - project: e2e-react + project: e2e-react-core + - node_version: 16 + project: e2e-react-extensions - node_version: 16 project: e2e-react-native - node_version: 16 @@ -278,7 +283,9 @@ jobs: - node_version: 19 project: e2e-lerna-smoke-tests - node_version: 19 - project: e2e-react + project: e2e-react-core + - node_version: 19 + project: e2e-react-extensions - node_version: 19 project: e2e-react-native - node_version: 19 diff --git a/.github/workflows/e2e-windows.yml b/.github/workflows/e2e-windows.yml index 44917687f1..39d365fce3 100644 --- a/.github/workflows/e2e-windows.yml +++ b/.github/workflows/e2e-windows.yml @@ -87,7 +87,8 @@ jobs: - e2e-nx-misc - e2e-nx-plugin - e2e-nx-run - - e2e-react + - e2e-react-core + - e2e-react-extensions - e2e-web - e2e-rollup - e2e-storybook @@ -126,7 +127,9 @@ jobs: codeowners: 'S04SYHYKGNP' - project: e2e-nx-run codeowners: 'S04SYHYKGNP' - - project: e2e-react + - project: e2e-react-core + codeowners: 'S04TNCNJG5N' + - project: e2e-react-extensions codeowners: 'S04TNCNJG5N' - project: e2e-web codeowners: 'S04SJ6PL98X' @@ -173,7 +176,9 @@ jobs: - node_version: 16 project: e2e-lerna-smoke-tests - node_version: 16 - project: e2e-react + project: e2e-react-core + - node_version: 16 + project: e2e-react-extensions - node_version: 16 project: e2e-web - node_version: 16 @@ -213,7 +218,9 @@ jobs: - node_version: 19 project: e2e-lerna-smoke-tests - node_version: 19 - project: e2e-react + project: e2e-react-core + - node_version: 19 + project: e2e-react-extensions - node_version: 19 project: e2e-web - node_version: 19 diff --git a/CODEOWNERS b/CODEOWNERS index 8ff9d36125..06cf682b0e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -33,7 +33,8 @@ yarn.lock @FrozenPandaz @vsavkin @AgentEnder @jaysoo @JamesHenry /docs/shared/packages/react/** @jaysoo @ndcunningham @mandarini @xiongemi /docs/shared/packages/next/** @jaysoo @ndcunningham @xiongemi /packages/react/** @jaysoo @ndcunningham @mandarini @xiongemi -/e2e/react/** @jaysoo @mandarini @xiongemi @ndcunningham +/e2e/react-core/** @jaysoo @mandarini @xiongemi @ndcunningham +/e2e/react-extensions/** @jaysoo @mandarini @xiongemi @ndcunningham /packages/next/** @ndcunningham @jaysoo @xiongemi /e2e/next/** @ndcunningham @jaysoo @xiongemi /packages/react/plugins/component-testing/** @jaysoo @ndcunningham @barbados-clemens diff --git a/e2e/react/jest.config.ts b/e2e/react-core/jest.config.ts similarity index 89% rename from e2e/react/jest.config.ts rename to e2e/react-core/jest.config.ts index d1362b0be7..da8f01e5dd 100644 --- a/e2e/react/jest.config.ts +++ b/e2e/react-core/jest.config.ts @@ -6,6 +6,6 @@ export default { moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], maxWorkers: 1, globals: {}, - displayName: 'e2e-react', + displayName: 'e2e-react-core', preset: '../../jest.preset.js', }; diff --git a/e2e/react/project.json b/e2e/react-core/project.json similarity index 76% rename from e2e/react/project.json rename to e2e/react-core/project.json index 79437b742c..45e531ced3 100644 --- a/e2e/react/project.json +++ b/e2e/react-core/project.json @@ -1,7 +1,7 @@ { - "name": "e2e-react", + "name": "e2e-react-core", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "e2e/react", + "sourceRoot": "e2e/react-core", "projectType": "application", "targets": { "e2e": {}, diff --git a/e2e/react/src/logo.svg b/e2e/react-core/src/logo.svg similarity index 100% rename from e2e/react/src/logo.svg rename to e2e/react-core/src/logo.svg diff --git a/e2e/react/src/react.module-federation.test.ts b/e2e/react-core/src/react-module-federation.test.ts similarity index 98% rename from e2e/react/src/react.module-federation.test.ts rename to e2e/react-core/src/react-module-federation.test.ts index 35893beb0c..b4536ba686 100644 --- a/e2e/react/src/react.module-federation.test.ts +++ b/e2e/react-core/src/react-module-federation.test.ts @@ -15,9 +15,9 @@ import { describe('React Module Federation', () => { let proj: string; - beforeEach(() => (proj = newProject())); + beforeAll(() => (proj = newProject())); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should generate host and remote apps', async () => { const shell = uniq('shell'); diff --git a/e2e/react/src/react-package.test.ts b/e2e/react-core/src/react-package.test.ts similarity index 64% rename from e2e/react/src/react-package.test.ts rename to e2e/react-core/src/react-package.test.ts index c32226d682..b7f74165c0 100644 --- a/e2e/react/src/react-package.test.ts +++ b/e2e/react-core/src/react-package.test.ts @@ -264,130 +264,3 @@ export async function h() { return 'c'; } }, 250000); }); }); - -describe('Build React applications and libraries with Vite', () => { - let proj: string; - - beforeEach(() => { - proj = newProject(); - }); - - it('should test and lint app with bundler=vite', async () => { - const viteApp = uniq('viteapp'); - - runCLI( - `generate @nrwl/react:app ${viteApp} --bundler=vite --unitTestRunner=vitest --no-interactive` - ); - - const appTestResults = await runCLIAsync(`test ${viteApp}`); - expect(appTestResults.combinedOutput).toContain( - 'Successfully ran target test' - ); - - const appLintResults = await runCLIAsync(`lint ${viteApp}`); - expect(appLintResults.combinedOutput).toContain( - 'Successfully ran target lint' - ); - - await runCLIAsync(`build ${viteApp}`); - checkFilesExist(`dist/apps/${viteApp}/index.html`); - }, 300_000); - - it('should test and lint app with bundler=vite and inSourceTests', async () => { - const viteApp = uniq('viteapp'); - const viteLib = uniq('vitelib'); - - runCLI( - `generate @nrwl/react:app ${viteApp} --bundler=vite --unitTestRunner=vitest --inSourceTests --no-interactive` - ); - expect(() => { - checkFilesExist(`apps/${viteApp}/src/app/app.spec.tsx`); - }).toThrow(); - - const appTestResults = await runCLIAsync(`test ${viteApp}`); - expect(appTestResults.combinedOutput).toContain( - 'Successfully ran target test' - ); - - const appLintResults = await runCLIAsync(`lint ${viteApp}`); - expect(appLintResults.combinedOutput).toContain( - 'Successfully ran target lint' - ); - - await runCLIAsync(`build ${viteApp}`); - checkFilesExist(`dist/apps/${viteApp}/index.html`); - - runCLI( - `generate @nrwl/react:lib ${viteLib} --bundler=vite --inSourceTests --unitTestRunner=vitest --no-interactive` - ); - expect(() => { - checkFilesExist(`libs/${viteLib}/src/lib/${viteLib}.spec.tsx`); - }).toThrow(); - - runCLI( - `generate @nrwl/react:component comp1 --inSourceTests --export --project=${viteLib} --no-interactive` - ); - expect(() => { - checkFilesExist(`libs/${viteLib}/src/lib/comp1/comp1.spec.tsx`); - }).toThrow(); - - runCLI( - `generate @nrwl/react:component comp2 --export --project=${viteLib} --no-interactive` - ); - checkFilesExist(`libs/${viteLib}/src/lib/comp2/comp2.spec.tsx`); - - const libTestResults = await runCLIAsync(`test ${viteLib}`); - expect(libTestResults.combinedOutput).toContain( - 'Successfully ran target test' - ); - - const libLintResults = await runCLIAsync(`lint ${viteLib}`); - expect(libLintResults.combinedOutput).toContain( - 'Successfully ran target lint' - ); - - await runCLIAsync(`build ${viteLib}`); - checkFilesExist( - `dist/libs/${viteLib}/index.d.ts`, - `dist/libs/${viteLib}/index.js`, - `dist/libs/${viteLib}/index.mjs` - ); - }, 300_000); - - it('should support bundling with Vite', async () => { - const viteLib = uniq('vitelib'); - - runCLI( - `generate @nrwl/react:lib ${viteLib} --bundler=vite --no-interactive --unit-test-runner=none` - ); - - const packageJson = readJson('package.json'); - // Vite does not need these libraries to work. - expect(packageJson.dependencies['core-js']).toBeUndefined(); - expect(packageJson.dependencies['tslib']).toBeUndefined(); - - await runCLIAsync(`build ${viteLib}`); - - checkFilesExist( - `dist/libs/${viteLib}/package.json`, - `dist/libs/${viteLib}/index.d.ts`, - `dist/libs/${viteLib}/index.js`, - `dist/libs/${viteLib}/index.mjs` - ); - - // Convert non-buildable lib to buildable one - const nonBuildableLib = uniq('nonbuildablelib'); - runCLI( - `generate @nrwl/react:lib ${nonBuildableLib} --no-interactive --unitTestRunner=jest` - ); - runCLI( - `generate @nrwl/vite:configuration ${nonBuildableLib} --uiFramework=react --no-interactive` - ); - await runCLIAsync(`build ${nonBuildableLib}`); - checkFilesExist( - `dist/libs/${nonBuildableLib}/index.d.ts`, - `dist/libs/${nonBuildableLib}/index.js`, - `dist/libs/${nonBuildableLib}/index.mjs` - ); - }, 300_000); -}); diff --git a/e2e/react/src/react.test.ts b/e2e/react-core/src/react.test.ts similarity index 56% rename from e2e/react/src/react.test.ts rename to e2e/react-core/src/react.test.ts index 4fa7cc7af4..4eb5eeef3c 100644 --- a/e2e/react/src/react.test.ts +++ b/e2e/react-core/src/react.test.ts @@ -21,12 +21,12 @@ import { join } from 'path'; describe('React Applications', () => { let proj: string; - beforeEach(() => { + beforeAll(() => { proj = newProject(); ensureCypressInstallation(); }); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should be able to generate a react app + lib (with CSR and SSR)', async () => { const appName = uniq('app'); @@ -187,152 +187,183 @@ describe('React Applications', () => { expect(await killPorts()).toBeTruthy(); }, 250_000); - async function testGeneratedApp( - appName, - opts: { - checkStyles: boolean; - checkLinter: boolean; - checkE2E: boolean; - checkSourceMap?: boolean; - } - ) { - if (opts.checkLinter) { - const lintResults = runCLI(`lint ${appName}`); - expect(lintResults).toContain('All files pass linting.'); - } - - runCLI( - `build ${appName} --outputHashing none ${ - opts.checkSourceMap ? '--sourceMap' : '' - }` - ); - const filesToCheck = [ - `dist/apps/${appName}/index.html`, - `dist/apps/${appName}/runtime.js`, - `dist/apps/${appName}/main.js`, - ]; - - if (opts.checkSourceMap) { - filesToCheck.push(`dist/apps/${appName}/main.js.map`); - } - - if (opts.checkStyles) { - filesToCheck.push(`dist/apps/${appName}/styles.css`); - } - checkFilesExist(...filesToCheck); - - if (opts.checkStyles) { - expect(readFile(`dist/apps/${appName}/index.html`)).toContain( - '' - ); - } - - const testResults = await runCLIAsync(`test ${appName}`); - expect(testResults.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - - if (opts.checkE2E && runCypressTests()) { - const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); - expect(e2eResults).toContain('All specs passed!'); - expect(await killPorts()).toBeTruthy(); - } - } -}); - -describe('React Applications: --style option', () => { - // Only create workspace once - beforeAll(() => newProject()); - - it.each` - style - ${'css'} - ${'scss'} - ${'less'} - ${'styl'} - `('should support global and css modules', ({ style }) => { + it('should generate app with routing', async () => { const appName = uniq('app'); - runCLI( - `generate @nrwl/react:app ${appName} --style=${style} --bundler=webpack --no-interactive` - ); - // make sure stylePreprocessorOptions works - updateProjectConfig(appName, (config) => { - config.targets.build.options.stylePreprocessorOptions = { - includePaths: ['libs/shared/lib'], - }; - return config; - }); - updateFile( - `apps/${appName}/src/styles.${style}`, - `@import 'base.${style}';` - ); - updateFile( - `apps/${appName}/src/app/app.module.${style}`, - (s) => `@import 'base.${style}';\n${s}` - ); - updateFile( - `libs/shared/lib/base.${style}`, - `body { font-family: "Comic Sans MS"; }` + runCLI( + `generate @nrwl/react:app ${appName} --routing --bundler=webpack --no-interactive` ); runCLI(`build ${appName} --outputHashing none`); - expect(readFile(`dist/apps/${appName}/styles.css`)).toMatch( - /Comic Sans MS/ + checkFilesExist( + `dist/apps/${appName}/index.html`, + `dist/apps/${appName}/runtime.js`, + `dist/apps/${appName}/main.js` ); - }); -}); + }, 250_000); -describe('React Applications and Libs with PostCSS', () => { - let proj: string; - - beforeAll(() => (proj = newProject())); - - it('should support single path or auto-loading of PostCSS config files', async () => { + it('should be able to add a redux slice', async () => { const appName = uniq('app'); const libName = uniq('lib'); runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`); + runCLI(`g @nrwl/react:redux lemon --project=${appName}`); runCLI( - `g @nrwl/react:lib ${libName} --no-interactive --unit-test-runner=none` + `g @nrwl/react:lib ${libName} --unit-test-runner=jest --no-interactive` + ); + runCLI(`g @nrwl/react:redux orange --project=${libName}`); + + const appTestResults = await runCLIAsync(`test ${appName}`); + expect(appTestResults.combinedOutput).toContain( + 'Test Suites: 2 passed, 2 total' ); - const mainPath = `apps/${appName}/src/main.tsx`; - updateFile( - mainPath, - `import '@${proj}/${libName}';\n${readFile(mainPath)}` + const libTestResults = await runCLIAsync(`test ${libName}`); + expect(libTestResults.combinedOutput).toContain( + 'Test Suites: 2 passed, 2 total' ); + }, 250_000); - createFile( - `apps/${appName}/postcss.config.js`, - ` + describe('React Applications: --style option', () => { + it.each` + style + ${'css'} + ${'scss'} + ${'less'} + ${'styl'} + `('should support global and css modules', ({ style }) => { + const appName = uniq('app'); + runCLI( + `generate @nrwl/react:app ${appName} --style=${style} --bundler=webpack --no-interactive` + ); + + // make sure stylePreprocessorOptions works + updateProjectConfig(appName, (config) => { + config.targets.build.options.stylePreprocessorOptions = { + includePaths: ['libs/shared/lib'], + }; + return config; + }); + updateFile( + `apps/${appName}/src/styles.${style}`, + `@import 'base.${style}';` + ); + updateFile( + `apps/${appName}/src/app/app.module.${style}`, + (s) => `@import 'base.${style}';\n${s}` + ); + updateFile( + `libs/shared/lib/base.${style}`, + `body { font-family: "Comic Sans MS"; }` + ); + + runCLI(`build ${appName} --outputHashing none`); + + expect(readFile(`dist/apps/${appName}/styles.css`)).toMatch( + /Comic Sans MS/ + ); + }); + }); + + describe('React Applications and Libs with PostCSS', () => { + it('should support single path or auto-loading of PostCSS config files', async () => { + const appName = uniq('app'); + const libName = uniq('lib'); + + runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`); + runCLI( + `g @nrwl/react:lib ${libName} --no-interactive --unit-test-runner=none` + ); + + const mainPath = `apps/${appName}/src/main.tsx`; + updateFile( + mainPath, + `import '@${proj}/${libName}';\n${readFile(mainPath)}` + ); + + createFile( + `apps/${appName}/postcss.config.js`, + ` console.log('HELLO FROM APP'); // need this output for e2e test module.exports = {}; ` - ); - createFile( - `libs/${libName}/postcss.config.js`, - ` + ); + createFile( + `libs/${libName}/postcss.config.js`, + ` console.log('HELLO FROM LIB'); // need this output for e2e test module.exports = {}; ` - ); + ); - let buildResults = await runCLIAsync(`build ${appName}`); + let buildResults = await runCLIAsync(`build ${appName}`); - expect(buildResults.combinedOutput).toMatch(/HELLO FROM APP/); - expect(buildResults.combinedOutput).toMatch(/HELLO FROM LIB/); + expect(buildResults.combinedOutput).toMatch(/HELLO FROM APP/); + expect(buildResults.combinedOutput).toMatch(/HELLO FROM LIB/); - // Only load app PostCSS config - updateJson(`apps/${appName}/project.json`, (json) => { - json.targets.build.options.postcssConfig = `apps/${appName}/postcss.config.js`; - return json; - }); + // Only load app PostCSS config + updateJson(`apps/${appName}/project.json`, (json) => { + json.targets.build.options.postcssConfig = `apps/${appName}/postcss.config.js`; + return json; + }); - buildResults = await runCLIAsync(`build ${appName}`); + buildResults = await runCLIAsync(`build ${appName}`); - expect(buildResults.combinedOutput).toMatch(/HELLO FROM APP/); - expect(buildResults.combinedOutput).not.toMatch(/HELLO FROM LIB/); - }, 250_000); + expect(buildResults.combinedOutput).toMatch(/HELLO FROM APP/); + expect(buildResults.combinedOutput).not.toMatch(/HELLO FROM LIB/); + }, 250_000); + }); }); + +async function testGeneratedApp( + appName, + opts: { + checkStyles: boolean; + checkLinter: boolean; + checkE2E: boolean; + checkSourceMap?: boolean; + } +) { + if (opts.checkLinter) { + const lintResults = runCLI(`lint ${appName}`); + expect(lintResults).toContain('All files pass linting.'); + } + + runCLI( + `build ${appName} --outputHashing none ${ + opts.checkSourceMap ? '--sourceMap' : '' + }` + ); + const filesToCheck = [ + `dist/apps/${appName}/index.html`, + `dist/apps/${appName}/runtime.js`, + `dist/apps/${appName}/main.js`, + ]; + + if (opts.checkSourceMap) { + filesToCheck.push(`dist/apps/${appName}/main.js.map`); + } + + if (opts.checkStyles) { + filesToCheck.push(`dist/apps/${appName}/styles.css`); + } + checkFilesExist(...filesToCheck); + + if (opts.checkStyles) { + expect(readFile(`dist/apps/${appName}/index.html`)).toContain( + '' + ); + } + + const testResults = await runCLIAsync(`test ${appName}`); + expect(testResults.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + + if (opts.checkE2E && runCypressTests()) { + const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`); + expect(e2eResults).toContain('All specs passed!'); + expect(await killPorts()).toBeTruthy(); + } +} diff --git a/e2e/react/tsconfig.json b/e2e/react-core/tsconfig.json similarity index 100% rename from e2e/react/tsconfig.json rename to e2e/react-core/tsconfig.json diff --git a/e2e/react/tsconfig.spec.json b/e2e/react-core/tsconfig.spec.json similarity index 100% rename from e2e/react/tsconfig.spec.json rename to e2e/react-core/tsconfig.spec.json diff --git a/e2e/react-extensions/jest.config.ts b/e2e/react-extensions/jest.config.ts new file mode 100644 index 0000000000..bc7b24d4dd --- /dev/null +++ b/e2e/react-extensions/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + transform: { + '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], + maxWorkers: 1, + globals: {}, + displayName: 'e2e-react-extensions', + preset: '../../jest.preset.js', +}; diff --git a/e2e/react-extensions/project.json b/e2e/react-extensions/project.json new file mode 100644 index 0000000000..c2b16e9226 --- /dev/null +++ b/e2e/react-extensions/project.json @@ -0,0 +1,11 @@ +{ + "name": "e2e-react-extensions", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "e2e/react-extensions", + "projectType": "application", + "targets": { + "e2e": {}, + "run-e2e-tests": {} + }, + "implicitDependencies": ["react"] +} diff --git a/e2e/react/src/cypress-component-tests.test.ts b/e2e/react-extensions/src/cypress-component-tests.test.ts similarity index 100% rename from e2e/react/src/cypress-component-tests.test.ts rename to e2e/react-extensions/src/cypress-component-tests.test.ts diff --git a/e2e/react-extensions/src/react-vite.test.ts b/e2e/react-extensions/src/react-vite.test.ts new file mode 100644 index 0000000000..675251d715 --- /dev/null +++ b/e2e/react-extensions/src/react-vite.test.ts @@ -0,0 +1,140 @@ +import { + checkFilesExist, + cleanupProject, + newProject, + readJson, + runCLI, + runCLIAsync, + uniq, +} from '@nrwl/e2e/utils'; + +describe('Build React applications and libraries with Vite', () => { + let proj: string; + + beforeEach(() => { + proj = newProject(); + }); + + afterAll(() => { + cleanupProject(); + }); + + it('should test and lint app with bundler=vite', async () => { + const viteApp = uniq('viteapp'); + + runCLI( + `generate @nrwl/react:app ${viteApp} --bundler=vite --unitTestRunner=vitest --no-interactive` + ); + + const appTestResults = await runCLIAsync(`test ${viteApp}`); + expect(appTestResults.combinedOutput).toContain( + 'Successfully ran target test' + ); + + const appLintResults = await runCLIAsync(`lint ${viteApp}`); + expect(appLintResults.combinedOutput).toContain( + 'Successfully ran target lint' + ); + + await runCLIAsync(`build ${viteApp}`); + checkFilesExist(`dist/apps/${viteApp}/index.html`); + }, 300_000); + + it('should test and lint app with bundler=vite and inSourceTests', async () => { + const viteApp = uniq('viteapp'); + const viteLib = uniq('vitelib'); + + runCLI( + `generate @nrwl/react:app ${viteApp} --bundler=vite --unitTestRunner=vitest --inSourceTests --no-interactive` + ); + expect(() => { + checkFilesExist(`apps/${viteApp}/src/app/app.spec.tsx`); + }).toThrow(); + + const appTestResults = await runCLIAsync(`test ${viteApp}`); + expect(appTestResults.combinedOutput).toContain( + 'Successfully ran target test' + ); + + const appLintResults = await runCLIAsync(`lint ${viteApp}`); + expect(appLintResults.combinedOutput).toContain( + 'Successfully ran target lint' + ); + + await runCLIAsync(`build ${viteApp}`); + checkFilesExist(`dist/apps/${viteApp}/index.html`); + + runCLI( + `generate @nrwl/react:lib ${viteLib} --bundler=vite --inSourceTests --unitTestRunner=vitest --no-interactive` + ); + expect(() => { + checkFilesExist(`libs/${viteLib}/src/lib/${viteLib}.spec.tsx`); + }).toThrow(); + + runCLI( + `generate @nrwl/react:component comp1 --inSourceTests --export --project=${viteLib} --no-interactive` + ); + expect(() => { + checkFilesExist(`libs/${viteLib}/src/lib/comp1/comp1.spec.tsx`); + }).toThrow(); + + runCLI( + `generate @nrwl/react:component comp2 --export --project=${viteLib} --no-interactive` + ); + checkFilesExist(`libs/${viteLib}/src/lib/comp2/comp2.spec.tsx`); + + const libTestResults = await runCLIAsync(`test ${viteLib}`); + expect(libTestResults.combinedOutput).toContain( + 'Successfully ran target test' + ); + + const libLintResults = await runCLIAsync(`lint ${viteLib}`); + expect(libLintResults.combinedOutput).toContain( + 'Successfully ran target lint' + ); + + await runCLIAsync(`build ${viteLib}`); + checkFilesExist( + `dist/libs/${viteLib}/index.d.ts`, + `dist/libs/${viteLib}/index.js`, + `dist/libs/${viteLib}/index.mjs` + ); + }, 300_000); + + it('should support bundling with Vite', async () => { + const viteLib = uniq('vitelib'); + + runCLI( + `generate @nrwl/react:lib ${viteLib} --bundler=vite --no-interactive --unit-test-runner=none` + ); + + const packageJson = readJson('package.json'); + // Vite does not need these libraries to work. + expect(packageJson.dependencies['core-js']).toBeUndefined(); + expect(packageJson.dependencies['tslib']).toBeUndefined(); + + await runCLIAsync(`build ${viteLib}`); + + checkFilesExist( + `dist/libs/${viteLib}/package.json`, + `dist/libs/${viteLib}/index.d.ts`, + `dist/libs/${viteLib}/index.js`, + `dist/libs/${viteLib}/index.mjs` + ); + + // Convert non-buildable lib to buildable one + const nonBuildableLib = uniq('nonbuildablelib'); + runCLI( + `generate @nrwl/react:lib ${nonBuildableLib} --no-interactive --unitTestRunner=jest` + ); + runCLI( + `generate @nrwl/vite:configuration ${nonBuildableLib} --uiFramework=react --no-interactive` + ); + await runCLIAsync(`build ${nonBuildableLib}`); + checkFilesExist( + `dist/libs/${nonBuildableLib}/index.d.ts`, + `dist/libs/${nonBuildableLib}/index.js`, + `dist/libs/${nonBuildableLib}/index.mjs` + ); + }, 300_000); +}); diff --git a/e2e/react-extensions/tsconfig.json b/e2e/react-extensions/tsconfig.json new file mode 100644 index 0000000000..6d5abf8483 --- /dev/null +++ b/e2e/react-extensions/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node", "jest"] + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/e2e/react-extensions/tsconfig.spec.json b/e2e/react-extensions/tsconfig.spec.json new file mode 100644 index 0000000000..1a24bfb0a1 --- /dev/null +++ b/e2e/react-extensions/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/e2e/react/src/react-misc.test.ts b/e2e/react/src/react-misc.test.ts deleted file mode 100644 index 5d1b9ec07f..0000000000 --- a/e2e/react/src/react-misc.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - checkFilesExist, - newProject, - runCLI, - runCLIAsync, - uniq, -} from '@nrwl/e2e/utils'; - -describe('React Applications: additional packages', () => { - beforeAll(() => newProject()); - - it('should generate app with routing', async () => { - const appName = uniq('app'); - - runCLI( - `generate @nrwl/react:app ${appName} --routing --bundler=webpack --no-interactive` - ); - - runCLI(`build ${appName} --outputHashing none`); - - checkFilesExist( - `dist/apps/${appName}/index.html`, - `dist/apps/${appName}/runtime.js`, - `dist/apps/${appName}/main.js` - ); - }, 250_000); - - it('should be able to add a redux slice', async () => { - const appName = uniq('app'); - const libName = uniq('lib'); - - runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`); - runCLI(`g @nrwl/react:redux lemon --project=${appName}`); - runCLI( - `g @nrwl/react:lib ${libName} --unit-test-runner=jest --no-interactive` - ); - runCLI(`g @nrwl/react:redux orange --project=${libName}`); - - const appTestResults = await runCLIAsync(`test ${appName}`); - expect(appTestResults.combinedOutput).toContain( - 'Test Suites: 2 passed, 2 total' - ); - - const libTestResults = await runCLIAsync(`test ${libName}`); - expect(libTestResults.combinedOutput).toContain( - 'Test Suites: 2 passed, 2 total' - ); - }, 250_000); -});