/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {Compiler, Component, NgModule, NgModuleFactoryLoader, NgModuleRef} from '@angular/core'; import {fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; import {PreloadAllModules, PreloadingStrategy, RouterPreloader} from '@angular/router'; import {Route, RouteConfigLoadEnd, RouteConfigLoadStart, Router, RouterModule} from '../index'; import {LoadedRouterConfig} from '../src/config'; import {RouterTestingModule, SpyNgModuleFactoryLoader} from '../testing'; describe('RouterPreloader', () => { @Component({template: ''}) class LazyLoadedCmp { } describe('should not load configurations with canLoad guard', () => { @NgModule({ declarations: [LazyLoadedCmp], imports: [RouterModule.forChild([{path: 'LoadedModule1', component: LazyLoadedCmp}])] }) class LoadedModule { } beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes( [{path: 'lazy', loadChildren: 'expected', canLoad: ['someGuard']}])], providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}] }); }); it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router], (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { loader.stubbedModules = {expected: LoadedModule}; preloader.preload().subscribe(() => {}); tick(); const c = router.config; expect((c[0] as any)._loadedConfig).not.toBeDefined(); }))); }); describe('should preload configurations', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes([{path: 'lazy', loadChildren: 'expected'}])], providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}] }); }); it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef], (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router, testModule: NgModuleRef) => { const events: Array = []; @NgModule({ declarations: [LazyLoadedCmp], imports: [RouterModule.forChild([{path: 'LoadedModule2', component: LazyLoadedCmp}])] }) class LoadedModule2 { } @NgModule({ imports: [RouterModule.forChild([{path: 'LoadedModule1', loadChildren: 'expected2'}])] }) class LoadedModule1 { } router.events.subscribe(e => { if (e instanceof RouteConfigLoadEnd || e instanceof RouteConfigLoadStart) { events.push(e); } }); loader.stubbedModules = { expected: LoadedModule1, expected2: LoadedModule2, }; preloader.preload().subscribe(() => {}); tick(); const c = router.config; expect(c[0].loadChildren).toEqual('expected'); const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig!; const module: any = loadedConfig.module; expect(loadedConfig.routes[0].path).toEqual('LoadedModule1'); expect(module._parent).toBe(testModule); const loadedConfig2: LoadedRouterConfig = (loadedConfig.routes[0] as any)._loadedConfig!; const module2: any = loadedConfig2.module; expect(loadedConfig2.routes[0].path).toEqual('LoadedModule2'); expect(module2._parent).toBe(module); expect(events.map(e => e.toString())).toEqual([ 'RouteConfigLoadStart(path: lazy)', 'RouteConfigLoadEnd(path: lazy)', 'RouteConfigLoadStart(path: LoadedModule1)', 'RouteConfigLoadEnd(path: LoadedModule1)', ]); }))); }); describe('should support modules that have already been loaded', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes([{path: 'lazy', loadChildren: 'expected'}])], providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}] }); }); it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router, NgModuleRef, Compiler], (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router, testModule: NgModuleRef, compiler: Compiler) => { @NgModule() class LoadedModule2 { } const module2 = compiler.compileModuleSync(LoadedModule2).create(null); @NgModule({ imports: [RouterModule.forChild([ { path: 'LoadedModule2', loadChildren: 'no', _loadedConfig: { routes: [{path: 'LoadedModule3', loadChildren: 'expected3'}], module: module2, } }, ])] }) class LoadedModule1 { } @NgModule({imports: [RouterModule.forChild([])]}) class LoadedModule3 { } loader.stubbedModules = { expected: LoadedModule1, expected3: LoadedModule3, }; preloader.preload().subscribe(() => {}); tick(); const c = router.config; const loadedConfig: LoadedRouterConfig = (c[0] as any)._loadedConfig!; const module: any = loadedConfig.module; expect(module._parent).toBe(testModule); const loadedConfig2: LoadedRouterConfig = (loadedConfig.routes[0] as any)._loadedConfig!; const loadedConfig3: LoadedRouterConfig = (loadedConfig2.routes[0] as any)._loadedConfig!; const module3: any = loadedConfig3.module; expect(module3._parent).toBe(module2); }))); }); describe('should ignore errors', () => { @NgModule({ declarations: [LazyLoadedCmp], imports: [RouterModule.forChild([{path: 'LoadedModule1', component: LazyLoadedCmp}])] }) class LoadedModule { } beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes([ {path: 'lazy1', loadChildren: 'expected1'}, {path: 'lazy2', loadChildren: 'expected2'} ])], providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}] }); }); it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router], (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { loader.stubbedModules = {expected2: LoadedModule}; preloader.preload().subscribe(() => {}); tick(); const c = router.config; expect((c[0] as any)._loadedConfig).not.toBeDefined(); expect((c[1] as any)._loadedConfig).toBeDefined(); }))); }); describe('should copy loaded configs', () => { const configs = [{path: 'LoadedModule1', component: LazyLoadedCmp}]; @NgModule({declarations: [LazyLoadedCmp], imports: [RouterModule.forChild(configs)]}) class LoadedModule { } beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule.withRoutes([{path: 'lazy1', loadChildren: 'expected'}])], providers: [{provide: PreloadingStrategy, useExisting: PreloadAllModules}] }); }); it('should work', fakeAsync(inject( [NgModuleFactoryLoader, RouterPreloader, Router], (loader: SpyNgModuleFactoryLoader, preloader: RouterPreloader, router: Router) => { loader.stubbedModules = {expected: LoadedModule}; preloader.preload().subscribe(() => {}); tick(); const c = router.config as {_loadedConfig: LoadedRouterConfig}[]; expect(c[0]._loadedConfig).toBeDefined(); expect(c[0]._loadedConfig!.routes).not.toBe(configs); expect(c[0]._loadedConfig!.routes[0]).not.toBe(configs[0]); expect(c[0]._loadedConfig!.routes[0].component).toBe(configs[0].component); }))); }); });