|
| 1 | +import { names } from '@nx/devkit'; |
| 2 | +import { |
| 3 | + checkFilesExist, |
| 4 | + cleanupProject, |
| 5 | + killPorts, |
| 6 | + killProcessAndPorts, |
| 7 | + newProject, |
| 8 | + readJson, |
| 9 | + runCLI, |
| 10 | + runCommandUntil, |
| 11 | + runE2ETests, |
| 12 | + uniq, |
| 13 | + updateFile, |
| 14 | + updateJson, |
| 15 | +} from '@nx/e2e/utils'; |
| 16 | +import { join } from 'path'; |
| 17 | + |
| 18 | +describe('Angular Module Federation', () => { |
| 19 | + let proj: string; |
| 20 | + let oldVerboseLoggingValue: string; |
| 21 | + |
| 22 | + beforeAll(() => { |
| 23 | + proj = newProject({ packages: ['@nx/angular'] }); |
| 24 | + oldVerboseLoggingValue = process.env.NX_E2E_VERBOSE_LOGGING; |
| 25 | + process.env.NX_E2E_VERBOSE_LOGGING = 'true'; |
| 26 | + }); |
| 27 | + afterAll(() => { |
| 28 | + cleanupProject(); |
| 29 | + process.env.NX_E2E_VERBOSE_LOGGING = oldVerboseLoggingValue; |
| 30 | + }); |
| 31 | + |
| 32 | + it('should generate valid host and remote apps', async () => { |
| 33 | + const hostApp = uniq('app'); |
| 34 | + const remoteApp1 = uniq('remote'); |
| 35 | + const sharedLib = uniq('shared-lib'); |
| 36 | + const wildcardLib = uniq('wildcard-lib'); |
| 37 | + const secondaryEntry = uniq('secondary'); |
| 38 | + const hostPort = 4300; |
| 39 | + const remotePort = 4301; |
| 40 | + |
| 41 | + // generate host app |
| 42 | + runCLI( |
| 43 | + `generate @nx/angular:host ${hostApp} --style=css --bundler=rspack --no-standalone --no-interactive` |
| 44 | + ); |
| 45 | + // generate remote app |
| 46 | + runCLI( |
| 47 | + `generate @nx/angular:remote ${remoteApp1} --host=${hostApp} --bundler=rspack --port=${remotePort} --style=css --no-standalone --no-interactive` |
| 48 | + ); |
| 49 | + |
| 50 | + // check files are generated without the layout directory ("apps/") |
| 51 | + checkFilesExist( |
| 52 | + `${hostApp}/src/app/app.module.ts`, |
| 53 | + `${remoteApp1}/src/app/app.module.ts` |
| 54 | + ); |
| 55 | + |
| 56 | + // check default generated host is built successfully |
| 57 | + const buildOutput = runCLI(`build ${hostApp}`); |
| 58 | + expect(buildOutput).toContain('Successfully ran target build'); |
| 59 | + |
| 60 | + // generate a shared lib with a seconary entry point |
| 61 | + runCLI( |
| 62 | + `generate @nx/angular:library ${sharedLib} --buildable --no-standalone --no-interactive` |
| 63 | + ); |
| 64 | + runCLI( |
| 65 | + `generate @nx/angular:library-secondary-entry-point --library=${sharedLib} --name=${secondaryEntry} --no-interactive` |
| 66 | + ); |
| 67 | + |
| 68 | + // Add a library that will be accessed via a wildcard in tspath mappings |
| 69 | + runCLI( |
| 70 | + `generate @nx/angular:library ${wildcardLib} --buildable --no-standalone --no-interactive` |
| 71 | + ); |
| 72 | + |
| 73 | + updateJson('tsconfig.base.json', (json) => { |
| 74 | + delete json.compilerOptions.paths[`@${proj}/${wildcardLib}`]; |
| 75 | + json.compilerOptions.paths[`@${proj}/${wildcardLib}/*`] = [ |
| 76 | + `${wildcardLib}/src/lib/*`, |
| 77 | + ]; |
| 78 | + return json; |
| 79 | + }); |
| 80 | + |
| 81 | + // update host & remote files to use shared library |
| 82 | + updateFile( |
| 83 | + `${hostApp}/src/app/app.module.ts`, |
| 84 | + `import { NgModule } from '@angular/core'; |
| 85 | + import { BrowserModule } from '@angular/platform-browser'; |
| 86 | + import { ${ |
| 87 | + names(wildcardLib).className |
| 88 | + }Module } from '@${proj}/${wildcardLib}/${ |
| 89 | + names(secondaryEntry).fileName |
| 90 | + }.module'; |
| 91 | + import { ${ |
| 92 | + names(sharedLib).className |
| 93 | + }Module } from '@${proj}/${sharedLib}'; |
| 94 | + import { ${ |
| 95 | + names(secondaryEntry).className |
| 96 | + }Module } from '@${proj}/${sharedLib}/${secondaryEntry}'; |
| 97 | + import { AppComponent } from './app.component'; |
| 98 | + import { NxWelcomeComponent } from './nx-welcome.component'; |
| 99 | + import { RouterModule } from '@angular/router'; |
| 100 | +
|
| 101 | + @NgModule({ |
| 102 | + declarations: [AppComponent, NxWelcomeComponent], |
| 103 | + imports: [ |
| 104 | + BrowserModule, |
| 105 | + ${names(sharedLib).className}Module, |
| 106 | + ${names(wildcardLib).className}Module, |
| 107 | + RouterModule.forRoot( |
| 108 | + [ |
| 109 | + { |
| 110 | + path: '${remoteApp1}', |
| 111 | + loadChildren: () => |
| 112 | + import('${remoteApp1}/Module').then( |
| 113 | + (m) => m.RemoteEntryModule |
| 114 | + ), |
| 115 | + }, |
| 116 | + ], |
| 117 | + { initialNavigation: 'enabledBlocking' } |
| 118 | + ), |
| 119 | + ], |
| 120 | + providers: [], |
| 121 | + bootstrap: [AppComponent], |
| 122 | + }) |
| 123 | + export class AppModule {} |
| 124 | + ` |
| 125 | + ); |
| 126 | + updateFile( |
| 127 | + `${remoteApp1}/src/app/remote-entry/entry.module.ts`, |
| 128 | + `import { NgModule } from '@angular/core'; |
| 129 | + import { CommonModule } from '@angular/common'; |
| 130 | + import { RouterModule } from '@angular/router'; |
| 131 | + import { ${names(sharedLib).className}Module } from '@${proj}/${sharedLib}'; |
| 132 | + import { ${ |
| 133 | + names(secondaryEntry).className |
| 134 | + }Module } from '@${proj}/${sharedLib}/${secondaryEntry}'; |
| 135 | + import { RemoteEntryComponent } from './entry.component'; |
| 136 | + import { NxWelcomeComponent } from './nx-welcome.component'; |
| 137 | +
|
| 138 | + @NgModule({ |
| 139 | + declarations: [RemoteEntryComponent, NxWelcomeComponent], |
| 140 | + imports: [ |
| 141 | + CommonModule, |
| 142 | + ${names(sharedLib).className}Module, |
| 143 | + RouterModule.forChild([ |
| 144 | + { |
| 145 | + path: '', |
| 146 | + component: RemoteEntryComponent, |
| 147 | + }, |
| 148 | + ]), |
| 149 | + ], |
| 150 | + providers: [], |
| 151 | + }) |
| 152 | + export class RemoteEntryModule {} |
| 153 | + ` |
| 154 | + ); |
| 155 | + |
| 156 | + const processSwc = await runCommandUntil( |
| 157 | + `serve ${remoteApp1}`, |
| 158 | + (output) => |
| 159 | + !output.includes(`Remote '${remoteApp1}' failed to serve correctly`) && |
| 160 | + output.includes(`browser compiled`) |
| 161 | + ); |
| 162 | + await killProcessAndPorts(processSwc.pid, remotePort); |
| 163 | + }, 20_000_000); |
| 164 | +}); |
0 commit comments