Chris Halcrow
Chris Halcrow

Reputation: 31950

Angular testing with Jasmine and Karma - router-outlet errors and component creation test fail, for lazy-loaded component


UPDATE - the answer from @Sanket is useful, however I still have the issue. I've commented on his answer, to explain the remaining issue


Error relating to load of BackupComponent

(I believe that router-outlet is an Angular component, not a Web Component, as the codebase has no references to CUSTOM_ELEMENTS_SCHEMA)

        <div class="col-lg-7 col-xs-12">
    1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
    2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
        </div>
        <div class="col-lg-7 col-xs-12">
          [ERROR ->]<router-outlet></router-outlet>
        </div>
      </div>
    Error: Template parse errors:
    'app-backup-list' is not a known element:
    1. If 'app-backup-list' is an Angular component, then verify that it is part of this module.
    2. If 'app-backup-list' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("<div class="row">
        <div class="col-lg-5 col-xs-12">
            [ERROR ->]<app-backup-list></app-backup-list>
        </div>
        <div class="col-lg-7 col-xs-12">
    "): ng:///DynamicTestModule/BackupComponent.html@2:8
    'router-outlet' is not a known element:
    1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
    2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
        </div>
        <div class="col-lg-7 col-xs-12">
          [ERROR ->]<router-outlet></router-outlet>
        </div>
      </div>
    "): ng:///DynamicTestModule/BackupComponent.html@5:6
    error properties: Object({ ngSyntaxError: true, ngParseErrors: [ 'app-backup-list' is not a known element:
    "): ng:///DynamicTestModule/BackupComponent.html@2:8, 'router-outlet' is not a known element:
    "): ng:///DynamicTestModule/BackupComponent.html@5:6 ] })
        at syntaxError (node_modules/@angular/compiler/fesm5/compiler.js:2430:1)
        at TemplateParser.push../node_modules/@angular/compiler/fesm5/compiler.js.TemplateParser.parse (node_modules/@angular/compiler/fesm5/compiler.js:20605:1)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._parseTemplate (node_modules/@angular/compiler/fesm5/compiler.js:26171:1)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileTemplate (node_modules/@angular/compiler/fesm5/compiler.js:26158:1)
        at node_modules/@angular/compiler/fesm5/compiler.js:26101:48
        at <Jasmine>
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileComponents (node_modules/@angular/compiler/fesm5/compiler.js:26101:1)
        at node_modules/@angular/compiler/fesm5/compiler.js:26019:1
        at Object.then (node_modules/@angular/compiler/fesm5/compiler.js:2421:33)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileModuleAndAllComponents (node_modules/@angular/compiler/fesm5/compiler.js:26017:1)
    Error: Expected undefined to be truthy.
        at <Jasmine>
        at UserContext.<anonymous> (src/app/backup/components/backup/backup.component.spec.ts:23:23)
        at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1)

backup.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { BackupComponent } from './backup.component';

describe('BackupComponent', () => {
  let component: BackupComponent;
  let fixture: ComponentFixture<BackupComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ BackupComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BackupComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

backup.component.html

<div class="row">
    <div class="col-lg-5 col-xs-12">
        <app-backup-list></app-backup-list>
    </div>
    <div class="col-lg-7 col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>

backup.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-backup',
  templateUrl: './backup.component.html',
  styleUrls: ['./backup.component.css']
})
export class BackupComponent {

  constructor() { }

}

backup.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { AccordionModule } from 'ngx-bootstrap';

import { BackupRoutingModule } from './backup-routing.module';
import { BackupComponent } from './components/backup/backup.component';
import { BackupEditComponent } from './components/backup-edit/backup-edit.component';
import { BackupItemComponent } from './components/backup-item/backup-item.component';
import { BackupListComponent } from './components/backup-list/backup-list.component';
import { SharedModule } from '../shared/shared.module';
import { CommonAngularWidgetsLibModule } from '@ecotech/common-angular-widgets-lib';

@NgModule({
  declarations: [
    BackupComponent,
    BackupEditComponent,
    BackupItemComponent,
    BackupListComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    AccordionModule,
    SharedModule,
    CommonAngularWidgetsLibModule,
    BackupRoutingModule
  ]
})
export class BackupModule { }

backup.routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { BackupComponent } from './components/backup/backup.component';
import { AuthGuard } from '../guards/auth.guard';
import { BackupEditComponent } from './components/backup-edit/backup-edit.component';
import { PendingChangesGuard } from '../guards/pending-changes.guard';

const routes: Routes = [{
  path: '',
  component: BackupComponent,
  canActivate: [AuthGuard],
  children: [{
    path: 'new',
    component: BackupEditComponent,
    canDeactivate: [PendingChangesGuard]
  }, {
    path: ':id',
    component: BackupEditComponent,
    canDeactivate: [PendingChangesGuard]
  }]
}];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class BackupRoutingModule { }

app.module.ts (cut down)

declare var require: any;

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core';
import { ReactiveFormsModule, FormsModule  } from '@angular/forms';
import { DynamicFormModule } from './dynamic-form/dynamic-form.module';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core/core.module';


@NgModule({
  declarations: [
    AppComponent,
    ... custom components, not including BackupComponent
  ],
  imports: [
    FormsModule,
    BrowserModule,
    ReactiveFormsModule,
    DynamicFormModule,
    CoreModule,
    SharedModule,
    AppRoutingModule,
  ],
  providers:
  [
    ... providers for app internationalisation only
  ],
  bootstrap: [AppComponent],
  entryComponents: [ConfirmationComponent,
    CalibrationWidgetComponent,
    RunCalibrationPointModalComponent,
    ... other custom components
  ]
})

export class AppModule { }

app.routing.module.ts (cut down) - responsible for the lazy loading of BackupModule

import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';


const appRoutes:  Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  ...
  { path: 'backups', loadChildren: './backup/backup.module#BackupModule' },
  ... other custom routes
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })],
  exports: [RouterModule]
})

export class AppRoutingModule { }

Upvotes: 2

Views: 1760

Answers (1)

Sanket
Sanket

Reputation: 632

You would need to include RouterTesting module in the imports and also AppBackupListComponent in declarations.

Below is sample code for your test backup.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { BackupComponent } from './backup.component';

describe('BackupComponent', () => {
  let component: BackupComponent;
  let fixture: ComponentFixture<BackupComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ BackupComponent, BackupListComponent ],
      imports: [RouterTestingModule]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BackupComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

Hope this helps.

Upvotes: 3

Related Questions