import { checkFilesExist, getSize, killPorts, newProject, cleanupProject, runCLI, tmpProjPath, uniq, updateFile, runCypressTests, updateProjectConfig, readFile, runCommandUntil, promisifiedTreeKill, } from '@nrwl/e2e/utils'; import { ChildProcess } from 'child_process'; import { names } from '@nrwl/devkit'; describe('Angular Projects', () => { let proj: string; beforeAll(() => (proj = newProject())); afterAll(() => cleanupProject()); it('should generate an app, a lib, link them, build, serve and test both correctly', async () => { const myapp = uniq('myapp'); const myapp2 = uniq('myapp2'); const mylib = uniq('mylib'); runCLI( `generate @nrwl/angular:app ${myapp} --directory=myDir --no-interactive` ); runCLI( `generate @nrwl/angular:app ${myapp2} --directory=myDir --no-interactive` ); runCLI( `generate @nrwl/angular:lib ${mylib} --directory=myDir --add-module-spec --no-interactive` ); updateFile( `apps/my-dir/${myapp}/src/app/app.module.ts`, ` import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { MyDir${ names(mylib).className }Module } from '@${proj}/my-dir/${mylib}'; import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; @NgModule({ imports: [BrowserModule, MyDir${names(mylib).className}Module], declarations: [AppComponent, NxWelcomeComponent], bootstrap: [AppComponent] }) export class AppModule {} ` ); runCLI( `run-many --target build --projects=my-dir-${myapp},my-dir-${myapp2} --parallel --prod --output-hashing none` ); checkFilesExist(`dist/apps/my-dir/${myapp}/main.js`); // This is a loose requirement because there are a lot of // influences external from this project that affect this. const es2015BundleSize = getSize( tmpProjPath(`dist/apps/my-dir/${myapp}/main.js`) ); console.log( `The current es2015 bundle size is ${es2015BundleSize / 1000} KB` ); expect(es2015BundleSize).toBeLessThanOrEqual(160000); runCLI( `run-many --target test --projects=my-dir-${myapp},my-dir-${mylib} --parallel` ); if (runCypressTests()) { const e2eResults = runCLI(`e2e my-dir-${myapp}-e2e --no-watch`); expect(e2eResults).toContain('All specs passed!'); expect(await killPorts()).toBeTruthy(); } const process = await runCommandUntil( `serve my-dir-${myapp} -- --port=4207`, (output) => output.includes(`listening on localhost:4207`) ); // port and process cleanup try { await promisifiedTreeKill(process.pid, 'SIGKILL'); await killPorts(4207); } catch (err) { expect(err).toBeFalsy(); } }, 1000000); it('should build the dependent buildable lib and its child lib, as well as the app', () => { // ARRANGE const app = uniq('app'); const buildableLib = uniq('buildlib1'); const buildableChildLib = uniq('buildlib2'); runCLI(`generate @nrwl/angular:app ${app} --style=css --no-interactive`); runCLI( `generate @nrwl/angular:library ${buildableLib} --buildable=true --no-interactive` ); runCLI( `generate @nrwl/angular:library ${buildableChildLib} --buildable=true --no-interactive` ); // update the app module to include a ref to the buildable lib updateFile( `apps/${app}/src/app/app.module.ts`, ` import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import {${ names(buildableLib).className }Module} from '@${proj}/${buildableLib}'; import { AppComponent } from './app.component'; import { NxWelcomeComponent } from './nx-welcome.component'; @NgModule({ declarations: [AppComponent, NxWelcomeComponent], imports: [BrowserModule, ${names(buildableLib).className}Module], providers: [], bootstrap: [AppComponent], }) export class AppModule {} ` ); // update the buildable lib module to include a ref to the buildable child lib updateFile( `libs/${buildableLib}/src/lib/${names(buildableLib).fileName}.module.ts`, ` import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ${ names(buildableChildLib).className }Module } from '@${proj}/${buildableChildLib}'; @NgModule({ imports: [CommonModule, ${names(buildableChildLib).className}Module], }) export class ${names(buildableLib).className}Module {} ` ); // update the angular.json updateProjectConfig(app, (config) => { config.targets.build.executor = '@nrwl/angular:webpack-browser'; config.targets.build.options = { ...config.targets.build.options, buildLibsFromSource: false, }; return config; }); // ACT const libOutput = runCLI(`build ${app} --configuration=development`); // ASSERT expect(libOutput).toContain( `Building entry point '@${proj}/${buildableLib}'` ); expect(libOutput).toContain(`nx run ${app}:build:development`); // to proof it has been built from source the "main.js" should actually contain // the path to dist const mainBundle = readFile(`dist/apps/${app}/main.js`); expect(mainBundle).toContain(`dist/libs/${buildableLib}`); }); it('should build publishable libs successfully', () => { // ARRANGE const lib = uniq('lib'); const childLib = uniq('child'); const entryPoint = uniq('entrypoint'); runCLI( `generate @nrwl/angular:lib ${lib} --publishable --importPath=@${proj}/${lib} --no-interactive` ); runCLI( `generate @nrwl/angular:secondary-entry-point --name=${entryPoint} --library=${lib} --no-interactive` ); runCLI( `generate @nrwl/angular:library ${childLib} --publishable=true --importPath=@${proj}/${childLib} --no-interactive` ); // create secondary entrypoint updateFile( `libs/${childLib}/sub/package.json`, ` { "ngPackage": {} } ` ); updateFile( `libs/${childLib}/sub/src/lib/sub.module.ts`, ` import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; @NgModule({ imports: [CommonModule] }) export class SubModule {} ` ); updateFile( `libs/${childLib}/sub/src/public_api.ts`, `export * from './lib/sub.module';` ); updateFile( `libs/${childLib}/sub/src/index.ts`, `export * from './public_api';` ); updateFile(`tsconfig.base.json`, (s) => { return s.replace( `"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"],`, `"@${proj}/${childLib}": ["libs/${childLib}/src/index.ts"], "@${proj}/${childLib}/sub": ["libs/${childLib}/sub/src/index.ts"], ` ); }); const moduleContent = ` import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ${ names(childLib).className }Module } from '@${proj}/${childLib}'; import { SubModule } from '@${proj}/${childLib}/sub'; @NgModule({ imports: [CommonModule, ${names(childLib).className}Module, SubModule] }) export class ${names(lib).className}Module {}`; updateFile(`libs/${lib}/src/lib/${lib}.module.ts`, moduleContent); // ACT const buildOutput = runCLI(`build ${lib}`); // ASSERT expect(buildOutput).toContain(`Building entry point '@${proj}/${lib}'`); expect(buildOutput).toContain( `Building entry point '@${proj}/${lib}/${entryPoint}'` ); expect(buildOutput).toContain('Successfully ran target build'); }); it('MFE - should serve the host and remote apps successfully', async () => { // ACT + ASSERT const port1 = 4205; const port2 = 4206; const hostApp = uniq('app'); const remoteApp1 = uniq('remote'); // generate host app runCLI( `generate @nrwl/angular:app ${hostApp} -- --mfe --mfeType=host --port=${port1} --routing --style=css --no-interactive` ); // generate remote apps runCLI( `generate @nrwl/angular:app ${remoteApp1} -- --mfe --mfeType=remote --host=${hostApp} --port=${port2} --routing --style=css --no-interactive` ); let process: ChildProcess; try { process = await runCommandUntil(`serve-mfe ${hostApp}`, (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 killPorts(port1); await killPorts(port2); } catch (err) { expect(err).toBeFalsy(); } }, 300000); it('MFE - should build the host app successfully', async () => { // ARRANGE const port1 = 4205; const port2 = 4206; const hostApp = uniq('app'); const remoteApp1 = uniq('remote'); // generate host app runCLI( `generate @nrwl/angular:host ${hostApp} -- --port=${port1} --no-interactive` ); // generate remote apps runCLI( `generate @nrwl/angular:remote ${remoteApp1} -- --host=${hostApp} --port=${port2} --no-interactive` ); // ACT const buildOutput = runCLI(`build ${hostApp}`); // ASSERT expect(buildOutput).toContain('Successfully ran target build'); }, 300000); });