6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
import { DefaultTimeout , runTargetSpec } from '@angular-devkit/architect/testing' ;
9
- import { normalize , virtualFs } from '@angular-devkit/core' ;
10
- import { tap } from 'rxjs/operators' ;
11
- import { host } from '../utils' ;
9
+ import { getSystemPath , join , normalize , virtualFs } from '@angular-devkit/core' ;
10
+ import * as express from 'express' ; // tslint:disable-line:no-implicit-dependencies
11
+ import { Server } from 'http' ;
12
+ import { concatMap , tap } from 'rxjs/operators' ;
13
+ import { host , protractorTargetSpec } from '../utils' ;
12
14
13
15
14
16
describe ( 'AppShell Builder' , ( ) => {
15
17
beforeEach ( done => host . initialize ( ) . toPromise ( ) . then ( done , done . fail ) ) ;
16
18
afterEach ( done => host . restore ( ) . toPromise ( ) . then ( done , done . fail ) ) ;
17
19
18
20
const targetSpec = { project : 'app' , target : 'app-shell' } ;
21
+ const appShellRouteFiles = {
22
+ 'src/app/app-shell/app-shell.component.html' : `
23
+ <p>
24
+ app-shell works!
25
+ </p>
26
+ ` ,
27
+ 'src/app/app-shell/app-shell.component.ts' : `
28
+ import { Component, OnInit } from '@angular/core';
29
+
30
+ @Component({
31
+ selector: 'app-app-shell',
32
+ templateUrl: './app-shell.component.html',
33
+ })
34
+ export class AppShellComponent implements OnInit {
35
+
36
+ constructor() { }
37
+
38
+ ngOnInit() {
39
+ }
40
+
41
+ }
42
+ ` ,
43
+ 'src/app/app.module.ts' : `
44
+ import { BrowserModule } from '@angular/platform-browser';
45
+ import { NgModule } from '@angular/core';
46
+
47
+ import { AppRoutingModule } from './app-routing.module';
48
+ import { AppComponent } from './app.component';
49
+ import { environment } from '../environments/environment';
50
+ import { RouterModule } from '@angular/router';
51
+
52
+ @NgModule({
53
+ declarations: [
54
+ AppComponent
55
+ ],
56
+ imports: [
57
+ BrowserModule.withServerTransition({ appId: 'serverApp' }),
58
+ AppRoutingModule,
59
+ RouterModule
60
+ ],
61
+ providers: [],
62
+ bootstrap: [AppComponent]
63
+ })
64
+ export class AppModule { }
65
+ ` ,
66
+ 'src/app/app.server.module.ts' : `
67
+ import { NgModule } from '@angular/core';
68
+ import { ServerModule } from '@angular/platform-server';
69
+
70
+ import { AppModule } from './app.module';
71
+ import { AppComponent } from './app.component';
72
+ import { Routes, RouterModule } from '@angular/router';
73
+ import { AppShellComponent } from './app-shell/app-shell.component';
74
+
75
+ const routes: Routes = [ { path: 'shell', component: AppShellComponent }];
76
+
77
+ @NgModule({
78
+ imports: [
79
+ AppModule,
80
+ ServerModule,
81
+ RouterModule.forRoot(routes),
82
+ ],
83
+ bootstrap: [AppComponent],
84
+ declarations: [AppShellComponent],
85
+ })
86
+ export class AppServerModule {}
87
+ ` ,
88
+ 'src/main.ts' : `
89
+ import { enableProdMode } from '@angular/core';
90
+ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
91
+
92
+ import { AppModule } from './app/app.module';
93
+ import { environment } from './environments/environment';
94
+
95
+ if (environment.production) {
96
+ enableProdMode();
97
+ }
98
+
99
+ document.addEventListener('DOMContentLoaded', () => {
100
+ platformBrowserDynamic().bootstrapModule(AppModule)
101
+ .catch(err => console.log(err));
102
+ });
103
+ ` ,
104
+ 'src/app/app-routing.module.ts' : `
105
+ import { NgModule } from '@angular/core';
106
+ import { Routes, RouterModule } from '@angular/router';
107
+
108
+ const routes: Routes = [];
109
+
110
+ @NgModule({
111
+ imports: [RouterModule.forRoot(routes)],
112
+ exports: [RouterModule]
113
+ })
114
+ export class AppRoutingModule { }
115
+ ` ,
116
+ 'src/app/app.component.html' : `
117
+ <router-outlet></router-outlet>
118
+ ` ,
119
+ } ;
19
120
20
121
it ( 'works (basic)' , done => {
21
122
host . replaceInFile ( 'src/app/app.module.ts' , / B r o w s e r M o d u l e / , `
@@ -33,26 +134,46 @@ describe('AppShell Builder', () => {
33
134
} ) ;
34
135
35
136
it ( 'works with route' , done => {
36
- host . writeMultipleFiles ( {
37
- 'src/app/app-shell/app-shell.component.html' : `
38
- <p>
39
- app-shell works!
40
- </p>
41
- ` ,
42
- 'src/app/app-shell/app-shell.component.ts' : `
43
- import { Component, OnInit } from '@angular/core';
44
-
45
- @Component({
46
- selector: 'app-app-shell',
47
- templateUrl: './app-shell.component.html',
48
- })
49
- export class AppShellComponent implements OnInit {
50
-
51
- constructor() { }
137
+ host . writeMultipleFiles ( appShellRouteFiles ) ;
138
+ const overrides = { route : 'shell' } ;
52
139
53
- ngOnInit() {
54
- }
140
+ runTargetSpec ( host , targetSpec , overrides , DefaultTimeout * 2 ) . pipe (
141
+ tap ( ( buildEvent ) => expect ( buildEvent . success ) . toBe ( true ) ) ,
142
+ tap ( ( ) => {
143
+ const fileName = 'dist/index.html' ;
144
+ const content = virtualFs . fileBufferToString ( host . scopedSync ( ) . read ( normalize ( fileName ) ) ) ;
145
+ expect ( content ) . toContain ( 'app-shell works!' ) ;
146
+ } ) ,
147
+ ) . toPromise ( ) . then ( done , done . fail ) ;
148
+ } ) ;
55
149
150
+ it ( 'works with route and service-worker' , done => {
151
+ host . writeMultipleFiles ( appShellRouteFiles ) ;
152
+ host . writeMultipleFiles ( {
153
+ 'src/ngsw-config.json' : `
154
+ {
155
+ "index": "/index.html",
156
+ "assetGroups": [{
157
+ "name": "app",
158
+ "installMode": "prefetch",
159
+ "resources": {
160
+ "files": [
161
+ "/favicon.ico",
162
+ "/index.html",
163
+ "/*.css",
164
+ "/*.js"
165
+ ]
166
+ }
167
+ }, {
168
+ "name": "assets",
169
+ "installMode": "lazy",
170
+ "updateMode": "prefetch",
171
+ "resources": {
172
+ "files": [
173
+ "/assets/**"
174
+ ]
175
+ }
176
+ }]
56
177
}
57
178
` ,
58
179
'src/app/app.module.ts' : `
@@ -61,6 +182,7 @@ describe('AppShell Builder', () => {
61
182
62
183
import { AppRoutingModule } from './app-routing.module';
63
184
import { AppComponent } from './app.component';
185
+ import { ServiceWorkerModule } from '@angular/service-worker';
64
186
import { environment } from '../environments/environment';
65
187
import { RouterModule } from '@angular/router';
66
188
@@ -71,77 +193,59 @@ describe('AppShell Builder', () => {
71
193
imports: [
72
194
BrowserModule.withServerTransition({ appId: 'serverApp' }),
73
195
AppRoutingModule,
196
+ ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }),
74
197
RouterModule
75
198
],
76
199
providers: [],
77
200
bootstrap: [AppComponent]
78
201
})
79
202
export class AppModule { }
80
203
` ,
81
- 'src/app/app.server.module.ts' : `
82
- import { NgModule } from '@angular/core';
83
- import { ServerModule } from '@angular/platform-server';
84
-
85
- import { AppModule } from './app.module';
86
- import { AppComponent } from './app.component';
87
- import { Routes, RouterModule } from '@angular/router';
88
- import { AppShellComponent } from './app-shell/app-shell.component';
89
-
90
- const routes: Routes = [ { path: 'shell', component: AppShellComponent }];
91
-
92
- @NgModule({
93
- imports: [
94
- AppModule,
95
- ServerModule,
96
- RouterModule.forRoot(routes),
97
- ],
98
- bootstrap: [AppComponent],
99
- declarations: [AppShellComponent],
100
- })
101
- export class AppServerModule {}
102
- ` ,
103
- 'src/main.ts' : `
104
- import { enableProdMode } from '@angular/core';
105
- import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
106
-
107
- import { AppModule } from './app/app.module';
108
- import { environment } from './environments/environment';
109
-
110
- if (environment.production) {
111
- enableProdMode();
112
- }
113
-
114
- document.addEventListener('DOMContentLoaded', () => {
115
- platformBrowserDynamic().bootstrapModule(AppModule)
116
- .catch(err => console.log(err));
204
+ 'e2e/app.e2e-spec.ts' : `
205
+ import { browser, by, element } from 'protractor';
206
+
207
+ it('should have ngsw in normal state', () => {
208
+ browser.get('/');
209
+ // Wait for service worker to load.
210
+ browser.sleep(2000);
211
+ browser.waitForAngularEnabled(false);
212
+ browser.get('/ngsw/state');
213
+ // Should have updated, and be in normal state.
214
+ expect(element(by.css('pre')).getText()).not.toContain('Last update check: never');
215
+ expect(element(by.css('pre')).getText()).toContain('Driver state: NORMAL');
117
216
});
118
217
` ,
119
- 'src/app/app-routing.module.ts' : `
120
- import { NgModule } from '@angular/core';
121
- import { Routes, RouterModule } from '@angular/router';
122
-
123
- const routes: Routes = [];
124
-
125
- @NgModule({
126
- imports: [RouterModule.forRoot(routes)],
127
- exports: [RouterModule]
128
- })
129
- export class AppRoutingModule { }
130
- ` ,
131
- 'src/app/app.component.html' : `
132
- <router-outlet></router-outlet>
133
- ` ,
134
218
} ) ;
219
+ // This should match the browser target prod config.
220
+ host . replaceInFile (
221
+ 'angular.json' ,
222
+ '"buildOptimizer": true' ,
223
+ '"buildOptimizer": true, "serviceWorker": true' ,
224
+ ) ;
135
225
136
226
const overrides = { route : 'shell' } ;
227
+ const prodTargetSpec = { ...targetSpec , configuration : 'production' } ;
228
+ let server : Server ;
137
229
138
- runTargetSpec ( host , targetSpec , overrides , DefaultTimeout * 2 ) . pipe (
230
+ // Build the app shell.
231
+ runTargetSpec ( host , prodTargetSpec , overrides , DefaultTimeout * 2 ) . pipe (
139
232
tap ( ( buildEvent ) => expect ( buildEvent . success ) . toBe ( true ) ) ,
233
+ // Make sure the index is pre-rendering the route.
140
234
tap ( ( ) => {
141
235
const fileName = 'dist/index.html' ;
142
236
const content = virtualFs . fileBufferToString ( host . scopedSync ( ) . read ( normalize ( fileName ) ) ) ;
143
237
expect ( content ) . toContain ( 'app-shell works!' ) ;
144
238
} ) ,
239
+ tap ( ( ) => {
240
+ // Serve the app using a simple static server.
241
+ const app = express ( ) ;
242
+ app . use ( '/' , express . static ( getSystemPath ( join ( host . root ( ) , 'dist' ) ) + '/' ) ) ;
243
+ server = app . listen ( 4200 ) ;
244
+ } ) ,
245
+ // Load app in protractor, then check service worker status.
246
+ concatMap ( ( ) => runTargetSpec ( host , protractorTargetSpec , { devServerTarget : undefined } ) ) ,
247
+ // Close the express server.
248
+ tap ( ( ) => server . close ( ) ) ,
145
249
) . toPromise ( ) . then ( done , done . fail ) ;
146
250
} ) ;
147
251
} ) ;
0 commit comments