diff --git a/e2e/node/src/node-esbuild.test.ts b/e2e/node/src/node-esbuild.test.ts index e98d7e71df..9a31903859 100644 --- a/e2e/node/src/node-esbuild.test.ts +++ b/e2e/node/src/node-esbuild.test.ts @@ -6,20 +6,21 @@ import { promisifiedTreeKill, readFile, runCLI, - runCLIAsync, runCommandUntil, setMaxWorkers, - tmpProjPath, uniq, updateFile, } from '@nx/e2e/utils'; -import { execSync } from 'child_process'; import { join } from 'path'; describe('Node Applications + esbuild', () => { - beforeEach(() => newProject()); + beforeAll(() => + newProject({ + packages: ['@nx/node'], + }) + ); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should generate an app using esbuild', async () => { const app = uniq('nodeapp'); diff --git a/e2e/node/src/node-server.test.ts b/e2e/node/src/node-server.test.ts index c23a0b56ba..81b9ff2ebc 100644 --- a/e2e/node/src/node-server.test.ts +++ b/e2e/node/src/node-server.test.ts @@ -17,11 +17,13 @@ import { join } from 'path'; describe('Node Applications + webpack', () => { let proj: string; - beforeEach(() => { - proj = newProject(); + beforeAll(() => { + proj = newProject({ + packages: ['@nx/node'], + }); }); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); function addLibImport(appName: string, libName: string, importPath?: string) { const content = readFile(`apps/${appName}/src/main.ts`); diff --git a/e2e/node/src/node-webpack.test.ts b/e2e/node/src/node-webpack.test.ts index e05fb46cc8..faf12d7114 100644 --- a/e2e/node/src/node-webpack.test.ts +++ b/e2e/node/src/node-webpack.test.ts @@ -17,9 +17,13 @@ import { execSync } from 'child_process'; import { join } from 'path'; describe('Node Applications + webpack', () => { - beforeEach(() => newProject()); + beforeAll(() => + newProject({ + packages: ['@nx/node'], + }) + ); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should generate an app using webpack', async () => { const app = uniq('nodeapp'); diff --git a/e2e/node/src/node.test.ts b/e2e/node/src/node.test.ts index 8270aecf11..4a2d2baf45 100644 --- a/e2e/node/src/node.test.ts +++ b/e2e/node/src/node.test.ts @@ -50,9 +50,13 @@ function getData(port, path = '/api'): Promise { } describe('Node Applications', () => { - beforeEach(() => newProject()); + beforeAll(() => + newProject({ + packages: ['@nx/node', '@nx/express', '@nx/nest'], + }) + ); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should be able to generate an empty application', async () => { const nodeapp = uniq('nodeapp'); @@ -331,12 +335,16 @@ describe('Node Applications', () => { }); describe('Build Node apps', () => { - beforeEach(() => newProject()); + let scope: string; + beforeAll(() => { + scope = newProject({ + packages: ['@nx/node', '@nx/express', '@nx/nest'], + }); + }); - afterEach(() => cleanupProject()); + afterAll(() => cleanupProject()); it('should generate a package.json with the `--generatePackageJson` flag', async () => { - const scope = newProject(); const packageManager = detectPackageManager(tmpProjPath()); const nestapp = uniq('nestapp'); runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`); @@ -523,7 +531,6 @@ ${jslib}(); describe('NestJS', () => { it('should have plugin output if specified in `tsPlugins`', async () => { - newProject(); const nestapp = uniq('nestapp'); runCLI(`generate @nx/nest:app ${nestapp} --linter=eslint`); setMaxWorkers(join('apps', nestapp, 'project.json')); @@ -576,99 +583,95 @@ ${jslib}(); expect(mainJs).toContain('_OPENAPI_METADATA_FACTORY'); }, 300000); }); -}); -describe('nest libraries', function () { - beforeEach(() => newProject()); + describe('nest libraries', function () { + it('should be able to generate a nest library', async () => { + const nestlib = uniq('nestlib'); + runCLI(`generate @nx/nest:lib ${nestlib}`); - it('should be able to generate a nest library', async () => { - const nestlib = uniq('nestlib'); - runCLI(`generate @nx/nest:lib ${nestlib}`); + const lintResults = runCLI(`lint ${nestlib}`); + expect(lintResults).toContain('All files pass linting.'); - const lintResults = runCLI(`lint ${nestlib}`); - expect(lintResults).toContain('All files pass linting.'); + const testResults = runCLI(`test ${nestlib}`); + expect(testResults).toContain( + `Successfully ran target test for project ${nestlib}` + ); + }, 60000); - const testResults = runCLI(`test ${nestlib}`); - expect(testResults).toContain( - `Successfully ran target test for project ${nestlib}` - ); - }, 60000); + it('should be able to generate a nest library w/ service', async () => { + const nestlib = uniq('nestlib'); - it('should be able to generate a nest library w/ service', async () => { - const nestlib = uniq('nestlib'); + runCLI(`generate @nx/nest:lib ${nestlib} --service`); - runCLI(`generate @nx/nest:lib ${nestlib} --service`); + const lintResults = runCLI(`lint ${nestlib}`); + expect(lintResults).toContain('All files pass linting.'); - const lintResults = runCLI(`lint ${nestlib}`); - expect(lintResults).toContain('All files pass linting.'); + const jestResult = await runCLIAsync(`test ${nestlib}`); + expect(jestResult.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + }, 200000); - const jestResult = await runCLIAsync(`test ${nestlib}`); - expect(jestResult.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - }, 200000); + it('should be able to generate a nest library w/ controller', async () => { + const nestlib = uniq('nestlib'); - it('should be able to generate a nest library w/ controller', async () => { - const nestlib = uniq('nestlib'); + runCLI(`generate @nx/nest:lib ${nestlib} --controller`); - runCLI(`generate @nx/nest:lib ${nestlib} --controller`); + const lintResults = runCLI(`lint ${nestlib}`); + expect(lintResults).toContain('All files pass linting.'); - const lintResults = runCLI(`lint ${nestlib}`); - expect(lintResults).toContain('All files pass linting.'); + const jestResult = await runCLIAsync(`test ${nestlib}`); + expect(jestResult.combinedOutput).toContain( + 'Test Suites: 1 passed, 1 total' + ); + }, 200000); - const jestResult = await runCLIAsync(`test ${nestlib}`); - expect(jestResult.combinedOutput).toContain( - 'Test Suites: 1 passed, 1 total' - ); - }, 200000); + it('should be able to generate a nest library w/ controller and service', async () => { + const nestlib = uniq('nestlib'); - it('should be able to generate a nest library w/ controller and service', async () => { - const nestlib = uniq('nestlib'); + runCLI(`generate @nx/nest:lib ${nestlib} --controller --service`); - runCLI(`generate @nx/nest:lib ${nestlib} --controller --service`); + const lintResults = runCLI(`lint ${nestlib}`); + expect(lintResults).toContain('All files pass linting.'); - const lintResults = runCLI(`lint ${nestlib}`); - expect(lintResults).toContain('All files pass linting.'); + const jestResult = await runCLIAsync(`test ${nestlib}`); + expect(jestResult.combinedOutput).toContain( + 'Test Suites: 2 passed, 2 total' + ); + }, 200000); - const jestResult = await runCLIAsync(`test ${nestlib}`); - expect(jestResult.combinedOutput).toContain( - 'Test Suites: 2 passed, 2 total' - ); - }, 200000); + it('should have plugin output if specified in `transformers`', async () => { + const nestlib = uniq('nestlib'); + runCLI(`generate @nx/nest:lib ${nestlib} --buildable`); - it('should have plugin output if specified in `transformers`', async () => { - newProject(); - const nestlib = uniq('nestlib'); - runCLI(`generate @nx/nest:lib ${nestlib} --buildable`); + packageInstall('@nestjs/swagger', undefined, '^7.0.0'); - packageInstall('@nestjs/swagger', undefined, '^7.0.0'); - - updateJson(join('libs', nestlib, 'project.json'), (config) => { - config.targets.build.options.transformers = [ - { - name: '@nestjs/swagger/plugin', - options: { - dtoFileNameSuffix: ['.model.ts'], + updateJson(join('libs', nestlib, 'project.json'), (config) => { + config.targets.build.options.transformers = [ + { + name: '@nestjs/swagger/plugin', + options: { + dtoFileNameSuffix: ['.model.ts'], + }, }, - }, - ]; - return config; - }); + ]; + return config; + }); - updateFile( - `libs/${nestlib}/src/lib/foo.model.ts`, - ` + updateFile( + `libs/${nestlib}/src/lib/foo.model.ts`, + ` export class FooModel { foo?: string; bar?: number; }` - ); + ); - await runCLIAsync(`build ${nestlib}`); + await runCLIAsync(`build ${nestlib}`); - const fooModelJs = readFile(`dist/libs/${nestlib}/src/lib/foo.model.js`); - expect(stripIndents`${fooModelJs}`).toContain( - stripIndents` + const fooModelJs = readFile(`dist/libs/${nestlib}/src/lib/foo.model.js`); + expect(stripIndents`${fooModelJs}`).toContain( + stripIndents` "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FooModel = void 0; @@ -681,10 +684,11 @@ class FooModel { exports.FooModel = FooModel; //# sourceMappingURL=foo.model.js.map ` - ); - }, 300000); + ); + }, 300000); - it('should run default jest tests', async () => { - await expectJestTestsToPass('@nx/node:lib'); - }, 100000); + it('should run default jest tests', async () => { + await expectJestTestsToPass('@nx/node:lib'); + }, 100000); + }); }); diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index ba434e9fbd..446d4e0bb6 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -21,6 +21,7 @@ import { angularCliVersion as defaultAngularCliVersion } from '@nx/workspace/src import { dump } from '@zkochan/js-yaml'; import { execSync, ExecSyncOptions } from 'child_process'; +import { performance, PerformanceMeasure } from 'perf_hooks'; import { logError, logInfo } from './log-utils'; import { getPackageManagerCommand, @@ -28,13 +29,41 @@ import { RunCmdOpts, runCommand, } from './command-utils'; -import { NxJsonConfiguration, output } from '@nx/devkit'; +import { output } from '@nx/devkit'; import { readFileSync } from 'fs'; import { join } from 'path'; import { resetWorkspaceContext } from 'nx/src/utils/workspace-context'; let projName: string; +// TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages. +const nxPackages = [ + `@nx/angular`, + `@nx/eslint-plugin`, + `@nx/express`, + `@nx/esbuild`, + `@nx/jest`, + `@nx/js`, + `@nx/eslint`, + `@nx/nest`, + `@nx/next`, + `@nx/node`, + `@nx/nuxt`, + `@nx/plugin`, + `@nx/playwright`, + `@nx/rollup`, + `@nx/react`, + `@nx/storybook`, + `@nx/vue`, + `@nx/vite`, + `@nx/web`, + `@nx/webpack`, + `@nx/react-native`, + `@nx/expo`, +] as const; + +type NxPackage = typeof nxPackages[number]; + /** * Sets up a new project in the temporary project path * for the currently selected CLI. @@ -43,15 +72,34 @@ export function newProject({ name = uniq('proj'), packageManager = getSelectedPackageManager(), unsetProjectNameAndRootFormat = true, + packages, +}: { + name?: string; + packageManager?: 'npm' | 'yarn' | 'pnpm'; + unsetProjectNameAndRootFormat?: boolean; + readonly packages?: Array; } = {}): string { + const newProjectStart = performance.mark('new-project:start'); try { const projScope = 'proj'; + let createNxWorkspaceMeasure: PerformanceMeasure; + let packageInstallMeasure: PerformanceMeasure; + if (!directoryExists(tmpBackupProjPath())) { + const createNxWorkspaceStart = performance.mark( + 'create-nx-workspace:start' + ); runCreateWorkspace(projScope, { preset: 'apps', packageManager, }); + const createNxWorkspaceEnd = performance.mark('create-nx-workspace:end'); + createNxWorkspaceMeasure = performance.measure( + 'create-nx-workspace', + createNxWorkspaceStart.name, + createNxWorkspaceEnd.name + ); if (unsetProjectNameAndRootFormat) { console.warn( @@ -69,33 +117,19 @@ export function newProject({ ); } - // TODO(jack): we should tag the projects (e.g. tags: ['package']) and filter from that rather than hard-code packages. - const packages = [ - `@nx/angular`, - `@nx/eslint-plugin`, - `@nx/express`, - `@nx/esbuild`, - `@nx/jest`, - `@nx/js`, - `@nx/eslint`, - `@nx/nest`, - `@nx/next`, - `@nx/node`, - `@nx/nuxt`, - `@nx/plugin`, - `@nx/playwright`, - `@nx/rollup`, - `@nx/react`, - `@nx/storybook`, - `@nx/vue`, - `@nx/vite`, - `@nx/web`, - `@nx/webpack`, - `@nx/react-native`, - `@nx/expo`, - ]; - packageInstall(packages.join(` `), projScope); - + if (!packages) { + console.warn( + 'ATTENTION: All packages are installed into the new workspace. To make this test faster, please pass the subset of packages that this test needs by passing a packages array in the options' + ); + } + const packageInstallStart = performance.mark('packageInstall:start'); + packageInstall((packages ?? nxPackages).join(` `), projScope); + const packageInstallEnd = performance.mark('packageInstall:end'); + packageInstallMeasure = performance.measure( + 'packageInstall', + packageInstallStart.name, + packageInstallEnd.name + ); // stop the daemon execSync(`${getPackageManagerCommand().runNx} reset`, { cwd: `${e2eCwd}/proj`, @@ -105,19 +139,55 @@ export function newProject({ moveSync(`${e2eCwd}/proj`, `${tmpBackupProjPath()}`); } projName = name; - copySync(`${tmpBackupProjPath()}`, `${tmpProjPath()}`); - if (isVerbose()) { - logInfo(`NX`, `E2E created a project: ${tmpProjPath()}`); - } + const projectDirectory = tmpProjPath(); + copySync(`${tmpBackupProjPath()}`, `${projectDirectory}`); + + // TODO: What is this for? if (packageManager === 'pnpm') { execSync(getPackageManagerCommand().install, { - cwd: tmpProjPath(), + cwd: projectDirectory, stdio: 'pipe', env: { CI: 'true', ...process.env }, encoding: 'utf-8', }); } + + const newProjectEnd = performance.mark('new-project:end'); + const perfMeasure = performance.measure( + 'newProject', + newProjectStart.name, + newProjectEnd.name + ); + + if (isVerbose()) { + logInfo( + `NX`, + `E2E created a project: ${projectDirectory} in ${ + perfMeasure.duration / 1000 + } seconds +${ + createNxWorkspaceMeasure + ? `create-nx-workspace: ${ + createNxWorkspaceMeasure.duration / 1000 + } seconds\n` + : '' +}${ + packageInstallMeasure + ? `packageInstall: ${ + packageInstallMeasure.duration / 1000 + } seconds\n` + : '' + }` + ); + } + + if (process.env.NX_E2E_EDITOR) { + const editor = process.env.NX_E2E_EDITOR; + execSync(`${editor} ${projectDirectory}`, { + stdio: 'inherit', + }); + } return projScope; } catch (e) { logError(`Failed to set up project for e2e tests.`, e.message);