Unit testing component with angulartics2 - Can't bind to 'angularticsCategory' since it isn't a known property of 'div' - unit-testing

I am starting a project using Angular 2.0.0 stable release created with angular-cli 1.0.0-beta.14 and angulartics 1.1.9.
I am trying to start some simple unit testing and am recording clicks on a button component
<div class="sidebar-toggle" (click)="toggleSideBar()" angulartics2On="click" angularticsCategory="{{ expanded ? 'expand': 'collapse' }}">
//divContent
</div>
However, when I run the test which is simple bootstrapping the component I get the error
Can't bind to 'angularticsCategory' since it isn't a known property of 'div'
The app works fine but the issue only comes up in testing. I can't find an example where someone is having the same error in testing. I know I am missing something like not properly exposing the angulartics2 lib in my karma.conf OR not injecting the Angulartics or a mocked dependency in my Testbed config.
Really lost and would like to know if anyone is having similar problems. Can provide more code snippets if needed but don't want to dump the whole file nobody's got time to read that!

In my case to get Angulatics2 unit tests to work I had to:
1) Import Angulartics2Module into the root app.module.ts
import { Angulartics2Module } from 'angulartics2';
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
#NgModule({
declarations: [
AppComponent,
HeaderComponent,
],
imports: [
BrowserModule,
Angulartics2Module.forRoot({ developerMode: !environment.production }),
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
2) Enable tracking in app.component.ts
import { Angulartics2GoogleGlobalSiteTag } from 'angulartics2/gst';
import { Component } from '#angular/core';
import { RouterOutlet } from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
constructor(
public angulartics2GoogleGlobalSiteTag: Angulartics2GoogleGlobalSiteTag,
) {
this.angulartics2GoogleGlobalSiteTag.startTracking();
}
}
3) Import Angulartics2Module.forRoot() and Angulartics2 provider in to app.component.spec.ts
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { TestBed, async } from '#angular/core/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { Angulartics2, Angulartics2Module } from 'angulartics2';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
Angulartics2Module.forRoot(),
HttpClientTestingModule,
],
declarations: [
AppComponent,
HeaderComponent,
],
providers: [
Angulartics2,
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
and for any other component.spec.ts files that you have using the Angulatics2 directives.

Related

error NG8001: 'mat-form-field' is not a known element

Currently I am struggling with an error after try to use a few mat components like "mat-form-field" or "mat-date-range-input" etc..
I've already imported them in the app.module file as I always do with these kind of component, but I get many errors like this:
If 'mat-form-field' is an Angular component, then verify that it is part of this module
If 'mat-label' is an Angular component, then verify that it is part of this module.
I have to say that in the same project I am using mat-tab-group and mat-tab etc... and I have not any errors with them.
These is my code:
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AngularMaterialModule } from './angular-material.module/angular-material.module.module';
import { MatButtonToggleModule } from '#angular/material/button-toggle';
import { MatSlideToggleModule } from '#angular/material/slide-toggle';
import { MatCardModule } from '#angular/material/card';
import { MatFormFieldModule } from '#angular/material/form-field';
import { MatInputModule } from '#angular/material/';
import { MatIconModule } from '#angular/material/icon';
import { ClipboardModule } from '#angular/cdk/clipboard';
#NgModule({
declarations: [], // I've omitted this part because is not relevant to this issue
imports: [
BrowserModule,
AppRoutingModule,
AngularMaterialModule,
MatProgressSpinnerModule,
MatButtonToggleModule,
MatSlideToggleModule,
MatCardModule,
MatFormFieldModule,
MatInputModule,
MatIconModule,
ClipboardModule
],
providers: [{provide: LocationStrategy, useClass: PathLocationStrategy}],
bootstrap: [AppComponent]
})
export class AppModule { }
And my component : create.report.component.html
<mat-form-field appearance="fill">
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [formGroup]="reportForm" [rangePicker]="picker">
<input matStartDate formControlName="from" placeholder="Start date">
<input matEndDate formControlName="to" placeholder="End date">
</mat-date-range-input>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-date-range-picker #picker></mat-date-range-picker>
</mat-form-field>
create.report.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder, Validators, FormGroup, FormArray, FormControl } from '#angular/forms';
import { Router } from '#angular/router';
#Component({
selector: 'app-report',
templateUrl: './create.report.component.html'
})
export class CreateReportComponent implements OnInit {
reportForm = new FormGroup({
from: new FormControl(),
to: new FormControl()
});
constructor(
private router: Router,
private fb: FormBuilder) {
this.buildForm();
}
ngOnInit(): void {
}
buildForm() {
// console.log('***buildForm this.hmService***', this.hmService);
this.reportForm = this.fb.group( {
from : [ '', [Validators.required, Validators.minLength(5)]],
to: [ '', [Validators.required, Validators.minLength(5)]],
published : [ '', [Validators.required, Validators.minLength(5)]]
});
}
}
Ok, finally I solved!
With this line I fix the issue I had with material components. Now I can use them in my create.report.component .
import { CreateReportComponent } from './pages/report/create/create.report.component';

