diff --git a/e2e/angular/src/angular-essential.test.ts b/e2e/angular/src/angular-essential.test.ts new file mode 100644 index 0000000000000..e7917e6dd0ff6 --- /dev/null +++ b/e2e/angular/src/angular-essential.test.ts @@ -0,0 +1,158 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * angular package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { + checkFilesExist, + cleanupProject, + newProject, + readFile, + runCLI, + uniq, + updateFile, +} from '@nx/e2e/utils'; +import { join } from 'node:path'; + +describe('Angular essential tests', () => { + beforeAll(() => { + newProject({ packages: ['@nx/angular', '@nx/eslint', '@nx/jest'] }); + }); + + afterAll(() => cleanupProject()); + + describe('Webpack', () => { + it('should be able to generate, build, test and lint an application', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/angular:app apps/${appName} --linter=eslint --bundler=webpack` + ); + + checkFilesExist(`apps/${appName}/src/app/app.component.ts`); + checkFilesExist(`apps/${appName}/src/app/app.config.ts`); + + const buildResult = runCLI(`build ${appName}`); + const lintResult = runCLI(`lint ${appName}`); + const testResult = runCLI(`test ${appName}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${appName}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${appName}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${appName}` + ); + }, 200_000); + }); + + describe('Rspack', () => { + it('should be able to generate, build, test and lint an application', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/angular:app apps/${appName} --linter=eslint --bundler=rspack` + ); + + const rspackConfigFileContents = readFile( + join('apps', appName, 'rspack.config.ts') + ); + const updatedConfigFileContents = rspackConfigFileContents.replace( + `maximumError: '1mb'`, + `maximumError: '3mb'` + ); + updateFile( + join('apps', appName, 'rspack.config.ts'), + updatedConfigFileContents + ); + + checkFilesExist(`apps/${appName}/src/app/app.component.ts`); + checkFilesExist(`apps/${appName}/src/app/app.config.ts`); + checkFilesExist(`apps/${appName}/rspack.config.ts`); + + const buildResult = runCLI(`build ${appName}`); + const lintResult = runCLI(`lint ${appName}`); + const testResult = runCLI(`test ${appName}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${appName}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${appName}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${appName}` + ); + }, 200_000); + }); + + describe('Esbuild (default)', () => { + it('should be able to generate, build, test and lint an application', async () => { + const appName = uniq('app'); + + runCLI(`generate @nx/angular:app apps/${appName} --linter=eslint`); + + checkFilesExist(`apps/${appName}/src/app/app.component.ts`); + checkFilesExist(`apps/${appName}/src/app/app.config.ts`); + + const buildResult = runCLI(`build ${appName}`); + const lintResult = runCLI(`lint ${appName}`); + const testResult = runCLI(`test ${appName}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${appName}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${appName}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${appName}` + ); + }, 200_000); + + it('should be able to generate, build, test and lint a library', async () => { + const libName = uniq('lib'); + + runCLI( + `generate @nx/angular:lib libs/${libName} --linter=eslint --buildable=true` + ); + + checkFilesExist(`libs/${libName}/src/index.ts`); + checkFilesExist( + `libs/${libName}/src/lib/${libName}/${libName}.component.ts` + ); + + const buildResult = runCLI(`build ${libName}`); + const lintResult = runCLI(`lint ${libName}`); + const testResult = runCLI(`test ${libName}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${libName}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${libName}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${libName}` + ); + }, 200_000); + }); +}); diff --git a/e2e/cypress/src/cypress-essential.test.ts b/e2e/cypress/src/cypress-essential.test.ts new file mode 100644 index 0000000000000..9181532b715cc --- /dev/null +++ b/e2e/cypress/src/cypress-essential.test.ts @@ -0,0 +1,86 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * cypress package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + checkFilesExist, + cleanupProject, + newProject, + runCLI, + runE2ETests, + uniq, +} from '@nx/e2e/utils'; + +describe('Cypress essential tests', () => { + beforeAll(() => { + newProject({ packages: ['@nx/react', '@nx/angular', '@nx/cypress'] }); + }); + + afterAll(() => cleanupProject()); + + describe('React', () => { + const myapp = uniq('myapp'); + it('should generate and run a react app with the Cypress as e2e test runner', () => { + runCLI( + `generate @nx/react:app apps/${myapp} --e2eTestRunner=cypress --linter=none` + ); + + // Making sure the cypress folders & files are created + checkFilesExist(`apps/${myapp}-e2e/cypress.config.ts`); + checkFilesExist(`apps/${myapp}-e2e/tsconfig.json`); + + checkFilesExist(`apps/${myapp}-e2e/src/fixtures/example.json`); + checkFilesExist(`apps/${myapp}-e2e/src/e2e/app.cy.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/app.po.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/e2e.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/commands.ts`); + + if (runE2ETests('cypress')) { + const result = runCLI(`e2e ${myapp}-e2e`); + expect(result).toContain('All specs passed!'); + } + }, 600_000); + }); + + describe('Angular', () => { + const myapp = uniq('myapp'); + it('should generate and run an angular app with the Cypress as e2e test runner', () => { + runCLI( + `generate @nx/angular:app apps/${myapp} --e2eTestRunner=cypress --linter=none` + ); + + // Making sure the cypress folders & files are created + checkFilesExist(`apps/${myapp}-e2e/cypress.config.ts`); + checkFilesExist(`apps/${myapp}-e2e/tsconfig.json`); + + checkFilesExist(`apps/${myapp}-e2e/src/fixtures/example.json`); + checkFilesExist(`apps/${myapp}-e2e/src/e2e/app.cy.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/app.po.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/e2e.ts`); + checkFilesExist(`apps/${myapp}-e2e/src/support/commands.ts`); + + if (runE2ETests('cypress')) { + const result = runCLI(`e2e ${myapp}-e2e`); + expect(result).toContain('All specs passed!'); + } + }, 600_000); + }); +}); diff --git a/e2e/esbuild/src/esbuild-essential.test.ts b/e2e/esbuild/src/esbuild-essential.test.ts new file mode 100644 index 0000000000000..1d05637a97fe3 --- /dev/null +++ b/e2e/esbuild/src/esbuild-essential.test.ts @@ -0,0 +1,158 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * esbuild package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + detectPackageManager, + newProject, + packageManagerLockFile, + readFile, + readJson, + runCLI, + runCommand, + tmpProjPath, + uniq, + updateFile, + updateJson, +} from '@nx/e2e/utils'; +import { join } from 'path'; +describe('Esbuild essential tests', () => { + beforeEach(() => newProject({ packages: ['@nx/js', '@nx/esbuild'] })); + + afterEach(() => cleanupProject()); + it('should setup and build projects using build', async () => { + const myPkg = uniq('my-pkg'); + runCLI( + `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` + ); + updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`); + updateJson(join('libs', myPkg, 'project.json'), (json) => { + json.targets.build.options.assets = [`libs/${myPkg}/assets/*`]; + return json; + }); + updateFile(`libs/${myPkg}/assets/a.md`, 'file a'); + updateFile(`libs/${myPkg}/assets/b.md`, 'file b'); + + // Copy package.json as asset rather than generate with Nx-detected fields. + runCLI(`build ${myPkg} --generatePackageJson=false`); + const packageJson = readJson(`libs/${myPkg}/package.json`); + // This is the file that is generated by lib generator (no deps, no main, etc.). + expect(packageJson).toEqual({ + name: `@proj/${myPkg}`, + version: '0.0.1', + private: true, + type: 'commonjs', + main: './index.cjs', + types: './index.d.ts', + dependencies: {}, + }); + + // Build normally with package.json generation. + runCLI(`build ${myPkg}`); + + expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/); + // main field should be set correctly in package.json + checkFilesExist( + `dist/libs/${myPkg}/package.json`, + `dist/libs/${myPkg}/${ + packageManagerLockFile[detectPackageManager(tmpProjPath())] + }` + ); + expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/); + + expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/); + // main field should be set correctly in package.json + + expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/); + expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/); + + /* Metafile is not generated by default, but passing --metafile generates it. + */ + checkFilesDoNotExist(`dist/libs/${myPkg}/meta.json`); + runCLI(`build ${myPkg} --metafile`); + checkFilesExist(`dist/libs/${myPkg}/meta.json`); + + /* Type errors are turned on by default + */ + updateFile( + `libs/${myPkg}/src/index.ts`, + ` + const x: number = 'a'; // type error + console.log('Bye'); + ` + ); + expect(() => runCLI(`build ${myPkg}`)).toThrow(); + expect(() => + runCLI(`build ${myPkg} --skipTypeCheck --no-declaration`) + ).not.toThrow(); + expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Bye/); + // Reset file + updateFile( + `libs/${myPkg}/src/index.ts`, + ` + console.log('Hello'); + ` + ); + }, 300_000); + + it('should support non-bundle builds', () => { + const myPkg = uniq('my-pkg'); + runCLI( + `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` + ); + updateFile(`libs/${myPkg}/src/lib/${myPkg}.ts`, `console.log('Hello');\n`); + updateFile(`libs/${myPkg}/src/index.ts`, `import './lib/${myPkg}.cjs';\n`); + + runCLI(`build ${myPkg} --bundle=false`); + + checkFilesExist( + `dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.cjs`, + `dist/libs/${myPkg}/index.cjs` + ); + // Test files are excluded in tsconfig (e.g. tsconfig.lib.json) + checkFilesDoNotExist( + `dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.spec.cjs` + ); + // Can run package (package.json fields are correctly generated) + expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/); + }, 300_000); + + it('should support external esbuild.config.js file', async () => { + const myPkg = uniq('my-pkg'); + runCLI( + `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` + ); + updateFile( + `libs/${myPkg}/esbuild.config.js`, + `console.log('custom config loaded');\nmodule.exports = {};\n` + ); + updateJson(join('libs', myPkg, 'project.json'), (json) => { + delete json.targets.build.options.esbuildOptions; + json.targets.build.options.esbuildConfig = `libs/${myPkg}/esbuild.config.js`; + return json; + }); + + const output = runCLI(`build ${myPkg}`); + expect(output).toContain('custom config loaded'); + }, 120_000); +}); diff --git a/e2e/esbuild/src/esbuild.test.ts b/e2e/esbuild/src/esbuild.test.ts index acae44e230ff2..fb39d136e9347 100644 --- a/e2e/esbuild/src/esbuild.test.ts +++ b/e2e/esbuild/src/esbuild.test.ts @@ -25,104 +25,6 @@ describe('EsBuild Plugin', () => { afterEach(() => cleanupProject()); - it('should setup and build projects using build', async () => { - const myPkg = uniq('my-pkg'); - runCLI( - `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` - ); - updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`); - updateJson(join('libs', myPkg, 'project.json'), (json) => { - json.targets.build.options.assets = [`libs/${myPkg}/assets/*`]; - return json; - }); - updateFile(`libs/${myPkg}/assets/a.md`, 'file a'); - updateFile(`libs/${myPkg}/assets/b.md`, 'file b'); - - // Copy package.json as asset rather than generate with Nx-detected fields. - runCLI(`build ${myPkg} --generatePackageJson=false`); - const packageJson = readJson(`libs/${myPkg}/package.json`); - // This is the file that is generated by lib generator (no deps, no main, etc.). - expect(packageJson).toEqual({ - name: `@proj/${myPkg}`, - version: '0.0.1', - private: true, - type: 'commonjs', - main: './index.cjs', - types: './index.d.ts', - dependencies: {}, - }); - - // Build normally with package.json generation. - runCLI(`build ${myPkg}`); - - expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/); - // main field should be set correctly in package.json - checkFilesExist( - `dist/libs/${myPkg}/package.json`, - `dist/libs/${myPkg}/${ - packageManagerLockFile[detectPackageManager(tmpProjPath())] - }` - ); - expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/); - - expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Hello/); - // main field should be set correctly in package.json - - expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/); - expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/); - - /* Metafile is not generated by default, but passing --metafile generates it. - */ - checkFilesDoNotExist(`dist/libs/${myPkg}/meta.json`); - runCLI(`build ${myPkg} --metafile`); - checkFilesExist(`dist/libs/${myPkg}/meta.json`); - - /* Type errors are turned on by default - */ - updateFile( - `libs/${myPkg}/src/index.ts`, - ` - const x: number = 'a'; // type error - console.log('Bye'); - ` - ); - expect(() => runCLI(`build ${myPkg}`)).toThrow(); - expect(() => - runCLI(`build ${myPkg} --skipTypeCheck --no-declaration`) - ).not.toThrow(); - expect(runCommand(`node dist/libs/${myPkg}/index.cjs`)).toMatch(/Bye/); - // Reset file - updateFile( - `libs/${myPkg}/src/index.ts`, - ` - console.log('Hello'); - ` - ); - - // TODO: Investigate why these assertions are flaky in CI - /* Test that watch mode copies assets on start, and again on update. - */ - // updateFile(`libs/${myPkg}/assets/a.md`, 'initial a'); - // const watchProcess = await runCommandUntil( - // `build ${myPkg} --watch`, - // (output) => { - // return output.includes('watching for changes'); - // } - // ); - // readFile(`dist/libs/${myPkg}/assets/a.md`).includes('initial a'); - // updateFile(`libs/${myPkg}/assets/a.md`, 'updated a'); - // await expect( - // waitUntil( - // () => readFile(`dist/libs/${myPkg}/assets/a.md`).includes('updated a'), - // { - // timeout: 20_000, - // ms: 500, - // } - // ) - // ).resolves.not.toThrow(); - // watchProcess.kill(); - }, 300_000); - it('should support bundling everything or only workspace libs', async () => { packageInstall('rambda', undefined, '~7.3.0', 'prod'); packageInstall('lodash', undefined, '~4.14.0', 'prod'); @@ -178,28 +80,6 @@ describe('EsBuild Plugin', () => { expect(runResult).toMatch(/Hello from child lib/); }, 300_000); - it('should support non-bundle builds', () => { - const myPkg = uniq('my-pkg'); - runCLI( - `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` - ); - updateFile(`libs/${myPkg}/src/lib/${myPkg}.ts`, `console.log('Hello');\n`); - updateFile(`libs/${myPkg}/src/index.ts`, `import './lib/${myPkg}.cjs';\n`); - - runCLI(`build ${myPkg} --bundle=false`); - - checkFilesExist( - `dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.cjs`, - `dist/libs/${myPkg}/index.cjs` - ); - // Test files are excluded in tsconfig (e.g. tsconfig.lib.json) - checkFilesDoNotExist( - `dist/libs/${myPkg}/libs/${myPkg}/src/lib/${myPkg}.spec.cjs` - ); - // Can run package (package.json fields are correctly generated) - expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/); - }, 300_000); - it('should support additional entry points', async () => { const myPkg = uniq('my-pkg'); runCLI( @@ -228,25 +108,6 @@ describe('EsBuild Plugin', () => { ).toMatch(/extra/); }, 120_000); - it('should support external esbuild.config.js file', async () => { - const myPkg = uniq('my-pkg'); - runCLI( - `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=esbuild` - ); - updateFile( - `libs/${myPkg}/esbuild.config.js`, - `console.log('custom config loaded');\nmodule.exports = {};\n` - ); - updateJson(join('libs', myPkg, 'project.json'), (json) => { - delete json.targets.build.options.esbuildOptions; - json.targets.build.options.esbuildConfig = `libs/${myPkg}/esbuild.config.js`; - return json; - }); - - const output = runCLI(`build ${myPkg}`); - expect(output).toContain('custom config loaded'); - }, 120_000); - it('should bundle in non-sensitive NX_ environment variables', () => { const myPkg = uniq('my-pkg'); runCLI( diff --git a/e2e/eslint/src/eslint-essential.test.ts b/e2e/eslint/src/eslint-essential.test.ts new file mode 100644 index 0000000000000..ab341bbff9298 --- /dev/null +++ b/e2e/eslint/src/eslint-essential.test.ts @@ -0,0 +1,74 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * eslint package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { + checkFilesExist, + cleanupProject, + newProject, + readJson, + runCLI, + uniq, + updateFile, +} from '@nx/e2e/utils'; + +describe('ESLint essential tests', () => { + let mylib: string; + + let originalEslintUseFlatConfigVal: string | undefined; + + beforeAll(() => { + // Opt into legacy .eslintrc config format for these tests + originalEslintUseFlatConfigVal = process.env.ESLINT_USE_FLAT_CONFIG; + process.env.ESLINT_USE_FLAT_CONFIG = 'false'; + newProject({ + packages: ['@nx/js', '@nx/eslint'], + }); + mylib = uniq('mylib'); + runCLI(`generate @nx/js:lib ${mylib} --linter=eslint`); + }); + + afterAll(() => { + cleanupProject(); + process.env.ESLINT_USE_FLAT_CONFIG = originalEslintUseFlatConfigVal; + }); + + it('should lint the library without errors', () => { + checkFilesExist(`${mylib}/.eslintrc.json`); + + const result = runCLI(`lint ${mylib}`); + expect(result).toContain('Successfully ran target lint'); + }); + + it('should lint the library with errors', () => { + updateFile(`${mylib}/src/index.ts`, `console.log("should fail");`); + + const eslintrc = readJson('.eslintrc.json'); + + eslintrc.overrides.forEach((override) => { + if (override.files.includes('*.ts')) { + override.rules['no-console'] = 'error'; + } + }); + updateFile('.eslintrc.json', JSON.stringify(eslintrc, null, 2)); + const result = runCLI(`lint ${mylib}`, { silenceError: true }); + expect(result).toContain('Unexpected console statement'); + }); +}); diff --git a/e2e/jest/src/jest-essential.test.ts b/e2e/jest/src/jest-essential.test.ts new file mode 100644 index 0000000000000..1402854ca61cd --- /dev/null +++ b/e2e/jest/src/jest-essential.test.ts @@ -0,0 +1,160 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * jest package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + cleanupProject, + expectJestTestsToPass, + newProject, + runCLI, + runCLIAsync, + uniq, + updateFile, +} from '@nx/e2e/utils'; +import { stripIndents } from 'nx/src/devkit-exports'; + +describe('Jest essential tests', () => { + beforeAll(() => { + newProject({ name: uniq('proj-jest'), packages: ['@nx/js', '@nx/node'] }); + }); + + afterAll(() => cleanupProject()); + + it('should be able test projects using jest', async () => { + await expectJestTestsToPass('@nx/js:lib --unitTestRunner=jest'); + }, 500000); + + it('should be resilient against NODE_ENV values', async () => { + const name = uniq('lib'); + runCLI( + `generate @nx/js:lib ${name} --unitTestRunner=jest --no-interactive` + ); + + const results = await runCLIAsync(`test ${name} --skip-nx-cache`, { + silenceError: true, + env: { + ...process.env, // need to set this for some reason, or else get "env: node: No such file or directory" + NODE_ENV: 'foobar', + }, + }); + expect(results.combinedOutput).toContain('Test Suites: 1 passed, 1 total'); + }); + + it('should merge with jest config globals', async () => { + const testGlobal = `'My Test Global'`; + const mylib = uniq('mylib'); + const utilLib = uniq('util-lib'); + runCLI( + `generate @nx/js:lib libs/${mylib} --unitTestRunner=jest --no-interactive` + ); + runCLI( + `generate @nx/js:lib libs/${utilLib} --importPath=@global-fun/globals --unitTestRunner=jest --no-interactive` + ); + updateFile( + `libs/${utilLib}/src/index.ts`, + stripIndents` + export function setup() {console.log('i am a global setup function')} + export function teardown() {console.log('i am a global teardown function')} + ` + ); + + updateFile(`libs/${mylib}/src/lib/${mylib}.ts`, `export class Test { }`); + + updateFile( + `libs/${mylib}/src/lib/${mylib}.spec.ts`, + ` + test('can access jest global', () => { + expect((global as any).testGlobal).toBe(${testGlobal}); + }); + ` + ); + + updateFile( + `libs/${mylib}/setup.ts`, + stripIndents` + const { registerTsProject } = require('@nx/js/src/internal'); + const { join } = require('path'); + const cleanup = registerTsProject(join(__dirname, '../../tsconfig.base.json')); + + import {setup} from '@global-fun/globals'; + export default async function() {setup();} + + cleanup(); + ` + ); + + updateFile( + `libs/${mylib}/teardown.ts`, + stripIndents` + const { registerTsProject } = require('@nx/js/src/internal'); + const { join } = require('path'); + const cleanup = registerTsProject(join(__dirname, '../../tsconfig.base.json')); + + import {teardown} from '@global-fun/globals'; + export default async function() {teardown();} + cleanup(); + ` + ); + + updateFile( + `libs/${mylib}/jest.config.ts`, + stripIndents` + export default { + testEnvironment: 'node', + displayName: "${mylib}", + preset: "../../jest.preset.js", + transform: { + "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], + }, + moduleFileExtensions: ["ts", "js", "html"], + coverageDirectory: "../../coverage/libs/${mylib}", + globals: { testGlobal: ${testGlobal} }, + globalSetup: '/setup.ts', + globalTeardown: '/teardown.ts' + };` + ); + + const appResult = await runCLIAsync(`test ${mylib} --no-watch`, { + silenceError: true, + }); + expect(appResult.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + }, 300000); + + it('should set the NODE_ENV to `test`', async () => { + const mylib = uniq('mylib'); + runCLI(`generate @nx/js:lib libs/${mylib} --unitTestRunner=jest`); + + updateFile( + `libs/${mylib}/src/lib/${mylib}.spec.ts`, + ` + test('can access jest global', () => { + expect(process.env['NODE_ENV']).toBe('test'); + }); + ` + ); + const appResult = await runCLIAsync(`test ${mylib} --no-watch`); + expect(appResult.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + }, 90000); +}); diff --git a/e2e/jest/src/jest.test.ts b/e2e/jest/src/jest.test.ts index eebf07802fddd..d97729afbd6cf 100644 --- a/e2e/jest/src/jest.test.ts +++ b/e2e/jest/src/jest.test.ts @@ -18,126 +18,6 @@ describe('Jest', () => { afterAll(() => cleanupProject()); - it('should be able test projects using jest', async () => { - await expectJestTestsToPass('@nx/js:lib --unitTestRunner=jest'); - }, 500000); - - it('should be resilient against NODE_ENV values', async () => { - const name = uniq('lib'); - runCLI( - `generate @nx/js:lib ${name} --unitTestRunner=jest --no-interactive` - ); - - const results = await runCLIAsync(`test ${name} --skip-nx-cache`, { - silenceError: true, - env: { - ...process.env, // need to set this for some reason, or else get "env: node: No such file or directory" - NODE_ENV: 'foobar', - }, - }); - expect(results.combinedOutput).toContain('Test Suites: 1 passed, 1 total'); - }); - - it('should merge with jest config globals', async () => { - const testGlobal = `'My Test Global'`; - const mylib = uniq('mylib'); - const utilLib = uniq('util-lib'); - runCLI( - `generate @nx/js:lib libs/${mylib} --unitTestRunner=jest --no-interactive` - ); - runCLI( - `generate @nx/js:lib libs/${utilLib} --importPath=@global-fun/globals --unitTestRunner=jest --no-interactive` - ); - updateFile( - `libs/${utilLib}/src/index.ts`, - stripIndents` - export function setup() {console.log('i am a global setup function')} - export function teardown() {console.log('i am a global teardown function')} - ` - ); - - updateFile(`libs/${mylib}/src/lib/${mylib}.ts`, `export class Test { }`); - - updateFile( - `libs/${mylib}/src/lib/${mylib}.spec.ts`, - ` - test('can access jest global', () => { - expect((global as any).testGlobal).toBe(${testGlobal}); - }); - ` - ); - - updateFile( - `libs/${mylib}/setup.ts`, - stripIndents` - const { registerTsProject } = require('@nx/js/src/internal'); - const { join } = require('path'); - const cleanup = registerTsProject(join(__dirname, '../../tsconfig.base.json')); - - import {setup} from '@global-fun/globals'; - export default async function() {setup();} - - cleanup(); - ` - ); - - updateFile( - `libs/${mylib}/teardown.ts`, - stripIndents` - const { registerTsProject } = require('@nx/js/src/internal'); - const { join } = require('path'); - const cleanup = registerTsProject(join(__dirname, '../../tsconfig.base.json')); - - import {teardown} from '@global-fun/globals'; - export default async function() {teardown();} - cleanup(); - ` - ); - - updateFile( - `libs/${mylib}/jest.config.ts`, - stripIndents` - export default { - testEnvironment: 'node', - displayName: "${mylib}", - preset: "../../jest.preset.js", - transform: { - "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], - }, - moduleFileExtensions: ["ts", "js", "html"], - coverageDirectory: "../../coverage/libs/${mylib}", - globals: { testGlobal: ${testGlobal} }, - globalSetup: '/setup.ts', - globalTeardown: '/teardown.ts' - };` - ); - - const appResult = await runCLIAsync(`test ${mylib} --no-watch`, { - silenceError: true, - }); - expect(appResult.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - }, 300000); - - it('should set the NODE_ENV to `test`', async () => { - const mylib = uniq('mylib'); - runCLI(`generate @nx/js:lib libs/${mylib} --unitTestRunner=jest`); - - updateFile( - `libs/${mylib}/src/lib/${mylib}.spec.ts`, - ` - test('can access jest global', () => { - expect(process.env['NODE_ENV']).toBe('test'); - }); - ` - ); - const appResult = await runCLIAsync(`test ${mylib} --no-watch`); - expect(appResult.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - }, 90000); - it('should be able to test node lib with babel-jest', async () => { const libName = uniq('babel-test-lib'); runCLI( diff --git a/e2e/js/src/js-essential.test.ts b/e2e/js/src/js-essential.test.ts new file mode 100644 index 0000000000000..bc270d0ef39e1 --- /dev/null +++ b/e2e/js/src/js-essential.test.ts @@ -0,0 +1,298 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * js package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + checkFilesDoNotExist, + checkFilesExist, + cleanupProject, + detectPackageManager, + newProject, + packageManagerLockFile, + readFile, + readJson, + rmDist, + runCLI, + runCLIAsync, + runCommand, + runCommandUntil, + tmpProjPath, + uniq, + updateFile, + updateJson, + waitUntil, +} from '@nx/e2e/utils'; + +describe('Js essential tests', () => { + let scope: string; + + describe('tsc', () => { + beforeAll(() => { + scope = newProject({ packages: ['@nx/js', '@nx/eslint', '@nx/jest'] }); + }); + + afterAll(() => cleanupProject()); + + it('should create a library that can be linted and tested', async () => { + const libName = uniq('mylib'); + const dirName = uniq('dir'); + + runCLI( + `generate @nx/js:lib --name=${dirName}-${libName} --directory libs/${dirName}/${libName}` + ); + + checkFilesExist( + `libs/${dirName}/${libName}/src/index.ts`, + `libs/${dirName}/${libName}/README.md` + ); + + const result = runCLI(`lint ${dirName}-${libName}`); + + expect(result).toContain( + `Successfully ran target lint for project ${dirName}-${libName}` + ); + + const testResult = await runCLIAsync(`test ${dirName}-${libName}`); + expect(testResult.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + }, 500_000); + + it('should create libs with js executors (--compiler=tsc)', async () => { + const lib = uniq('lib'); + runCLI(`generate @nx/js:lib libs/${lib} --bundler=tsc --no-interactive`); + const libPackageJson = readJson(`libs/${lib}/package.json`); + expect(libPackageJson.scripts).toBeUndefined(); + + expect(runCLI(`build ${lib}`)).toContain( + 'Done compiling TypeScript files' + ); + checkFilesExist( + `dist/libs/${lib}/README.md`, + `dist/libs/${lib}/package.json`, + `dist/libs/${lib}/src/index.js`, + `dist/libs/${lib}/src/lib/${lib}.js`, + `dist/libs/${lib}/src/index.d.ts`, + `dist/libs/${lib}/src/lib/${lib}.d.ts` + ); + + runCLI(`build ${lib} --generateLockfile=true`); + checkFilesExist( + `dist/libs/${lib}/package.json`, + `dist/libs/${lib}/${ + packageManagerLockFile[detectPackageManager(tmpProjPath())] + }` + ); + + updateJson(`libs/${lib}/project.json`, (json) => { + json.targets.build.options.assets.push({ + input: `libs/${lib}/docs`, + glob: '**/*.md', + output: 'docs', + }); + return json; + }); + const libBuildProcess = await runCommandUntil( + `build ${lib} --watch`, + (output) => output.includes(`Watching for file changes`), + { + env: { + NX_DAEMON: 'true', + }, + } + ); + updateFile(`libs/${lib}/README.md`, `Hello, World!`); + updateJson(`libs/${lib}/package.json`, (json) => { + json.version = '999.9.9'; + return json; + }); + updateFile(`libs/${lib}/docs/a/b/nested.md`, 'Nested File'); + await expect( + waitUntil( + () => + readFile(`dist/libs/${lib}/README.md`).includes(`Hello, World!`), + { + timeout: 20_000, + ms: 500, + } + ) + ).resolves.not.toThrow(); + await expect( + waitUntil( + () => + readFile(`dist/libs/${lib}/docs/a/b/nested.md`).includes( + `Nested File` + ), + { + timeout: 20_000, + ms: 500, + } + ) + ).resolves.not.toThrow(); + await expect( + waitUntil( + () => + readFile(`dist/libs/${lib}/package.json`).includes( + `"version": "999.9.9"` + ), + { + timeout: 20_000, + ms: 500, + } + ) + ).resolves.not.toThrow(); + libBuildProcess.kill(); + + const parentLib = uniq('parentlib'); + runCLI( + `generate @nx/js:lib libs/${parentLib} --bundler=tsc --no-interactive` + ); + const parentLibPackageJson = readJson(`libs/${parentLib}/package.json`); + expect(parentLibPackageJson.scripts).toBeUndefined(); + expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain( + 'Ran all test suites' + ); + + expect(runCLI(`build ${parentLib}`)).toContain( + 'Done compiling TypeScript files' + ); + checkFilesExist( + `dist/libs/${parentLib}/package.json`, + `dist/libs/${parentLib}/src/index.js`, + `dist/libs/${parentLib}/src/lib/${parentLib}.js`, + `dist/libs/${parentLib}/src/index.d.ts`, + `dist/libs/${parentLib}/src/lib/${parentLib}.d.ts` + ); + + const tsconfig = readJson(`tsconfig.base.json`); + expect(tsconfig.compilerOptions.paths).toMatchObject({ + [`@${scope}/${lib}`]: [`libs/${lib}/src/index.ts`], + [`@${scope}/${parentLib}`]: [`libs/${parentLib}/src/index.ts`], + }); + + updateFile(`libs/${parentLib}/src/index.ts`, () => { + return ` + import { ${lib} } from '@${scope}/${lib}' + export * from './lib/${parentLib}'; + `; + }); + + const output = runCLI(`build ${parentLib}`); + expect(output).toContain('1 task it depends on'); + expect(output).toContain('Done compiling TypeScript files'); + + updateJson(`libs/${lib}/tsconfig.json`, (json) => { + json.compilerOptions = { ...json.compilerOptions, importHelpers: true }; + return json; + }); + + updateJson(`libs/${lib}/package.json`, (json) => { + // Delete automatically generated helper dependency to test legacy behavior. + delete json.dependencies.tslib; + return json; + }); + + // check batch build + rmDist(); + let batchBuildOutput = runCLI(`build ${parentLib} --skip-nx-cache`, { + env: { NX_BATCH_MODE: 'true' }, + }); + + expect(batchBuildOutput).toContain(`Running 2 tasks with @nx/js:tsc`); + expect(batchBuildOutput).toContain( + `Compiling TypeScript files for project "${lib}"...` + ); + expect(batchBuildOutput).toContain( + `Done compiling TypeScript files for project "${lib}".` + ); + expect(batchBuildOutput).toContain( + `Compiling TypeScript files for project "${parentLib}"...` + ); + expect(batchBuildOutput).toContain( + `Done compiling TypeScript files for project "${parentLib}".` + ); + expect(batchBuildOutput).toContain( + `Successfully ran target build for project ${parentLib} and 1 task it depends on` + ); + + batchBuildOutput = runCLI(`build ${parentLib} --skip-nx-cache --batch`); + expect(batchBuildOutput).toContain(`Running 2 tasks with @nx/js:tsc`); + + checkFilesExist( + // parent + `dist/libs/${parentLib}/package.json`, + `dist/libs/${parentLib}/README.md`, + `dist/libs/${parentLib}/tsconfig.tsbuildinfo`, + `dist/libs/${parentLib}/src/index.js`, + `dist/libs/${parentLib}/src/index.d.ts`, + `dist/libs/${parentLib}/src/lib/${parentLib}.js`, + `dist/libs/${parentLib}/src/lib/${parentLib}.d.ts`, + // child + `dist/libs/${lib}/package.json`, + `dist/libs/${lib}/README.md`, + `dist/libs/${lib}/tsconfig.tsbuildinfo`, + `dist/libs/${lib}/src/index.js`, + `dist/libs/${lib}/src/index.d.ts`, + `dist/libs/${lib}/src/lib/${lib}.js`, + `dist/libs/${lib}/src/lib/${lib}.d.ts` + ); + + // run a second time skipping the nx cache and with the outputs present + const secondBatchBuildOutput = runCLI( + `build ${parentLib} --skip-nx-cache`, + { env: { NX_BATCH_MODE: 'true' } } + ); + expect(secondBatchBuildOutput).toContain( + `Successfully ran target build for project ${parentLib} and 1 task it depends on` + ); + }, 240_000); + }); + + describe('swc', () => { + beforeAll(() => { + scope = newProject({ packages: ['@nx/js', '@nx/eslint', '@nx/jest'] }); + }); + + afterAll(() => cleanupProject()); + + it('should create libs with js executors (--bundler=swc)', async () => { + const lib = uniq('lib'); + runCLI(`generate @nx/js:lib libs/${lib} --bundler=swc --no-interactive`); + + const libPackageJson = readJson(`libs/${lib}/package.json`); + expect(libPackageJson.scripts).toBeUndefined(); + + expect(() => runCLI(`build ${lib}`)).not.toThrow(); + checkFilesExist( + `dist/libs/${lib}/package.json`, + `dist/libs/${lib}/src/index.js`, + `dist/libs/${lib}/src/lib/${lib}.js`, + `dist/libs/${lib}/src/index.d.ts`, + `dist/libs/${lib}/src/lib/${lib}.d.ts` + ); + + const tsconfig = readJson(`tsconfig.base.json`); + expect(tsconfig.compilerOptions.paths).toEqual({ + [`@${scope}/${lib}`]: [`libs/${lib}/src/index.ts`], + }); + }, 240_000); + }); +}); diff --git a/e2e/js/src/js-executor-tsc.test.ts b/e2e/js/src/js-executor-tsc.test.ts index d4685d8a74ae7..b14ed261585d6 100644 --- a/e2e/js/src/js-executor-tsc.test.ts +++ b/e2e/js/src/js-executor-tsc.test.ts @@ -26,192 +26,6 @@ describe('js:tsc executor', () => { beforeAll(() => (scope = newProject())); afterAll(() => cleanupProject()); - it('should create libs with js executors (--compiler=tsc)', async () => { - const lib = uniq('lib'); - runCLI(`generate @nx/js:lib libs/${lib} --bundler=tsc --no-interactive`); - const libPackageJson = readJson(`libs/${lib}/package.json`); - expect(libPackageJson.scripts).toBeUndefined(); - - expect(runCLI(`build ${lib}`)).toContain('Done compiling TypeScript files'); - checkFilesExist( - `dist/libs/${lib}/README.md`, - `dist/libs/${lib}/package.json`, - `dist/libs/${lib}/src/index.js`, - `dist/libs/${lib}/src/lib/${lib}.js`, - `dist/libs/${lib}/src/index.d.ts`, - `dist/libs/${lib}/src/lib/${lib}.d.ts` - ); - - runCLI(`build ${lib} --generateLockfile=true`); - checkFilesExist( - `dist/libs/${lib}/package.json`, - `dist/libs/${lib}/${ - packageManagerLockFile[detectPackageManager(tmpProjPath())] - }` - ); - - updateJson(`libs/${lib}/project.json`, (json) => { - json.targets.build.options.assets.push({ - input: `libs/${lib}/docs`, - glob: '**/*.md', - output: 'docs', - }); - return json; - }); - const libBuildProcess = await runCommandUntil( - `build ${lib} --watch`, - (output) => output.includes(`Watching for file changes`), - { - env: { - NX_DAEMON: 'true', - }, - } - ); - updateFile(`libs/${lib}/README.md`, `Hello, World!`); - updateJson(`libs/${lib}/package.json`, (json) => { - json.version = '999.9.9'; - return json; - }); - updateFile(`libs/${lib}/docs/a/b/nested.md`, 'Nested File'); - await expect( - waitUntil( - () => readFile(`dist/libs/${lib}/README.md`).includes(`Hello, World!`), - { - timeout: 20_000, - ms: 500, - } - ) - ).resolves.not.toThrow(); - await expect( - waitUntil( - () => - readFile(`dist/libs/${lib}/docs/a/b/nested.md`).includes( - `Nested File` - ), - { - timeout: 20_000, - ms: 500, - } - ) - ).resolves.not.toThrow(); - await expect( - waitUntil( - () => - readFile(`dist/libs/${lib}/package.json`).includes( - `"version": "999.9.9"` - ), - { - timeout: 20_000, - ms: 500, - } - ) - ).resolves.not.toThrow(); - libBuildProcess.kill(); - - const parentLib = uniq('parentlib'); - runCLI( - `generate @nx/js:lib libs/${parentLib} --bundler=tsc --no-interactive` - ); - const parentLibPackageJson = readJson(`libs/${parentLib}/package.json`); - expect(parentLibPackageJson.scripts).toBeUndefined(); - expect((await runCLIAsync(`test ${parentLib}`)).combinedOutput).toContain( - 'Ran all test suites' - ); - - expect(runCLI(`build ${parentLib}`)).toContain( - 'Done compiling TypeScript files' - ); - checkFilesExist( - `dist/libs/${parentLib}/package.json`, - `dist/libs/${parentLib}/src/index.js`, - `dist/libs/${parentLib}/src/lib/${parentLib}.js`, - `dist/libs/${parentLib}/src/index.d.ts`, - `dist/libs/${parentLib}/src/lib/${parentLib}.d.ts` - ); - - const tsconfig = readJson(`tsconfig.base.json`); - expect(tsconfig.compilerOptions.paths).toEqual({ - [`@${scope}/${lib}`]: [`libs/${lib}/src/index.ts`], - [`@${scope}/${parentLib}`]: [`libs/${parentLib}/src/index.ts`], - }); - - updateFile(`libs/${parentLib}/src/index.ts`, () => { - return ` - import { ${lib} } from '@${scope}/${lib}' - export * from './lib/${parentLib}'; - `; - }); - - const output = runCLI(`build ${parentLib}`); - expect(output).toContain('1 task it depends on'); - expect(output).toContain('Done compiling TypeScript files'); - - updateJson(`libs/${lib}/tsconfig.json`, (json) => { - json.compilerOptions = { ...json.compilerOptions, importHelpers: true }; - return json; - }); - - updateJson(`libs/${lib}/package.json`, (json) => { - // Delete automatically generated helper dependency to test legacy behavior. - delete json.dependencies.tslib; - return json; - }); - - // check batch build - rmDist(); - let batchBuildOutput = runCLI(`build ${parentLib} --skip-nx-cache`, { - env: { NX_BATCH_MODE: 'true' }, - }); - - expect(batchBuildOutput).toContain(`Running 2 tasks with @nx/js:tsc`); - expect(batchBuildOutput).toContain( - `Compiling TypeScript files for project "${lib}"...` - ); - expect(batchBuildOutput).toContain( - `Done compiling TypeScript files for project "${lib}".` - ); - expect(batchBuildOutput).toContain( - `Compiling TypeScript files for project "${parentLib}"...` - ); - expect(batchBuildOutput).toContain( - `Done compiling TypeScript files for project "${parentLib}".` - ); - expect(batchBuildOutput).toContain( - `Successfully ran target build for project ${parentLib} and 1 task it depends on` - ); - - batchBuildOutput = runCLI(`build ${parentLib} --skip-nx-cache --batch`); - expect(batchBuildOutput).toContain(`Running 2 tasks with @nx/js:tsc`); - - checkFilesExist( - // parent - `dist/libs/${parentLib}/package.json`, - `dist/libs/${parentLib}/README.md`, - `dist/libs/${parentLib}/tsconfig.tsbuildinfo`, - `dist/libs/${parentLib}/src/index.js`, - `dist/libs/${parentLib}/src/index.d.ts`, - `dist/libs/${parentLib}/src/lib/${parentLib}.js`, - `dist/libs/${parentLib}/src/lib/${parentLib}.d.ts`, - // child - `dist/libs/${lib}/package.json`, - `dist/libs/${lib}/README.md`, - `dist/libs/${lib}/tsconfig.tsbuildinfo`, - `dist/libs/${lib}/src/index.js`, - `dist/libs/${lib}/src/index.d.ts`, - `dist/libs/${lib}/src/lib/${lib}.js`, - `dist/libs/${lib}/src/lib/${lib}.d.ts` - ); - - // run a second time skipping the nx cache and with the outputs present - const secondBatchBuildOutput = runCLI( - `build ${parentLib} --skip-nx-cache`, - { env: { NX_BATCH_MODE: 'true' } } - ); - expect(secondBatchBuildOutput).toContain( - `Successfully ran target build for project ${parentLib} and 1 task it depends on` - ); - }, 240_000); - it('should not create a `.babelrc` file when creating libs with js executors (--compiler=tsc)', () => { const lib = uniq('lib'); runCLI( diff --git a/e2e/js/src/js-generators.ts b/e2e/js/src/js-generators.ts index fcd90d488d070..afc7460896846 100644 --- a/e2e/js/src/js-generators.ts +++ b/e2e/js/src/js-generators.ts @@ -41,32 +41,6 @@ describe('js e2e', () => { }); }, 240_000); - it('should create a library that can be linted and tested', async () => { - const libName = uniq('mylib'); - const dirName = uniq('dir'); - - runCLI( - `generate @nx/js:lib --name=${dirName}-${libName} --directory libs/${dirName}/${libName}` - ); - - checkFilesExist( - `libs/${dirName}/${libName}/src/index.ts`, - `libs/${dirName}/${libName}/README.md` - ); - - // Lint - const result = runCLI(`lint ${dirName}-${libName}`); - - expect(result).toContain(`Linting "${dirName}-${libName}"...`); - expect(result).toContain('All files pass linting'); - - // Test - const testResult = await runCLIAsync(`test ${dirName}-${libName}`); - expect(testResult.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - }, 500_000); - it('should be able to add build to non-buildable projects', () => { const nonBuildable = uniq('nonbuildable'); diff --git a/e2e/next/src/next-essential.test.ts b/e2e/next/src/next-essential.test.ts new file mode 100644 index 0000000000000..c2852ae086042 --- /dev/null +++ b/e2e/next/src/next-essential.test.ts @@ -0,0 +1,83 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * next package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { checkFilesExist, newProject, runCLI, uniq } from '@nx/e2e/utils'; + +describe('Next essential tests', () => { + beforeAll(() => { + newProject({ name: uniq('proj-next'), packages: ['@nx/next'] }); + }); + + it('should be able to generate, build and lint projects ', () => { + const appName = uniq('app1'); + const libName = uniq('@my-org/lib1'); + + runCLI( + `generate @nx/next:app ${appName} --no-interactive --linter=eslint --unitTestRunner=jest` + ); + + // check files are generated without the layout directory ("apps/") and + // using the project name as the directory when no directory is provided + checkFilesExist(`${appName}/src/app/page.tsx`); + // check build works + expect(runCLI(`build ${appName}`)).toContain( + `Successfully ran target build for project ${appName}` + ); + // check tests pass + const appTestResult = runCLI(`test ${appName} --passWithNoTests`); + expect(appTestResult).toContain( + `Successfully ran target test for project ${appName}` + ); + + runCLI( + `generate @nx/next:lib ${libName} --buildable --no-interactive --linter=eslint --unitTestRunner=jest` + ); + + // check files are generated without the layout directory ("libs/") and + // using the project name as the directory when no directory is provided + checkFilesExist(`${libName}/src/index.ts`); + // check build works + expect(runCLI(`build ${libName}`)).toContain( + `Successfully ran target build for project ${libName}` + ); + }, 600_000); + + it('should support --custom-server flag (swc)', async () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/next:app ${appName} --no-interactive --custom-server --linter=eslint --unitTestRunner=jest` + ); + + // Check for custom server files added to source + checkFilesExist(`${appName}/server/main.ts`); + checkFilesExist(`${appName}/.server.swcrc`); + + const result = runCLI(`build ${appName}`); + + checkFilesExist(`dist/${appName}-server/server/main.js`); + + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + }, 300_000); +}); diff --git a/e2e/next/src/next.test.ts b/e2e/next/src/next.test.ts index 0c3e579ba5b51..1b09740484703 100644 --- a/e2e/next/src/next.test.ts +++ b/e2e/next/src/next.test.ts @@ -33,40 +33,6 @@ describe('Next.js Applications', () => { afterAll(() => cleanupProject()); - it('should support generating projects with the new name and root format', () => { - const appName = uniq('app1'); - const libName = uniq('@my-org/lib1'); - - runCLI( - `generate @nx/next:app ${appName} --no-interactive --linter=eslint --unitTestRunner=jest` - ); - - // check files are generated without the layout directory ("apps/") and - // using the project name as the directory when no directory is provided - checkFilesExist(`${appName}/src/app/page.tsx`); - // check build works - expect(runCLI(`build ${appName}`)).toContain( - `Successfully ran target build for project ${appName}` - ); - // check tests pass - const appTestResult = runCLI(`test ${appName} --passWithNoTests`); - expect(appTestResult).toContain( - `Successfully ran target test for project ${appName}` - ); - - runCLI( - `generate @nx/next:lib ${libName} --buildable --no-interactive --linter=eslint --unitTestRunner=jest` - ); - - // check files are generated without the layout directory ("libs/") and - // using the project name as the directory when no directory is provided - checkFilesExist(`${libName}/src/index.ts`); - // check build works - expect(runCLI(`build ${libName}`)).toContain( - `Successfully ran target build for project ${libName}` - ); - }, 600_000); - it('should build in dev mode without errors', async () => { const appName = uniq('app'); diff --git a/e2e/node/src/node-essential.test.ts b/e2e/node/src/node-essential.test.ts new file mode 100644 index 0000000000000..09ef8d03d2df6 --- /dev/null +++ b/e2e/node/src/node-essential.test.ts @@ -0,0 +1,83 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * node package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + checkFilesExist, + cleanupProject, + killPorts, + newProject, + runCLI, + runCLIAsync, + tmpProjPath, + uniq, + updateFile, +} from '@nx/e2e/utils'; + +import { execSync } from 'child_process'; + +describe('Node essential tests', () => { + let originalEnvPort; + beforeAll(() => { + originalEnvPort = process.env.PORT; + newProject({ + packages: ['@nx/node', '@nx/esbuild', '@nx/jest', '@nx/eslint'], + }); + }); + + afterAll(() => { + process.env.PORT = originalEnvPort; + cleanupProject(); + }); + + it('should be able to generate, build, test and lint an application', async () => { + const nodeapp = uniq('nodeapp'); + const port = getRandomPort(); + process.env.PORT = `${port}`; + runCLI( + `generate @nx/node:app apps/${nodeapp} --port=${port} --linter=eslint --unitTestRunner=jest` + ); + + const lintResult = runCLI(`lint ${nodeapp}`); + const testResult = runCLI(`test ${nodeapp}`); + + expect(lintResult).toContain( + `Successfully ran target lint for project ${nodeapp}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${nodeapp}` + ); + + updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`); + await runCLIAsync(`build ${nodeapp}`); + + checkFilesExist(`dist/apps/${nodeapp}/main.js`); + const result = execSync(`node dist/apps/${nodeapp}/main.js`, { + cwd: tmpProjPath(), + }).toString(); + expect(result).toContain('Hello World!'); + await killPorts(port); + }, 300000); +}); + +function getRandomPort() { + return Math.floor(1000 + Math.random() * 9000); +} diff --git a/e2e/node/src/node.test.ts b/e2e/node/src/node.test.ts index 8a850f9c69f32..039d89e2f9a5f 100644 --- a/e2e/node/src/node.test.ts +++ b/e2e/node/src/node.test.ts @@ -67,28 +67,6 @@ describe('Node Applications', () => { cleanupProject(); }); - it('should be able to generate an empty application', async () => { - const nodeapp = uniq('nodeapp'); - const port = getRandomPort(); - process.env.PORT = `${port}`; - runCLI( - `generate @nx/node:app apps/${nodeapp} --port=${port} --linter=eslint --unitTestRunner=jest` - ); - - expect(() => runCLI(`lint ${nodeapp}`)).not.toThrow(); - expect(() => runCLI(`test ${nodeapp}`)).not.toThrow(); - - updateFile(`apps/${nodeapp}/src/main.ts`, `console.log('Hello World!');`); - await runCLIAsync(`build ${nodeapp}`); - - checkFilesExist(`dist/apps/${nodeapp}/main.js`); - const result = execSync(`node dist/apps/${nodeapp}/main.js`, { - cwd: tmpProjPath(), - }).toString(); - expect(result).toContain('Hello World!'); - await killPorts(port); - }, 300000); - // TODO(crystal, @ndcunningham): This does not work because NxWebpackPlugin({}) outputFilename does not work. xit('should be able to generate the correct outputFileName in options', async () => { const nodeapp = uniq('nodeapp'); diff --git a/e2e/react/src/react-essential.test.ts b/e2e/react/src/react-essential.test.ts new file mode 100644 index 0000000000000..3871f952c0eea --- /dev/null +++ b/e2e/react/src/react-essential.test.ts @@ -0,0 +1,211 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * react package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + checkFilesExist, + cleanupProject, + newProject, + runCLI, + uniq, +} from '@nx/e2e/utils'; + +describe('React essential tests', () => { + beforeAll(() => { + newProject({ + packages: [ + '@nx/react', + '@nx/eslint', + '@nx/jest', + '@nx/vite', + '@nx/webpack', + '@nx/rspack', + ], + }); + }); + + afterAll(() => cleanupProject()); + describe('Vite', () => { + it('should be able to generate, build, test and lint an application', async () => { + const reactapp = uniq('reactapp'); + + runCLI( + `generate @nx/react:app apps/${reactapp} --linter=eslint --unitTestRunner=jest` + ); + + checkFilesExist(`apps/${reactapp}/vite.config.ts`); + checkFilesExist(`apps/${reactapp}/src/app/app.tsx`); + + const buildResult = runCLI(`build ${reactapp}`); + const lintResult = runCLI(`lint ${reactapp}`); + const testResult = runCLI(`test ${reactapp} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactapp}` + ); + + checkFilesExist(`dist/apps/${reactapp}/index.html`); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactapp}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactapp}` + ); + }, 600_000); + + it('should be able to generate, build, test and lint a library', async () => { + const reactlib = uniq('reactlib'); + + runCLI( + `generate @nx/react:lib --directory=libs/${reactlib} --bundler=vite --linter=eslint --unitTestRunner=vitest` + ); + + checkFilesExist(`libs/${reactlib}/vite.config.ts`); + checkFilesExist(`libs/${reactlib}/src/lib/${reactlib}.tsx`); + + const buildResult = runCLI(`build ${reactlib}`); + const lintResult = runCLI(`lint ${reactlib}`); + const testResult = runCLI(`test ${reactlib} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactlib}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactlib}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactlib}` + ); + }, 600_000); + }); + + describe('Webpack', () => { + it('should be able to generate, build, test and lint an application', async () => { + const reactapp = uniq('reactapp'); + + runCLI( + `generate @nx/react:app apps/${reactapp} --bundler=webpack --linter=eslint --unitTestRunner=jest` + ); + + checkFilesExist(`apps/${reactapp}/webpack.config.js`); + checkFilesExist(`apps/${reactapp}/src/app/app.tsx`); + + const buildResult = runCLI(`build ${reactapp}`); + const lintResult = runCLI(`lint ${reactapp}`); + const testResult = runCLI(`test ${reactapp} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactapp}` + ); + + checkFilesExist(`dist/apps/${reactapp}/index.html`); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactapp}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactapp}` + ); + }, 600_000); + }); + + describe('Rspack', () => { + it('should be able to generate, build, test and lint an application', async () => { + const reactapp = uniq('reactapp'); + + runCLI( + `generate @nx/react:app apps/${reactapp} --bundler=rspack --linter=eslint --unitTestRunner=jest` + ); + + checkFilesExist(`apps/${reactapp}/rspack.config.js`); + checkFilesExist(`apps/${reactapp}/src/app/app.tsx`); + + const buildResult = runCLI(`build ${reactapp}`); + const lintResult = runCLI(`lint ${reactapp}`); + const testResult = runCLI(`test ${reactapp} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactapp}` + ); + checkFilesExist(`dist/apps/${reactapp}/index.html`); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactapp}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactapp}` + ); + }, 600_000); + }); + + describe('Rsbuild', () => { + it('should be able to generate, build, test and lint an application', async () => { + const reactapp = uniq('reactapp'); + + runCLI( + `generate @nx/react:app apps/${reactapp} --bundler=rsbuild --linter=eslint --unitTestRunner=jest` + ); + + checkFilesExist(`apps/${reactapp}/rsbuild.config.ts`); + checkFilesExist(`apps/${reactapp}/src/app/app.tsx`); + + const buildResult = runCLI(`build ${reactapp}`); + const lintResult = runCLI(`lint ${reactapp}`); + const testResult = runCLI(`test ${reactapp} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactapp}` + ); + checkFilesExist(`apps/${reactapp}/dist/index.html`); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactapp}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactapp}` + ); + }, 600_000); + }); + + describe('Rollup', () => { + it('should be able to generate, build, test and lint a library', async () => { + const reactlib = uniq('reactlib'); + + runCLI( + `generate @nx/react:lib --directory=libs/${reactlib} --bundler=rollup --linter=eslint --unitTestRunner=jest` + ); + + checkFilesExist(`libs/${reactlib}/rollup.config.cjs`); + checkFilesExist(`libs/${reactlib}/src/lib/${reactlib}.tsx`); + + const buildResult = runCLI(`build ${reactlib}`); + const lintResult = runCLI(`lint ${reactlib}`); + const testResult = runCLI(`test ${reactlib} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${reactlib}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${reactlib}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${reactlib}` + ); + }); + }); +}); diff --git a/e2e/rollup/src/rollup-essential.test.ts b/e2e/rollup/src/rollup-essential.test.ts new file mode 100644 index 0000000000000..d005e7ba080b4 --- /dev/null +++ b/e2e/rollup/src/rollup-essential.test.ts @@ -0,0 +1,132 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * rollup package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { + checkFilesExist, + cleanupProject, + newProject, + readJson, + rmDist, + runCLI, + runCommand, + uniq, + updateFile, +} from '@nx/e2e/utils'; + +describe('Rollup essential tests', () => { + beforeAll(() => { + newProject({ + packages: ['@nx/rollup', '@nx/eslint', '@nx/jest'], + }); + }); + + afterAll(() => cleanupProject()); + + it('should be able to setup project to build node programs with rollup and different compilers', async () => { + const myPkg = uniq('my-pkg'); + runCLI( + `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=rollup` + ); + updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`); + + // babel (default) + runCLI( + `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts` + ); + updateFile( + `libs/${myPkg}/rollup.config.cjs`, + ` + const { withNx } = require('@nx/rollup/with-nx'); + module.exports = withNx({ + outputPath: '../../dist/libs/${myPkg}', + main: './src/index.ts', + tsConfig: './tsconfig.lib.json', + compiler: 'babel', + generateExportsField: true, + additionalEntryPoints: ['./src/{foo,bar}.ts'], + format: ['cjs', 'esm'] + }); + ` + ); + rmDist(); + runCLI(`build ${myPkg}`); + checkFilesExist(`dist/libs/${myPkg}/index.cjs.d.ts`); + expect(readJson(`dist/libs/${myPkg}/package.json`).exports).toEqual({ + '.': { + module: './index.esm.js', + import: './index.cjs.mjs', + default: './index.cjs.js', + types: './index.esm.d.ts', + }, + './package.json': './package.json', + }); + let output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); + expect(output).toMatch(/Hello/); + + // swc + runCLI( + `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts --compiler=swc` + ); + updateFile( + `libs/${myPkg}/rollup.config.cjs`, + ` + const { withNx } = require('@nx/rollup/with-nx'); + module.exports = withNx({ + outputPath: '../../dist/libs/${myPkg}', + main: './src/index.ts', + tsConfig: './tsconfig.lib.json', + compiler: 'swc', + generateExportsField: true, + additionalEntryPoints: ['./src/{foo,bar}.ts'], + format: ['cjs', 'esm'] + }); + ` + ); + rmDist(); + runCLI(`build ${myPkg}`); + output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); + expect(output).toMatch(/Hello/); + + // tsc + runCLI( + `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts --compiler=tsc` + ); + updateFile( + `libs/${myPkg}/rollup.config.cjs`, + ` + const { withNx } = require('@nx/rollup/with-nx'); + module.exports = withNx({ + outputPath: '../../dist/libs/${myPkg}', + main: './src/index.ts', + tsConfig: './tsconfig.lib.json', + compiler: 'tsc', + generateExportsField: true, + additionalEntryPoints: ['./src/{foo,bar}.ts'], + format: ['cjs', 'esm'] + }); + ` + ); + rmDist(); + runCLI(`build ${myPkg}`); + output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); + expect(output).toMatch(/Hello/); + }, 500_000); +}); diff --git a/e2e/rollup/src/rollup.test.ts b/e2e/rollup/src/rollup.test.ts index bce8c2ca4ccec..64b9ad2428e3d 100644 --- a/e2e/rollup/src/rollup.test.ts +++ b/e2e/rollup/src/rollup.test.ts @@ -15,96 +15,6 @@ describe('Rollup Plugin', () => { beforeAll(() => newProject({ packages: ['@nx/rollup', '@nx/js'] })); afterAll(() => cleanupProject()); - it('should be able to setup project to build node programs with rollup and different compilers', async () => { - const myPkg = uniq('my-pkg'); - runCLI( - `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --bundler=rollup` - ); - updateFile(`libs/${myPkg}/src/index.ts`, `console.log('Hello');\n`); - - // babel (default) - runCLI( - `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts` - ); - updateFile( - `libs/${myPkg}/rollup.config.cjs`, - ` - const { withNx } = require('@nx/rollup/with-nx'); - module.exports = withNx({ - outputPath: '../../dist/libs/${myPkg}', - main: './src/index.ts', - tsConfig: './tsconfig.lib.json', - compiler: 'babel', - generateExportsField: true, - additionalEntryPoints: ['./src/{foo,bar}.ts'], - format: ['cjs', 'esm'] - }); - ` - ); - rmDist(); - runCLI(`build ${myPkg}`); - checkFilesExist(`dist/libs/${myPkg}/index.cjs.d.ts`); - expect(readJson(`dist/libs/${myPkg}/package.json`).exports).toEqual({ - '.': { - module: './index.esm.js', - import: './index.cjs.mjs', - default: './index.cjs.js', - types: './index.esm.d.ts', - }, - './package.json': './package.json', - }); - let output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); - expect(output).toMatch(/Hello/); - - // swc - runCLI( - `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts --compiler=swc` - ); - updateFile( - `libs/${myPkg}/rollup.config.cjs`, - ` - const { withNx } = require('@nx/rollup/with-nx'); - module.exports = withNx({ - outputPath: '../../dist/libs/${myPkg}', - main: './src/index.ts', - tsConfig: './tsconfig.lib.json', - compiler: 'swc', - generateExportsField: true, - additionalEntryPoints: ['./src/{foo,bar}.ts'], - format: ['cjs', 'esm'] - }); - ` - ); - rmDist(); - runCLI(`build ${myPkg}`); - output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); - expect(output).toMatch(/Hello/); - - // tsc - runCLI( - `generate @nx/rollup:configuration ${myPkg} --tsConfig=./tsconfig.lib.json --main=./src/index.ts --compiler=tsc` - ); - updateFile( - `libs/${myPkg}/rollup.config.cjs`, - ` - const { withNx } = require('@nx/rollup/with-nx'); - module.exports = withNx({ - outputPath: '../../dist/libs/${myPkg}', - main: './src/index.ts', - tsConfig: './tsconfig.lib.json', - compiler: 'tsc', - generateExportsField: true, - additionalEntryPoints: ['./src/{foo,bar}.ts'], - format: ['cjs', 'esm'] - }); - ` - ); - rmDist(); - runCLI(`build ${myPkg}`); - output = runCommand(`node dist/libs/${myPkg}/index.cjs.js`); - expect(output).toMatch(/Hello/); - }, 500000); - it('should support additional entry-points and sourcemaps', async () => { const myPkg = uniq('my-pkg'); runCLI( diff --git a/e2e/rspack/tests/rspack-essential.test.ts b/e2e/rspack/tests/rspack-essential.test.ts new file mode 100644 index 0000000000000..d8ec81d841a35 --- /dev/null +++ b/e2e/rspack/tests/rspack-essential.test.ts @@ -0,0 +1,153 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * rspack package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + checkFilesExist, + cleanupProject, + newProject, + uniq, + updateFile, + runCLI, + createFile, +} from '@nx/e2e/utils'; + +describe('Rspack essential tests', () => { + beforeAll(() => { + newProject({ packages: ['@nx/react', '@nx/rspack'] }); + }); + + afterAll(() => cleanupProject()); + + it('should support a standard config object', () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/react:application --directory=apps/${appName} --bundler=rspack --e2eTestRunner=none` + ); + + updateFile( + `apps/${appName}/rspack.config.js`, + ` + const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin'); + const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin'); + const { join } = require('path'); + + module.exports = { + output: { + path: join(__dirname, '../../dist/${appName}'), + // do not remove dist, so files between builds will remain + clean: false, + }, + devServer: { + port: 4200, + historyApiFallback: { + index: '/index.html', + disableDotRule: true, + htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], + }, + }, + plugins: [ + new NxAppRspackPlugin({ + tsConfig: './tsconfig.app.json', + main: './src/main.tsx', + index: './src/index.html', + baseHref: '/', + assets: ['./src/favicon.ico', './src/assets'], + styles: ['./src/styles.scss'], + outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none', + optimization: process.env['NODE_ENV'] === 'production', + }), + new NxReactRspackPlugin({ + // Uncomment this line if you don't want to use SVGR + // See: https://react-svgr.com/ + // svgr: false + }), + ], + };` + ); + + const result = runCLI(`build ${appName}`); + + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + + // Ensure dist is not removed between builds since output.clean === false + createFile(`dist/apps/${appName}/extra.js`); + runCLI(`build ${appName} --skip-nx-cache`); + checkFilesExist(`dist/apps/${appName}/extra.js`); + }); + + it('should support a standard function that returns a config object', () => { + const appName = uniq('app'); + + runCLI( + `generate @nx/react:application --directory=apps/${appName} --bundler=rspack --e2eTestRunner=none` + ); + + updateFile( + `apps/${appName}/rspack.config.js`, + ` + const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin'); + const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin'); + const { join } = require('path'); + + module.exports = () => { + return { + output: { + path: join(__dirname, '../../dist/${appName}'), + }, + devServer: { + port: 4200, + historyApiFallback: { + index: '/index.html', + disableDotRule: true, + htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], + }, + }, + plugins: [ + new NxAppRspackPlugin({ + tsConfig: './tsconfig.app.json', + main: './src/main.tsx', + index: './src/index.html', + baseHref: '/', + assets: ['./src/favicon.ico', './src/assets'], + styles: ['./src/styles.scss'], + outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none', + optimization: process.env['NODE_ENV'] === 'production', + }), + new NxReactRspackPlugin({ + // Uncomment this line if you don't want to use SVGR + // See: https://react-svgr.com/ + // svgr: false + }), + ], + }; + };` + ); + + const result = runCLI(`build ${appName}`); + expect(result).toContain( + `Successfully ran target build for project ${appName}` + ); + }); +}); diff --git a/e2e/rspack/tests/rspack.spec.ts b/e2e/rspack/tests/rspack.spec.ts index cbe7b82f3238d..057ef9cc78f59 100644 --- a/e2e/rspack/tests/rspack.spec.ts +++ b/e2e/rspack/tests/rspack.spec.ts @@ -7,7 +7,6 @@ import { runCLI, createFile, readJson, - updateJson, } from '@nx/e2e/utils'; describe('rspack e2e', () => { @@ -40,120 +39,6 @@ describe('rspack e2e', () => { }); describe('config types', () => { - it('should support a standard config object', () => { - const appName = uniq('app'); - - runCLI( - `generate @nx/react:application --directory=apps/${appName} --bundler=rspack --e2eTestRunner=none` - ); - - updateFile( - `apps/${appName}/rspack.config.js`, - ` - const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin'); - const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin'); - const { join } = require('path'); - - module.exports = { - output: { - path: join(__dirname, '../../dist/${appName}'), - // do not remove dist, so files between builds will remain - clean: false, - }, - devServer: { - port: 4200, - historyApiFallback: { - index: '/index.html', - disableDotRule: true, - htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], - }, - }, - plugins: [ - new NxAppRspackPlugin({ - tsConfig: './tsconfig.app.json', - main: './src/main.tsx', - index: './src/index.html', - baseHref: '/', - assets: ['./src/favicon.ico', './src/assets'], - styles: ['./src/styles.scss'], - outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none', - optimization: process.env['NODE_ENV'] === 'production', - }), - new NxReactRspackPlugin({ - // Uncomment this line if you don't want to use SVGR - // See: https://react-svgr.com/ - // svgr: false - }), - ], - };` - ); - - const result = runCLI(`build ${appName}`); - - expect(result).toContain( - `Successfully ran target build for project ${appName}` - ); - - // Ensure dist is not removed between builds since output.clean === false - createFile(`dist/apps/${appName}/extra.js`); - runCLI(`build ${appName} --skip-nx-cache`); - checkFilesExist(`dist/apps/${appName}/extra.js`); - }); - - it('should support a standard function that returns a config object', () => { - const appName = uniq('app'); - - runCLI( - `generate @nx/react:application --directory=apps/${appName} --bundler=rspack --e2eTestRunner=none` - ); - - updateFile( - `apps/${appName}/rspack.config.js`, - ` - const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin'); - const { NxReactRspackPlugin } = require('@nx/rspack/react-plugin'); - const { join } = require('path'); - - module.exports = () => { - return { - output: { - path: join(__dirname, '../../dist/${appName}'), - }, - devServer: { - port: 4200, - historyApiFallback: { - index: '/index.html', - disableDotRule: true, - htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], - }, - }, - plugins: [ - new NxAppRspackPlugin({ - tsConfig: './tsconfig.app.json', - main: './src/main.tsx', - index: './src/index.html', - baseHref: '/', - assets: ['./src/favicon.ico', './src/assets'], - styles: ['./src/styles.scss'], - outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none', - optimization: process.env['NODE_ENV'] === 'production', - }), - new NxReactRspackPlugin({ - // Uncomment this line if you don't want to use SVGR - // See: https://react-svgr.com/ - // svgr: false - }), - ], - }; - };` - ); - - const result = runCLI(`build ${appName}`); - expect(result).toContain( - `Successfully ran target build for project ${appName}` - ); - }); - it('should support an array of standard config objects', () => { const appName = uniq('app'); const serverName = uniq('server'); diff --git a/e2e/vite/src/vite-essential.test.ts b/e2e/vite/src/vite-essential.test.ts new file mode 100644 index 0000000000000..3765934ff335e --- /dev/null +++ b/e2e/vite/src/vite-essential.test.ts @@ -0,0 +1,63 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * vite package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ + +import { + cleanupProject, + killProcessAndPorts, + newProject, + readJson, + runCLI, + runCommandUntil, + uniq, + updateFile, + updateJson, +} from '@nx/e2e/utils'; + +describe('Vite essential tests', () => { + beforeAll(() => { + newProject({ packages: ['@nx/js', '@nx/vite'] }); + }); + afterAll(() => { + cleanupProject(); + }); + + it('should be able to build, test and typecheck a library with vite', () => { + const viteLib = uniq('vite-lib'); + runCLI( + `generate @nx/js:lib packages/${viteLib} --bundler=vite --e2eTestRunner=none` + ); + + const buildResult = runCLI(`build ${viteLib}`); + const typecheckResult = runCLI(`typecheck ${viteLib}`); + const testResult = runCLI(`test ${viteLib} --passWithNoTests`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${viteLib}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${viteLib}` + ); + expect(typecheckResult).toContain( + `Successfully ran target typecheck for project ${viteLib}` + ); + }); +}); diff --git a/e2e/vue/src/vue-essential.test.ts b/e2e/vue/src/vue-essential.test.ts new file mode 100644 index 0000000000000..4bd91809b1e0c --- /dev/null +++ b/e2e/vue/src/vue-essential.test.ts @@ -0,0 +1,95 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * vue package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { cleanupProject, newProject, runCLI, uniq } from '@nx/e2e/utils'; +describe('Vue essential tests', () => { + beforeAll(() => { + newProject({ + packages: ['@nx/vue', '@nx/vite', '@nx/rspack', '@nx/eslint'], + }); + }); + + afterAll(() => cleanupProject()); + + describe('Vite', () => { + it('should build, test and lint an application', async () => { + const app = uniq('app'); + + runCLI( + `generate @nx/vue:app ${app} --unitTestRunner=vitest --linter=eslint --e2eTestRunner=none` + ); + const buildResult = runCLI(`build ${app}`); + const lintResult = runCLI(`lint ${app}`); + const testResult = runCLI(`test ${app}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${app}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${app}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${app}` + ); + }, 200_000); + + it('should be able to generate, build a library', async () => { + const lib = uniq('lib'); + + runCLI( + `generate @nx/vue:lib --directory=${lib} --bundler=vite --linter=eslint` + ); + + const buildResult = runCLI(`build ${lib}`); + const lintResult = runCLI(`lint ${lib}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${lib}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${lib}` + ); + }, 200_000); + }); + + describe('Rsbuild', () => { + it('should generate, build, test and lint an application', async () => { + const app = uniq('app'); + + runCLI( + `generate @nx/vue:app ${app} --bundler=rsbuild --unitTestRunner=vitest --linter=eslint --e2eTestRunner=none` + ); + const buildResult = runCLI(`build ${app}`); + const lintResult = runCLI(`lint ${app}`); + const testResult = runCLI(`test ${app}`); + + expect(buildResult).toContain( + `Successfully ran target build for project ${app}` + ); + expect(testResult).toContain( + `Successfully ran target test for project ${app}` + ); + expect(lintResult).toContain( + `Successfully ran target lint for project ${app}` + ); + }, 200_000); + }); +}); diff --git a/e2e/vue/src/vue.test.ts b/e2e/vue/src/vue.test.ts index 8205c04ad996e..d5a42125bac99 100644 --- a/e2e/vue/src/vue.test.ts +++ b/e2e/vue/src/vue.test.ts @@ -18,28 +18,6 @@ describe('Vue Plugin', () => { afterAll(() => cleanupProject()); - it('should serve application in dev mode', async () => { - const app = uniq('app'); - - runCLI( - `generate @nx/vue:app ${app} --unitTestRunner=vitest --e2eTestRunner=playwright` - ); - let result = runCLI(`test ${app}`); - expect(result).toContain(`Successfully ran target test for project ${app}`); - - result = runCLI(`build ${app}`); - expect(result).toContain( - `Successfully ran target build for project ${app}` - ); - - // TODO: enable this when tests are passing again. - // if (runE2ETests()) { - // const e2eResults = runCLI(`e2e ${app}-e2e --no-watch`); - // expect(e2eResults).toContain('Successfully ran target e2e'); - // expect(await killPorts()).toBeTruthy(); - // } - }, 200_000); - it('should serve application in dev mode with rsbuild', async () => { const app = uniq('app'); diff --git a/e2e/webpack/src/webpack-essential.test.ts b/e2e/webpack/src/webpack-essential.test.ts new file mode 100644 index 0000000000000..92068b7aadc89 --- /dev/null +++ b/e2e/webpack/src/webpack-essential.test.ts @@ -0,0 +1,174 @@ +/** + * GOLDEN TEST SUITE — CRITICAL HEALTH CHECK + * + * This file contains the “essential” (aka golden) tests for the + * webpack package. It tests the core public API. + * + * FAILURE POLICY + * - If any test here fails, the package is considered broken. + * - Fix the underlying issue immediately, do not merge or release until green. + * - Do not update snapshots here without an accompanying, intentional code change. + * + * GUIDELINES + * - Keep tests small and focused on fundamental behavior. + * - No edge-cases we should only test the critical paths. + * - CI pipelines must block on failures in this file (no skips or timeouts). + * + * MAINTENANCE + * - Whenever you change core behavior, update this file first to cover the new expectations. + * - Add new “essential” tests here only if they test core functionality. + * ────────────────────────────────────────────────────────────────────────────── + */ +import { + checkFilesExist, + cleanupProject, + createFile, + fileExists, + listFiles, + newProject, + readFile, + runCLI, + runCommand, + uniq, + updateFile, + updateJson, +} from '@nx/e2e/utils'; + +describe('Webpack essential tests', () => { + beforeAll(() => { + newProject({ + packages: ['@nx/web', '@nx/webpack', '@nx/eslint'], + }); + }); + + afterAll(() => cleanupProject()); + + it('should be able to build with NxWebpackPlugin and a standard webpack config file', () => { + const appName = uniq('app'); + runCLI( + `generate @nx/web:app ${appName} --bundler webpack --directory=apps/${appName}` + ); + updateFile(`apps/${appName}/src/main.ts`, `console.log('Hello');\n`); + updateFile(`apps/${appName}/src/foo.ts`, `console.log('Foo');\n`); + updateFile(`apps/${appName}/src/bar.ts`, `console.log('Bar');\n`); + + updateFile( + `apps/${appName}/webpack.config.js`, + ` + const path = require('path'); + const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); + + module.exports = { + target: 'node', + output: { + path: path.join(__dirname, '../../dist/apps/${appName}'), + // do not remove dist, so files between builds will remain + clean: false, + }, + plugins: [ + new NxAppWebpackPlugin({ + compiler: 'tsc', + main: './src/main.ts', + additionalEntryPoints: [ + './src/foo.ts', + { + entryName: 'bar', + entryPath: './src/bar.ts', + } + ], + tsConfig: './tsconfig.app.json', + outputHashing: 'none', + optimization: false, + }) + ] + };` + ); + + runCLI(`build ${appName}`); + + expect(runCommand(`node dist/apps/${appName}/main.js`)).toMatch(/Hello/); + expect(runCommand(`node dist/apps/${appName}/foo.js`)).toMatch(/Foo/); + expect(runCommand(`node dist/apps/${appName}/bar.js`)).toMatch(/Bar/); + + // Ensure dist is not removed between builds since output.clean === false + createFile(`dist/apps/${appName}/extra.js`); + runCLI(`build ${appName} --skip-nx-cache`); + checkFilesExist(`dist/apps/${appName}/extra.js`); + }, 500_000); + + it('should bundle in NX_PUBLIC_ environment variables', () => { + const appName = uniq('app'); + runCLI( + `generate @nx/web:app ${appName} --directory=apps/${appName} --bundler webpack` + ); + + checkFilesExist(`apps/${appName}/src/main.ts`); + updateFile( + `apps/${appName}/src/main.ts`, + ` + console.log(process.env['NX_PUBLIC_TEST']); + ` + ); + + runCLI(`build ${appName}`, { + env: { + NX_PUBLIC_TEST: 'foobar', + }, + }); + + const mainFile = listFiles(`dist/apps/${appName}`).filter((f) => + f.startsWith('main.') + ); + const content = readFile(`dist/apps/${appName}/${mainFile}`); + expect(content).toMatch(/foobar/); + }); + + it('should allow options to be passed from the executor', async () => { + const appName = uniq('app'); + runCLI( + `generate @nx/web:app ${appName} --directory=apps/${appName} --bundler webpack` + ); + + checkFilesExist(`apps/${appName}/project.json`); + updateJson(`apps/${appName}/project.json`, (json) => { + json.targets.build = { + executor: '@nx/webpack:webpack', + outputs: ['{options.outputPath}'], + options: { + generatePackageJson: true, // This should be passed to the plugin. + outputPath: `dist/apps/${appName}`, + webpackConfig: `apps/${appName}/webpack.config.js`, + }, + }; + return json; + }); + + checkFilesExist(`apps/${appName}/webpack.config.js`); + updateFile( + `apps/${appName}/webpack.config.js`, + ` + const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); + const { join } = require('path'); + module.exports = { + output: { + path: join(__dirname, '../../dist/apps/demo'), + }, + plugins: [ + new NxAppWebpackPlugin({ + // NOTE: generatePackageJson is missing here, but executor passes it. + target: 'web', + compiler: 'swc', + main: './src/main.ts', + tsConfig: './tsconfig.app.json', + optimization: false, + outputHashing: 'none', + }), + ], + };` + ); + + runCLI(`build ${appName}`); + + fileExists(`dist/apps/${appName}/package.json`); + }); +}); diff --git a/e2e/webpack/src/webpack.test.ts b/e2e/webpack/src/webpack.test.ts index 367ee724c9609..71893f9bd7ffa 100644 --- a/e2e/webpack/src/webpack.test.ts +++ b/e2e/webpack/src/webpack.test.ts @@ -135,86 +135,6 @@ describe('Webpack Plugin', () => { expect(output).toContain('Babel env is babelEnv'); }, 500_000); - it('should be able to build with NxWebpackPlugin and a standard webpack config file', () => { - const appName = uniq('app'); - runCLI( - `generate @nx/web:app ${appName} --bundler webpack --directory=apps/${appName}` - ); - updateFile(`apps/${appName}/src/main.ts`, `console.log('Hello');\n`); - updateFile(`apps/${appName}/src/foo.ts`, `console.log('Foo');\n`); - updateFile(`apps/${appName}/src/bar.ts`, `console.log('Bar');\n`); - - updateFile( - `apps/${appName}/webpack.config.js`, - ` - const path = require('path'); - const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); - - module.exports = { - target: 'node', - output: { - path: path.join(__dirname, '../../dist/apps/${appName}'), - // do not remove dist, so files between builds will remain - clean: false, - }, - plugins: [ - new NxAppWebpackPlugin({ - compiler: 'tsc', - main: './src/main.ts', - additionalEntryPoints: [ - './src/foo.ts', - { - entryName: 'bar', - entryPath: './src/bar.ts', - } - ], - tsConfig: './tsconfig.app.json', - outputHashing: 'none', - optimization: false, - }) - ] - };` - ); - - runCLI(`build ${appName}`); - - expect(runCommand(`node dist/apps/${appName}/main.js`)).toMatch(/Hello/); - expect(runCommand(`node dist/apps/${appName}/foo.js`)).toMatch(/Foo/); - expect(runCommand(`node dist/apps/${appName}/bar.js`)).toMatch(/Bar/); - - // Ensure dist is not removed between builds since output.clean === false - createFile(`dist/apps/${appName}/extra.js`); - runCLI(`build ${appName} --skip-nx-cache`); - checkFilesExist(`dist/apps/${appName}/extra.js`); - }, 500_000); - - it('should bundle in NX_PUBLIC_ environment variables', () => { - const appName = uniq('app'); - runCLI( - `generate @nx/web:app ${appName} --directory=apps/${appName} --bundler webpack` - ); - - checkFilesExist(`apps/${appName}/src/main.ts`); - updateFile( - `apps/${appName}/src/main.ts`, - ` - console.log(process.env['NX_PUBLIC_TEST']); - ` - ); - - runCLI(`build ${appName}`, { - env: { - NX_PUBLIC_TEST: 'foobar', - }, - }); - - const mainFile = listFiles(`dist/apps/${appName}`).filter((f) => - f.startsWith('main.') - ); - const content = readFile(`dist/apps/${appName}/${mainFile}`); - expect(content).toMatch(/foobar/); - }); - it('should support babel + core-js to polyfill JS features', async () => { const appName = uniq('app'); runCLI( @@ -246,139 +166,6 @@ describe('Webpack Plugin', () => { expect(readMainFile(`dist/apps/${appName}`)).not.toMatch(`await Promise`); }); - it('should allow options to be passed from the executor', async () => { - const appName = uniq('app'); - runCLI( - `generate @nx/web:app ${appName} --directory=apps/${appName} --bundler webpack` - ); - - checkFilesExist(`apps/${appName}/project.json`); - updateJson(`apps/${appName}/project.json`, (json) => { - json.targets.build = { - executor: '@nx/webpack:webpack', - outputs: ['{options.outputPath}'], - options: { - generatePackageJson: true, // This should be passed to the plugin. - outputPath: `dist/apps/${appName}`, - webpackConfig: `apps/${appName}/webpack.config.js`, - }, - }; - return json; - }); - - checkFilesExist(`apps/${appName}/webpack.config.js`); - updateFile( - `apps/${appName}/webpack.config.js`, - ` - const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); - const { join } = require('path'); - module.exports = { - output: { - path: join(__dirname, '../../dist/apps/demo'), - }, - plugins: [ - new NxAppWebpackPlugin({ - // NOTE: generatePackageJson is missing here, but executor passes it. - target: 'web', - compiler: 'swc', - main: './src/main.ts', - tsConfig: './tsconfig.app.json', - optimization: false, - outputHashing: 'none', - }), - ], - };` - ); - - runCLI(`build ${appName}`); - - fileExists(`dist/apps/${appName}/package.json`); - }); - - it('should resolve assets from executors as relative to workspace root', () => { - const appName = uniq('app'); - runCLI( - `generate @nx/web:app ${appName} --directory=apps/${appName} --bundler webpack` - ); - updateFile('shared/docs/TEST.md', 'TEST'); - updateJson(`apps/${appName}/project.json`, (json) => { - json.targets.build = { - executor: '@nx/webpack:webpack', - outputs: ['{options.outputPath}'], - options: { - assets: [ - { - input: 'shared/docs', - glob: 'TEST.md', - output: '.', - }, - ], - outputPath: `dist/apps/${appName}`, - webpackConfig: `apps/${appName}/webpack.config.js`, - }, - }; - return json; - }); - - runCLI(`build ${appName}`); - - checkFilesExist(`dist/apps/${appName}/TEST.md`); - }); - - it('it should support building libraries and apps when buildLibsFromSource is false', () => { - const appName = uniq('app'); - const myPkg = uniq('my-pkg'); - - runCLI( - `generate @nx/web:application ${appName} --directory=apps/${appName}` - ); - - runCLI( - `generate @nx/js:lib ${myPkg} --directory=libs/${myPkg} --importPath=@${appName}/${myPkg}` - ); - - updateFile(`libs/${myPkg}/src/index.ts`, `export const foo = 'bar';\n`); - - updateFile( - `apps/${appName}/src/main.ts`, - `import { foo } from '@${appName}/${myPkg}';\nconsole.log(foo);\n` - ); - - updateFile( - `apps/${appName}/webpack.config.js`, - ` - const path = require('path'); - const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); - - module.exports = { - target: 'node', - output: { - path: path.join(__dirname, '../../dist/${appName}') - }, - plugins: [ - new NxAppWebpackPlugin({ - compiler: 'tsc', - main: 'apps/${appName}/src/main.ts', - tsConfig: 'apps/${appName}/tsconfig.app.json', - outputHashing: 'none', - optimization: false, - buildLibsFromSource: false, - }) - ] - };` - ); - - const result = runCLI(`build ${appName}`); - - expect(result).toContain( - `Running target build for project ${appName} and 1 task it depends on` - ); - expect(result).toContain(`nx run ${myPkg}:build`); - expect(result).toContain( - `Successfully ran target build for project ${appName} and 1 task it depends on` - ); - }); - it('should be able to support webpack config with nx enhanced and babel', () => { const appName = uniq('app'); diff --git a/packages/react/src/generators/library/lib/install-common-dependencies.ts b/packages/react/src/generators/library/lib/install-common-dependencies.ts index bc342bb47ebfa..1b9ae18f1a066 100644 --- a/packages/react/src/generators/library/lib/install-common-dependencies.ts +++ b/packages/react/src/generators/library/lib/install-common-dependencies.ts @@ -11,6 +11,7 @@ import { babelPresetReactVersion, lessVersion, sassVersion, + testingLibraryDomVersion, testingLibraryReactVersion, tsLibVersion, typesNodeVersion, @@ -52,6 +53,7 @@ export async function installCommonDependencies( if (options.unitTestRunner && options.unitTestRunner !== 'none') { devDependencies['@testing-library/react'] = testingLibraryReactVersion; + devDependencies['@testing-library/dom'] = testingLibraryDomVersion; } const baseInstallTask = addDependenciesToPackageJson(