diff --git a/e2e/angular-core/src/module-federation.test.ts b/e2e/angular-core/src/module-federation.test.ts index 4812ba9667..b9765f8ba1 100644 --- a/e2e/angular-core/src/module-federation.test.ts +++ b/e2e/angular-core/src/module-federation.test.ts @@ -1,5 +1,6 @@ import { cleanupProject, + killPort, newProject, promisifiedTreeKill, readProjectConfig, @@ -15,9 +16,17 @@ import { names } from '@nrwl/devkit'; describe('Angular Projects', () => { let proj: string; + let oldValue; - beforeAll(() => (proj = newProject())); - afterAll(() => cleanupProject()); + beforeAll(() => { + proj = newProject(); + oldValue = process.env.NX_E2E_VERBOSE_LOGGING; + process.env.NX_E2E_VERBOSE_LOGGING = 'true'; + }); + afterAll(() => { + cleanupProject(); + process.env.NX_E2E_VERBOSE_LOGGING = oldValue; + }); it('should serve the host and remote apps successfully, even with a shared library with a secondary entry point between them', async () => { // ACT + ASSERT @@ -60,7 +69,7 @@ describe('Angular Projects', () => { import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; import { RouterModule } from '@angular/router'; - + @NgModule({ declarations: [AppComponent, NxWelcomeComponent], imports: [ @@ -95,7 +104,7 @@ describe('Angular Projects', () => { names(secondaryEntry).className }Module } from '@${proj}/${secondaryEntry}'; import { RemoteEntryComponent } from './entry.component'; - + @NgModule({ declarations: [RemoteEntryComponent], imports: [ @@ -135,6 +144,8 @@ describe('Angular Projects', () => { if (process && process.pid) { await promisifiedTreeKill(process.pid, 'SIGKILL'); } + await killPort(port1); + await killPort(port2); } catch (err) { expect(err).toBeFalsy(); } @@ -168,21 +179,27 @@ describe('Angular Projects', () => { `generate @nrwl/angular:remote ${remoteApp1} --ssr --no-interactive` ); - let process = await runCommandUntil(`serve-ssr ${remoteApp1}`, (output) => { - return ( - output.includes(`Browser application bundle generation complete.`) && - output.includes(`Server application bundle generation complete.`) && - output.includes( - `Angular Universal Live Development Server is listening` - ) - ); - }); + const port = 4301; + + let process = await runCommandUntil( + `serve-ssr ${remoteApp1} --port=${port}`, + (output) => { + return ( + output.includes(`Browser application bundle generation complete.`) && + output.includes(`Server application bundle generation complete.`) && + output.includes( + `Angular Universal Live Development Server is listening` + ) + ); + } + ); // port and process cleanup try { if (process && process.pid) { await promisifiedTreeKill(process.pid, 'SIGKILL'); } + await killPort(port); } catch (err) { expect(err).toBeFalsy(); } @@ -204,25 +221,33 @@ describe('Angular Projects', () => { const remoteApp2Port = readProjectConfig(remoteApp2).targets.serve.options.port; - let process = await runCommandUntil(`serve-ssr ${hostApp}`, (output) => { - return ( - output.includes( - `Node Express server listening on http://localhost:${remoteApp1Port}` - ) && - output.includes( - `Node Express server listening on http://localhost:${remoteApp2Port}` - ) && - output.includes( - `Angular Universal Live Development Server is listening` - ) - ); - }); + const port = 4401; + + let process = await runCommandUntil( + `serve-ssr ${hostApp} --port=${port}`, + (output) => { + return ( + output.includes( + `Node Express server listening on http://localhost:${remoteApp1Port}` + ) && + output.includes( + `Node Express server listening on http://localhost:${remoteApp2Port}` + ) && + output.includes( + `Angular Universal Live Development Server is listening` + ) + ); + } + ); // port and process cleanup try { if (process && process.pid) { await promisifiedTreeKill(process.pid, 'SIGKILL'); } + await killPort(port); + await killPort(remoteApp1Port); + await killPort(remoteApp2Port); } catch (err) { expect(err).toBeFalsy(); } @@ -240,18 +265,24 @@ describe('Angular Projects', () => { return project; }); + const port = 4501; + // ACT - let process = await runCommandUntil(`serve-ssr ${ssrApp}`, (output) => { - return output.includes( - `Angular Universal Live Development Server is listening on http://localhost:4200` - ); - }); + let process = await runCommandUntil( + `serve-ssr ${ssrApp} --port=${port}`, + (output) => { + return output.includes( + `Angular Universal Live Development Server is listening on http://localhost:${port}` + ); + } + ); // port and process cleanup try { if (process && process.pid) { await promisifiedTreeKill(process.pid, 'SIGKILL'); } + await killPort(port); } catch (err) { expect(err).toBeFalsy(); } diff --git a/e2e/utils/command-utils.ts b/e2e/utils/command-utils.ts index 23bafa854e..7490867807 100644 --- a/e2e/utils/command-utils.ts +++ b/e2e/utils/command-utils.ts @@ -11,13 +11,10 @@ import { import { TargetConfiguration } from '@nrwl/devkit'; import { ChildProcess, exec, execSync, ExecSyncOptions } from 'child_process'; import { join } from 'path'; -import { check as portCheck } from 'tcp-port-used'; import * as isCI from 'is-ci'; import { Workspaces } from '../../packages/nx/src/config/workspaces'; import { updateFile } from './file-utils'; -import { logError, logInfo, logSuccess, stripConsoleColors } from './log-utils'; - -export const kill = require('kill-port'); +import { logError, stripConsoleColors } from './log-utils'; export interface RunCmdOpts { silenceError?: boolean; @@ -243,6 +240,15 @@ export function runCommandUntil( p.stderr?.on('data', checkCriteria); p.on('exit', (code) => { if (!complete) { + if (isVerboseE2ERun()) { + logError( + `Original output:`, + output + .split('\n') + .map((l) => ` ${l}`) + .join('\n') + ); + } rej(`Exited with ${code}`); } else { res(p); @@ -398,37 +404,6 @@ export function runLernaCLI( } } -const KILL_PORT_DELAY = 5000; - -export async function killPort(port: number): Promise { - if (await portCheck(port)) { - try { - logInfo(`Attempting to close port ${port}`); - await kill(port); - await new Promise((resolve) => - setTimeout(() => resolve(), KILL_PORT_DELAY) - ); - if (await portCheck(port)) { - logError(`Port ${port} still open`); - } else { - logSuccess(`Port ${port} successfully closed`); - return true; - } - } catch { - logError(`Port ${port} closing failed`); - } - return false; - } else { - return true; - } -} - -export async function killPorts(port?: number): Promise { - return port - ? await killPort(port) - : (await killPort(3333)) && (await killPort(4200)); -} - export function waitUntil( predicate: () => boolean, opts: { timeout: number; ms: number; allowError?: boolean } = { diff --git a/e2e/utils/create-project-utils.ts b/e2e/utils/create-project-utils.ts index 2ce531e058..ab3ef71149 100644 --- a/e2e/utils/create-project-utils.ts +++ b/e2e/utils/create-project-utils.ts @@ -81,7 +81,7 @@ export function newProject({ packageInstall(packages.join(` `), projScope); // stop the daemon - execSync('nx reset', { + execSync(`${getPackageManagerCommand().runNx} reset`, { cwd: `${e2eCwd}/proj`, stdio: isVerbose() ? 'inherit' : 'pipe', }); @@ -94,6 +94,14 @@ export function newProject({ if (isVerbose()) { logInfo(`NX`, `E2E created a project: ${tmpProjPath()}`); } + if (packageManager === 'pnpm') { + execSync(getPackageManagerCommand().install, { + cwd: tmpProjPath(), + stdio: 'pipe', + env: { CI: 'true', ...process.env }, + encoding: 'utf-8', + }); + } return projScope; } catch (e) { logError(`Failed to set up project for e2e tests.`, e.message); @@ -101,6 +109,14 @@ export function newProject({ } } +// pnpm v7 sadly doesn't automatically install peer dependencies +export function addPnpmRc() { + updateFile( + '.npmrc', + 'strict-peer-dependencies=false\nauto-install-peers=true' + ); +} + export function runCreateWorkspace( name: string, { diff --git a/e2e/utils/file-utils.ts b/e2e/utils/file-utils.ts index 9fb865a1a5..9110a2abbd 100644 --- a/e2e/utils/file-utils.ts +++ b/e2e/utils/file-utils.ts @@ -12,8 +12,6 @@ import { import * as path from 'path'; import { e2eCwd } from './get-env-info'; import { tmpProjPath } from './create-project-utils'; -import { promisify } from 'util'; -import * as treeKill from 'tree-kill'; export function createFile(f: string, content: string = ''): void { const path = tmpProjPath(f); @@ -121,8 +119,3 @@ export function getSize(filePath: string): number { export function tmpBackupProjPath(path?: string) { return path ? `${e2eCwd}/proj-backup/${path}` : `${e2eCwd}/proj-backup`; } - -export const promisifiedTreeKill: ( - pid: number, - signal: string -) => Promise = promisify(treeKill); diff --git a/e2e/utils/index.ts b/e2e/utils/index.ts index c6ad7bd7d9..07f20e19af 100644 --- a/e2e/utils/index.ts +++ b/e2e/utils/index.ts @@ -10,3 +10,4 @@ export * from './get-env-info'; export * from './log-utils'; export * from './project-config-utils'; export * from './test-utils'; +export * from './process-utils'; diff --git a/e2e/utils/process-utils.ts b/e2e/utils/process-utils.ts new file mode 100644 index 0000000000..672d9d4598 --- /dev/null +++ b/e2e/utils/process-utils.ts @@ -0,0 +1,41 @@ +import { promisify } from 'util'; +import * as treeKill from 'tree-kill'; +import { logError, logInfo, logSuccess } from './log-utils'; +import { check as portCheck } from 'tcp-port-used'; + +export const kill = require('kill-port'); +const KILL_PORT_DELAY = 5000; + +export const promisifiedTreeKill: ( + pid: number, + signal: string +) => Promise = promisify(treeKill); + +export async function killPort(port: number): Promise { + if (await portCheck(port)) { + try { + logInfo(`Attempting to close port ${port}`); + await kill(port); + await new Promise((resolve) => + setTimeout(() => resolve(), KILL_PORT_DELAY) + ); + if (await portCheck(port)) { + logError(`Port ${port} still open`); + } else { + logSuccess(`Port ${port} successfully closed`); + return true; + } + } catch { + logError(`Port ${port} closing failed`); + } + return false; + } else { + return true; + } +} + +export async function killPorts(port?: number): Promise { + return port + ? await killPort(port) + : (await killPort(3333)) && (await killPort(4200)); +} diff --git a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap index a545cae8a6..c210947773 100644 --- a/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap +++ b/packages/angular/src/generators/host/__snapshots__/host.spec.ts.snap @@ -124,7 +124,8 @@ function run(): void { run(); -export * from './bootstrap.server';" +export * from './bootstrap.server'; +" `; exports[`Host App Generator --ssr should generate the correct files 5`] = `"import('./src/main.server');"`; diff --git a/packages/angular/src/generators/host/files/src/main.server.ts__tmpl__ b/packages/angular/src/generators/host/files/src/main.server.ts__tmpl__ index 909e5eee4a..1716bd22eb 100644 --- a/packages/angular/src/generators/host/files/src/main.server.ts__tmpl__ +++ b/packages/angular/src/generators/host/files/src/main.server.ts__tmpl__ @@ -63,4 +63,4 @@ function run(): void { run(); -export * from './bootstrap.server'; \ No newline at end of file +export * from './bootstrap.server'; diff --git a/packages/angular/src/generators/host/lib/add-ssr.ts b/packages/angular/src/generators/host/lib/add-ssr.ts index 26ac16904d..3b38f19463 100644 --- a/packages/angular/src/generators/host/lib/add-ssr.ts +++ b/packages/angular/src/generators/host/lib/add-ssr.ts @@ -13,6 +13,8 @@ import { corsVersion, expressVersion, moduleFederationNodeVersion, + typesCorsVersion, + typesExpressVersion, } from '../../../utils/versions'; export async function addSsr(tree: Tree, options: Schema, appName: string) { @@ -65,7 +67,10 @@ export async function addSsr(tree: Tree, options: Schema, appName: string) { express: expressVersion, '@module-federation/node': moduleFederationNodeVersion, }, - {} + { + '@types/cors': typesCorsVersion, + '@types/express': typesExpressVersion, + } ); return installTask; diff --git a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap index d20f4e3ead..457190479d 100644 --- a/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap +++ b/packages/angular/src/generators/remote/__snapshots__/remote.spec.ts.snap @@ -120,7 +120,8 @@ function run(): void { run(); -export * from './bootstrap.server';" +export * from './bootstrap.server'; +" `; exports[`MF Remote App Generator --ssr should generate the correct files 5`] = `"import('./src/main.server');"`; diff --git a/packages/angular/src/generators/remote/files/src/main.server.ts__tmpl__ b/packages/angular/src/generators/remote/files/src/main.server.ts__tmpl__ index 6204abf96a..e5a6b3ae63 100644 --- a/packages/angular/src/generators/remote/files/src/main.server.ts__tmpl__ +++ b/packages/angular/src/generators/remote/files/src/main.server.ts__tmpl__ @@ -71,4 +71,4 @@ function run(): void { run(); -export * from './bootstrap.server'; \ No newline at end of file +export * from './bootstrap.server'; diff --git a/packages/angular/src/generators/remote/lib/add-ssr.ts b/packages/angular/src/generators/remote/lib/add-ssr.ts index f5ac1528fe..8a6da2b07e 100644 --- a/packages/angular/src/generators/remote/lib/add-ssr.ts +++ b/packages/angular/src/generators/remote/lib/add-ssr.ts @@ -12,6 +12,8 @@ import { corsVersion, expressVersion, moduleFederationNodeVersion, + typesCorsVersion, + typesExpressVersion, } from '../../../utils/versions'; export async function addSsr( @@ -70,7 +72,10 @@ export async function addSsr( express: expressVersion, '@module-federation/node': moduleFederationNodeVersion, }, - {} + { + '@types/cors': typesCorsVersion, + '@types/express': typesExpressVersion, + } ); return installTask; diff --git a/packages/angular/src/utils/backward-compatible-versions.ts b/packages/angular/src/utils/backward-compatible-versions.ts index ef0d2c10c0..556c4844d7 100644 --- a/packages/angular/src/utils/backward-compatible-versions.ts +++ b/packages/angular/src/utils/backward-compatible-versions.ts @@ -22,7 +22,9 @@ export const backwardCompatibleVersions: Record< tsLibVersion: '^2.3.0', ngUniversalVersion: '~14.2.0', corsVersion: '~2.8.5', + typesCorsVersion: '~2.8.5', expressVersion: '~4.18.2', + typesExpressVersion: '4.17.14', moduleFederationNodeVersion: '^0.10.1', angularEslintVersion: '~14.0.4', tailwindVersion: '^3.0.2', diff --git a/packages/angular/src/utils/versions.ts b/packages/angular/src/utils/versions.ts index 5d588f380f..19b59b63d1 100644 --- a/packages/angular/src/utils/versions.ts +++ b/packages/angular/src/utils/versions.ts @@ -11,7 +11,9 @@ export const tsLibVersion = '^2.3.0'; export const ngUniversalVersion = '~15.1.0'; export const corsVersion = '~2.8.5'; +export const typesCorsVersion = '~2.8.5'; export const expressVersion = '~4.18.2'; +export const typesExpressVersion = '4.17.14'; export const moduleFederationNodeVersion = '~0.10.1'; export const angularEslintVersion = '~15.0.0';