App testing return "NullInjectorError: No provider for Location!"

Testing an app in Angular 7 with Karma, I can't remove the error in subj.
I have searched various places (mostly here) but either the solutions don't work or are not relevant to my case.
App.component.html:
<app-header></app-header>
<router-outlet></router-outlet>
Header.component.ts:
import { Component, OnInit, ViewChild, ElementRef, AfterViewInit, EventEmitter, Output } from '#angular/core';
import { Router } from '#angular/router';
import { Location } from '#angular/common';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.less']
})
export class HeaderComponent implements AfterViewInit, OnInit {
constructor(private location: Location, private router: Router) {
setInterval(() => {
this.now = new Date();
}, 1000);
}
...
onKeyDown(event: KeyboardEvent): void {
event.preventDefault();
if (event.which === this.KEY_ESCAPE){
if (this.router.url !== '/'){
this.location.back();
}
}
}
App.component.spec.ts:
import { TestBed, async } from '#angular/core/testing';
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { RouterOutlet } from '#angular/router';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,
HeaderComponent,
RouterOutlet
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
AppComponent should create the app
Error: StaticInjectorError(DynamicTestModule)[HeaderComponent -> Location]:
StaticInjectorError(Platform: core)[HeaderComponent -> Location]:
NullInjectorError: No provider for Location!
In my case, I was using the RouterTestingModule already which should not raise this error. I have imported Location from the wrong place so the error has appeared. 'Location' should be imported from here:
import {Location} from '#angular/common';
In your imports you should only import RouterTestingModule like this:
TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(
[
{path: 'add', component: DummyComponent, pathMatch: 'full'}
]
)
],
and then you can use Location like this:
it('Should navigate to / before + button click', () => {
const location: Location = TestBed.get(Location);
expect(location.path()).toBe('');
});
Just don't forget to import Location from #angular/common
Fixed it: all I needed to do was to import RouterModule.forRoot([]), which seems to be a common mistake as it fixes a lot of StaticInjectorError(DynamicTestModule) errors.
An alternative to NullInjectorError: No provider for Location! during the testing is to import RouterTestingModule.
ex. mycomponent.spec.ts:
import { RouterTestingModule } from '#angular/router/testing';
...
beforeEach(() => TestBed.configureTestingModule({
imports: [ RouterTestingModule ]
}));
Can u try to import > CommonModule
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [CommonModule]
declarations: [
AppComponent,
HeaderComponent,
RouterOutlet
],
}).compileComponents();
}));

Unit testing errors for ngModel, custom pipe and modal [duplicate]

