import { cleanupProject, killPort, newProject, promisifiedTreeKill, readProjectConfig, runCLI, runCommandUntil, uniq, updateFile, updateProjectConfig, } from '@nrwl/e2e/utils'; import { ChildProcess } from 'child_process'; import { names } from '@nrwl/devkit'; describe('Angular Projects', () => { let proj: string; let oldValue; 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 const port1 = 4200; const port2 = 4206; const hostApp = uniq('app'); const remoteApp1 = uniq('remote'); const sharedLib = uniq('shared-lib'); const secondaryEntry = uniq('secondary'); // generate host app runCLI( `generate @nrwl/angular:host ${hostApp} --style=css --no-interactive` ); // generate remote apps runCLI( `generate @nrwl/angular:remote ${remoteApp1} --host=${hostApp} --port=${port2} --style=css --no-interactive` ); // generate a shared lib runCLI( `generate @nrwl/angular:library ${sharedLib} --buildable --no-interactive` ); runCLI( `generate @nrwl/angular:library-secondary-entry-point --library=${sharedLib} --name=${secondaryEntry} --no-interactive` ); // update the files to use shared library updateFile( `apps/${hostApp}/src/app/app.module.ts`, `import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ${ names(sharedLib).className }Module } from '@${proj}/${sharedLib}'; import { ${ names(secondaryEntry).className }Module } from '@${proj}/${secondaryEntry}'; import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; import { RouterModule } from '@angular/router'; @NgModule({ declarations: [AppComponent, NxWelcomeComponent], imports: [ BrowserModule, SharedModule, RouterModule.forRoot( [ { path: '${remoteApp1}', loadChildren: () => import('${remoteApp1}/Module').then( (m) => m.RemoteEntryModule ), }, ], { initialNavigation: 'enabledBlocking' } ), ], providers: [], bootstrap: [AppComponent], }) export class AppModule {} ` ); updateFile( `apps/${remoteApp1}/src/app/remote-entry/entry.module.ts`, `import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { ${names(sharedLib).className}Module } from '@${proj}/${sharedLib}'; import { ${ names(secondaryEntry).className }Module } from '@${proj}/${secondaryEntry}'; import { RemoteEntryComponent } from './entry.component'; @NgModule({ declarations: [RemoteEntryComponent], imports: [ CommonModule, SharedModule, RouterModule.forChild([ { path: '', component: RemoteEntryComponent, }, ]), ], providers: [], }) export class RemoteEntryModule {} ` ); let process: ChildProcess; try { process = await runCommandUntil( `serve ${hostApp} --dev-remotes=${remoteApp1}`, (output) => { return ( output.includes(`listening on localhost:${port2}`) && output.includes(`listening on localhost:${port1}`) ); } ); } catch (err) { console.error(err); } // port and process cleanup try { if (process && process.pid) { await promisifiedTreeKill(process.pid, 'SIGKILL'); } await killPort(port1); await killPort(port2); } catch (err) { expect(err).toBeFalsy(); } }, 300000); it('should build the host app successfully', async () => { // ARRANGE const hostApp = uniq('app'); const remoteApp1 = uniq('remote'); // generate host app runCLI(`generate @nrwl/angular:host ${hostApp} --no-interactive`); // generate remote apps runCLI( `generate @nrwl/angular:remote ${remoteApp1} --host=${hostApp} --no-interactive` ); // ACT const buildOutput = runCLI(`build ${hostApp}`); // ASSERT expect(buildOutput).toContain('Successfully ran target build'); }, 300000); it('should serve a ssr remote app successfully', async () => { // ARRANGE const remoteApp1 = uniq('remote'); // generate remote apps runCLI( `generate @nrwl/angular:remote ${remoteApp1} --ssr --no-interactive` ); 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(); } }, 10_000_000); it('should scaffold a ssr MF setup successfully', async () => { // ARRANGE const remoteApp1 = uniq('remote1'); const remoteApp2 = uniq('remote2'); const hostApp = uniq('host1'); // generate remote apps runCLI( `generate @nrwl/angular:host ${hostApp} --ssr --remotes=${remoteApp1},${remoteApp2} --no-interactive` ); // ports const remoteApp1Port = readProjectConfig(remoteApp1).targets.serve.options.port; const remoteApp2Port = readProjectConfig(remoteApp2).targets.serve.options.port; 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(); } }, 20_000_000); it('Custom Webpack Config for SSR - should serve the app correctly', async () => { // ARRANGE const ssrApp = uniq('app'); runCLI(`generate @nrwl/angular:app ${ssrApp} --no-interactive`); runCLI(`generate @nrwl/angular:setup-ssr ${ssrApp} --no-interactive`); updateProjectConfig(ssrApp, (project) => { project.targets.server.executor = '@nrwl/angular:webpack-server'; return project; }); const port = 4501; // ACT 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(); } }, 300000); });