This question already has answers here:
angular2 testing: Can't bind to 'ngModel' since it isn't a known property of 'input'
(2 answers)
Closed 5 years ago.
Trying to test my Angular application, but when trying to create my component I get multiple errors.
Can't bind to 'ngModel' since it isn't a known property of 'input'.
The pipe 'sortOnLike' could not be found.
'app-edit-message-modal' is not a known element:
If 'app-edit-message-modal' is an Angular component, then verify that it is part of this module.
If 'app-edit-message-modal' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '#NgModule.schemas' of this component to suppress this message.
Answers to similar errors haven't helped me much.
dashboard.spec.ts
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { Router } from "#angular/router";
import { MockAF } from "../../providers/mockAf";
import { AF } from "../../providers/af";
import { DashboardComponent } from './dashboard.component';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
let routerStub;
beforeEach(async(() => {
routerStub = {
navigate: jasmine.createSpy('navigate')
};
TestBed.configureTestingModule({
declarations: [ DashboardComponent ],
providers: [
{ provide: AF, useClass: MockAF },
{ provide: Router, useValue: routerStub },
],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Snippets from HTMLfile:
dashboard.component.html
<figure class="highlight">
<input type="textarea" class="message-text" [(ngModel)]="newMessage"
(keyup.enter)="sendMessage()">
<a class="send-message" (click)="sendMessage()">SEND</a>
</figure>
<a *ngIf="isMe(message.email)" type='edit' class='edit-text' style="cursor:
pointer;" (click)="show(message.$key, message.message)">Edit</a>
<!-- Modal (popup) for editing messages belonging to you -->
<app-edit-message-modal>
// modal form
</app-edit-message-modal>
<div *ngFor="let message of afService.messages | async |
sortOnLike:'votes':false">
snippets from dashboard.component.ts
import { Component, OnInit, AfterViewChecked, ElementRef, ViewChild } from
'#angular/core';
import { ActivatedRoute, Router } from '#angular/router';
import { AF } from '../../providers/af';
import { FirebaseListObservable, AngularFire } from 'angularfire2';
import { Bubble } from './bubble';
import { EditMessageModalComponent } from '../edit-message-modal/edit-
message-modal.component';
show(key: string, message: string): void {
this.modalMessage = message;
this.modalMessageKey = key;
this.modal.show();
}
hide(): void {
this.modalMessage = null;
this.modalMessageKey = null;
this.modal.hide();
}
app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { FormsModule } from '#angular/forms';
import { HttpModule } from '#angular/http';
import { RouterModule, Routes } from '#angular/router';
import { AngularFireModule } from 'angularfire2';
import { AppComponent } from './app.component';
import { RegistrationPageComponent } from './registration-page/registration-
page.component';
import { LoginPageComponent } from './login-page/login-page.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AF } from '../providers/af';
import { FrontscreenComponent } from './frontscreen/frontscreen.component';
import { StudentDashboardComponent } from './student-dashboard/student-
dashboard.component';
import { LecturerDashboardComponent } from './lecturer-dashboard/lecturer-
dashboard.component';
import { firebaseConfig } from './config';
import { EditCourseModalComponent } from './edit-course-modal/edit-course-
modal.component';
import { EditMessageModalComponent } from './edit-message-modal/edit-
message-modal.component';
import { SortOnLikePipe } from './sort-on-like.pipe'
#NgModule({
declarations: [
AppComponent,
RegistrationPageComponent,
LoginPageComponent,
DashboardComponent,
FrontscreenComponent,
StudentDashboardComponent,
LecturerDashboardComponent,
EditCourseModalComponent,
EditMessageModalComponent,
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AngularFireModule.initializeApp(firebaseConfig),
RouterModule.forRoot(routes),
SortOnLikePipe
],
providers: [AF],
bootstrap: [AppComponent],
})
export class AppModule { }
in your test, inside beforeEach block. You need to add the following to TestBed.configureTestingModule
All used pipes, components and directives have to be declared. in your case: SortOnLikePipe and EditMessageModalComponent
all used modules have to be imported. in your case: FormsModule
all needed services have to be provided
Here are the ones you are missing:
I'd imagine you might be missing more..
TestBed.configureTestingModule({
declarations: [ DashboardComponent, SortOnLikePipe, EditMessageModalComponent ],
imports:[FormsModule]
providers: [
{ provide: AF, useClass: MockAF },
{ provide: Router, useValue: routerStub },
],
})

Error in Angular 4.x Karma test "No Provider for FocusTrapFactory"

I have a small Angular v4.x using Angluar Material 2.x
It has a modal (using MdDialog) login component - and pretty much nothing else.
All my tests are failing with:
Failed: No provider for FocusTrapFactory! at injectionError
(http://localhost:9876/base/src/test.ts?31c6eb17e2414560f8e07e35e9c56bebb408ba58:2074:86)
[angular]
at noProviderError (http://localhost:9876/base/src/test.ts?31c6eb17e2414560f8e07e35e9c56bebb408ba58:2112:12)
[angular] ...
my login.component.spec.ts is
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { FormsModule } from '#angular/forms';
import { RouterTestingModule } from '#angular/router/testing';
import { BrowserDynamicTestingModule } from '#angular/platform-browser-dynamic/testing';
import { NoopAnimationsModule } from '#angular/platform-browser/animations';
import { MdDialog, MdDialogContainer, MdInputContainer, OVERLAY_PROVIDERS } from '#angular/material';
import { HttpModule } from '#angular/http';
import { AuthenticationService } from '../shared/servicesIndex';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
entryComponents: [MdDialogContainer]
}
});
TestBed.configureTestingModule(
{
imports: [
FormsModule,
RouterTestingModule,
HttpModule,
NoopAnimationsModule
],
declarations: [
LoginComponent,
MdInputContainer,
MdDialogContainer
],
providers: [
MdDialog,
OVERLAY_PROVIDERS,
AuthenticationService
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Logic tells me to import FocusTrapFactory and add it to my list of providers - but I can't find it to import it!
I'm at a loss. My Google-fu is fu.
Just needed to add import { MaterialModule } from '#angular/material'; then add MaterialModule to the imports array in my app.component.spec.ts.
You should add the right packages for the material components. Unfortunately MaterialModule is marked depricated in the latest release.
The best way to replace this is to make your own Module that imports (and exports) only the modules that are actually used in your application.
I managed to fix this issue by creating this class:
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { MdMenuModule, MdIconModule, MdRippleModule, MdToolbarModule, MdSidenavModule, MdButtonModule } from '#angular/material'
// Add animations
import { BrowserAnimationsModule } from '#angular/platform-browser/animations'
const MATERIAL_MODULES = [
MdMenuModule,
MdIconModule,
MdRippleModule,
MdToolbarModule,
MdSidenavModule,
MdButtonModule,
BrowserAnimationsModule
];
#NgModule({
imports: MATERIAL_MODULES,
exports: MATERIAL_MODULES
})
export class AngularMaterialModule { }
This module should be included in your own app.module
Good luck!

Angular2 - Platforms have to be created via `createPlatform`

I have a test in Angular2 where I am including the ROUTER_PROVIDERS and I get this error message :
"ORIGINAL EXCEPTION: Platforms have to be created via createPlatform!"
First I was getting a "No provider for Router" error, and after this I included the ROUTER_PROVIDERS inside a beforeEachProvider call. that brought this issue up which I dont know how to figure out what it relates to.
My test looks like this currently :
import {TestComponentBuilder, ComponentFixture} from '#angular/compiler/testing';
import {async, it, describe, expect, beforeEach, beforeEachProviders, inject} from '#angular/core/testing';
import {Component} from '#angular/core';
import {By} from '#angular/platform-browser';
import {HeaderComponent} from './header.component';
import {LocationStrategy, HashLocationStrategy} from '#angular/common';
import {ROUTER_PROVIDERS} from '#angular/router-deprecated';
describe('Header component', () => {
// Setup the dependencies for this test
beforeEachProviders(() => [
ROUTER_PROVIDERS
]);
#Component({
selector: 'test-header',
template: `<et-header [username]="username" [user_image_url]="user_image_url"></et-header>`,
directives: [HeaderComponent]
})
class TestHeader {
username: string = "Header test";
user_image_url: string = "https://placeholdit.imgix.net/~text?txtsize=33&txt=350%C3%97150&w=350&h=150";
}
it('should sent font-size to x-large', async(inject([TestComponentBuilder], (tcb) => {
return tcb.overrideTemplate(TestHeader, '<et-header [username]="username" [user_image_url]="user_image_url"></et-header>')
.createAsync(TestHeader).then((fixture: any) => {
fixture.detectChanges();
let headerDebugElement = fixture.debugElement.query(By.css('header'));
//expect(headerDebugElement.nativeElement.classList.contains('header')).toBe(true);
expect(true).toBe(true);
});
})));
});
Thank you in advance.
Joao Garin
FOR ROUTER RELEASE < RC3
Finally, found a solution to this problem.
When running Unit Tests you need to use
import {ROUTER_FAKE_PROVIDERS} from '#angular/router/testing';
to supply the provider for the Router.
FOR ROUTER RELEASE 3.0.0-alpha.7 (RC3)
import {ActivatedRoute, Router} from '#angular/router';
class MockRouter { createUrlTree() {} }
class MockActivatedRoute { }
beforeEachProviders(() => [
provide(Router, { useClass: MockRouter }),
provide(ActivatedRoute, { useClass: MockActivatedRoute })
]);
this will provide the router for your tests
NOTE: Angular 2 Router now has its own release cycle separate from the rest of the Angular 2 Packages and hence its version numbering is different
Here's a workaround for RC4 to mock the [routerLink] directive when testing a component.
This will break in the next release given the changes in the master branch.
import { provide } from '#angular/core';
import { LocationStrategy } from '#angular/common';
import { Router, ActivatedRoute } from '#angular/router';
import { addProviders } from '#angular/core/testing';
import { SpyLocation } from '#angular/common/testing';
class MockRouter {
createUrlTree() {}
navigateByUrl() {}
navigate() {}
}
class MockActivatedRoute { }
beforeEach(() => {
addProviders([
{provide : Router useClass: MockRouter },
{provide : ActivatedRoute, useClass: MockActivatedRoute },
{provide:LocationStrategy, useClass: SpyLocation }
]);
});
I get this error because of ...
[routerLink] in my HTML.
The correct fix for latest router on RC4 is ...
beforeEach(() => addProviders([
APP_ROUTER_PROVIDERS, // must be first
{provide: APP_BASE_HREF, useValue: '/'}, // must be second
{provide: ActivatedRoute, useClass: Mock},
{provide: Router, useClass: Mock}
]));
A github project with this approach is ...
https://github.com/danday74/angular2-coverage
Note that this allows you to use ...
<a [routerLink]="['/route1']">Route 1</a>
But will fail for ...
<a [routerLink]="['/route1']" [routerLinkActive]="'active'">Route 1</a>