Angular component's test fails with timeout - javascript

I am trying to test my component. The components receives an observable string and object as an input and iterates over this object and makes some calculations.
The component works fine in a browser, but throws an error when I execute the unit test:
Chrome 65.0.3325 (Mac OS X 10.13.1) ERROR
Disconnected, because no message in 10000 ms.
Chrome 65.0.3325 (Mac OS X 10.13.1): Executed 1 of 116 DISCONNECTED (10.136 secs / 0.092 secs)
Chrome 65.0.3325 (Mac OS X 10.13.1) ERROR
Chrome 65.0.3325 (Mac OS X 10.13.1): Executed 1 of 116 DISCONNECTED (10.136 secs / 0.092 secs)
So i’ve started simplifying the component but still don't understand why the tests are failing.
My component's template:
<div *ngIf="hasDestinationFilter"></div>
<div *ngFor="let shipment of (filteredShipments$ | async)"></div>
TypeScript:
import { Component, Input, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/combineLatest';
import 'rxjs/add/operator/map';
import { ShipmentFilterService } from '../../services/shipment-filter/shipment-filter.service';
#Component({
selector: 'search-results',
templateUrl: './search-results.component.html',
})
export class SearchResultsComponent implements OnInit {
#Input() searchResults$: Observable<any>;
#Input() destination$: Observable<string>;
filteredShipments$: Observable<any[]>;
hasDestinationFilter = false;
constructor(private shipmentFilterService: ShipmentFilterService) {}
ngOnInit() {
this.filteredShipments$ = Observable
.combineLatest(this.searchResults$, this.destination$)
.map(([ { shipments }, destination ]) => {
this.hasDestinationFilter = this.shipmentFilterService.hasDestinationFilter(shipments);
if (this.hasDestinationFilter) {
return shipments;
}
return shipments;
});
}
}
My unit test:
import { NO_ERRORS_SCHEMA } from '#angular/core';
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { ShipmentFilterService } from '../../services/shipment-filter/shipment-filter.service';
import { SearchResultsComponent } from './search-results.component';
describe('SearchResultsComponent', () => {
let mockedHasDestinationFilter;
const mockedShipment = {
collectedDate: null,
collectionCity: null,
collectionDate: null,
consignmentKey: null,
consignmentNumber: null,
customerReference: null,
deliveryDueDate: null,
deliveryTown: null,
destinationCountry: null,
fedexNumber: null,
fromDate: null,
originCountry: null,
pieceQuantity: null,
podFound: null,
revisedDeliveryDate: null,
shipmentId: null,
signatory: null,
signatoryFound: false,
statusData: [],
toDate: null,
trackId: null,
};
const shipmentFilterServiceStub = {
hasDestinationFilter: () => mockedHasDestinationFilter,
};
beforeEach(async(() => {
TestBed
.configureTestingModule({
declarations: [
SearchResultsComponent,
],
providers: [
{ provide: ShipmentFilterService, useValue: shipmentFilterServiceStub },
],
schemas: [
NO_ERRORS_SCHEMA,
],
})
.compileComponents();
}));
function getComponent(destination = null) {
let component: SearchResultsComponent;
let fixture: ComponentFixture<SearchResultsComponent>;
fixture = TestBed.createComponent(SearchResultsComponent);
component = fixture.componentInstance;
component.searchResults$ = Observable.of({
query: {
input: 'abc123',
},
shipments: [
mockedShipment,
mockedShipment,
mockedShipment,
mockedShipment,
mockedShipment,
],
});
component.destination$ = Observable.of(destination);
fixture.detectChanges();
return fixture;
}
describe('optionally filters shipments by destination', () => {
it('shows all shipments when there is no destination filter', () => {
mockedHasDestinationFilter = false;
const component = getComponent();
expect(component.debugElement.queryAll(By.css('pb-shipment')).length).toBe(5);
});
fit('shows all shipments when there is destination filter, but no destination has been chosen', () => {
mockedHasDestinationFilter = true;
const component = getComponent('');
});
});
});
It's hard to understand where the problem since unit tests fails with timeout, but I noticed if I remove <div *ngIf="hasDestinationFilter"></div> or <div *ngFor="let shipment of (filteredShipments$ | async)"></div> from my templates the tests doesn't fail with timeout. Where can be my mistake?

Try to initialize the values of your inputs with default values
#Input() searchResults$: Observable<any> = Observable.from("");
#Input() destination$: Observable<string> = Observable.from("");

Related

Cannot read properties of null (reading 'nativeElement') - Test Angular

I'm new to Angular and I'm trying to perform a test... I want to test my function in component.ts that receives an event through click, and this value passes to an output for the component dad.
The test code looks like this...
import { ComponentFixture, TestBed } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { QuadradosComponent } from './quadrados.component';
describe('QuadradosComponent', () => {
let component: QuadradosComponent;
let fixture: ComponentFixture<QuadradosComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ QuadradosComponent ]
})
.compileComponents();
fixture = TestBed.createComponent(QuadradosComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Deve emitir mensagem quando clicar na div', () => {
const emitMessageSpy = spyOn(component.eventoOutput, 'emit')
let divClick = fixture.debugElement.query(By.css('.casa')).nativeElement
divClick.click()
expect(emitMessageSpy).toHaveBeenCalled
})
});
The component.ts
import { Component, EventEmitter, Input, Output, OnInit} from '#angular/core';
import { JogadasService } from 'src/app/services/jogadas.service';
#Component({
selector: 'app-quadrados',
templateUrl: './quadrados.component.html',
styleUrls: ['./quadrados.component.css']
})
export class QuadradosComponent implements OnInit{
constructor(public service: JogadasService) {}
ngOnInit(): void {
this.service.emitirVitoria.subscribe(
array => this.vitoria.push(...array)
)
console.log(document.getElementsByClassName('casa'))
}
vitoria: number[] =[];
posicoes: number = 0;
#Input() quadrados?: Array<number>;
#Output() eventoOutput = new EventEmitter();
#Output() index = new EventEmitter();
eventoClick(evento: MouseEvent) {
this.eventoOutput.emit(evento);
console.log(document.getElementsByClassName('casa'))
}
pegaIndex(index: number) {
this.posicoes = index;
this.index.emit(this.posicoes);
}
vitoriaArray(modelo: number) {
for(let i = 0; i< this.vitoria.length; i++) {
if(this.vitoria[i] == modelo) {
return this.vitoria[i]
}
}
}
}
the component HTML:
<div class="centraliza">
<div class="jogo-da-velha">
<div class="linha">
<div class="casa" *ngFor="let quadrado of quadrados, let i = index"
(click)="pegaIndex(i)"
[ngClass]="i == vitoriaArray(i) ? 'casa-vencedor' : null"
(click)="eventoClick($event)" ></div>
</div>
</div>
</div>
I'm using Angular/Cli in version 15, I'm not getting a good understanding of the tests in Angular, thanks for the help.
div.casa is being rendered with an *ngFor and you have to make sure quadrados is a non empty array and not undefined.
Try this:
it('Deve emitir mensagem quando clicar na div', () => {
const emitMessageSpy = spyOn(component.eventoOutput, 'emit')
// Mock quadrados here
component.quadrados = [{ } as any];
// detect the changes here since the view model changed
fixture.detectChanges();
// the div should be there now
let divClick = fixture.debugElement.query(By.css('.casa')).nativeElement
divClick.click()
expect(emitMessageSpy).toHaveBeenCalled
})
Here is a good resource on learning testing with Angular: https://testing-angular.com/.

fetching Data to ng-bootstrap table in angular 11

Hi Every one here
I face problem with fetching data to array but when I put data to array the editor said not defined array
Error Message:
Failed to compile.
src/app/customers/customers-list/customers-list.component.ts:111:14 - error TS2551: Property 'CUSTOMERS' does not exist on type 'CustomersListComponent'. Did you mean 'customers$'?
111 this.CUSTOMERS = posts;
~~~~~~~~~
src/app/customers/customers-list/customers-list.component.ts:64:3
64 customers$: Observable<Customer[]>;
~~~~~~~~~~
'customers$' is declared here.
This is the CODE
import {
Component,
OnInit,
PipeTransform, // table
} from '#angular/core';
import { DecimalPipe } from '#angular/common'; // table
import { FormControl } from '#angular/forms'; // table
import { Observable } from 'rxjs'; // table
import { map, startWith } from 'rxjs/operators'; // table
import {NgbModal} from '#ng-bootstrap/ng-bootstrap'; // modal
import {AddCustomerComponent} from '../add-customer/add-customer.component'; // modal
import { faFolderPlus, faPencilAlt, faTrashAlt } from '#fortawesome/free-solid-svg-icons'; // fontawsome icons
import {HttpClient} from '#angular/common/http';
// table
interface Customer {
id: number;
name: string;
company: string;
remaining: number;
email: string;
mobile: number;
whats_up: number;
}
let CUSTOMERS: Customer[] = [
{
id: 12,
name: 'jack',
company: 'SDTE',
remaining: 580,
email: 'test#test.com',
mobile: +456456456456,
whats_up: +456456456
}
];
function search(text: string, pipe: PipeTransform): Customer[] {
return CUSTOMERS.filter(customer => {
const term = text.toLowerCase();
return customer.name.toLowerCase().includes(term)
|| customer.company.toLowerCase().includes(term)
|| pipe.transform(customer.remaining).includes(term)
|| customer.email.toLowerCase().includes(term)
|| pipe.transform(customer.mobile).includes(term)
|| pipe.transform(customer.whats_up).includes(term);
});
}
#Component({
selector: 'app-customers-list',
templateUrl: './customers-list.component.html',
styleUrls: ['./customers-list.component.css'],
providers: [DecimalPipe] // table
})
export class CustomersListComponent implements OnInit {
// table
customers$: Observable<Customer[]>;
filter = new FormControl('');
faFolderPlus = faFolderPlus;
faPencilAlt = faPencilAlt;
faTrashAlt = faTrashAlt;
constructor(
pipe: DecimalPipe, // table
private modalService: NgbModal, // modal
private http: HttpClient // Get All Data
) {
// table
this.customers$ = this.filter.valueChanges.pipe(
startWith(''),
map(text => search(text, pipe))
);
}
ngOnInit(): void {
this.getAllData();
}
// modal
openPopupModal() {
const modalRef = this.modalService.open(AddCustomerComponent,{ centered: true, size: 'lg' });
modalRef.componentInstance.name = 'World';
}
private getAllData() {
this.http
.get('http://localhost:3000/customers')
.subscribe(
posts => {
console.log('GET all Data works');
this.CUSTOMERS = posts; // <<<<< Here is the problem ************ How can I to Fix it.
});
}
}
I
this.CUSTOMERS = posts; this refers to current class CustomersListComponent but your variable is outside the class so you need to assign directly CUSTOMERS = posts; :)
You need to specify the return type.
You could try using this:
private getAllData() {
this.http
.get<Customer[]>('http://localhost:3000/customers') // <<<<< Try using this.
.subscribe(
posts => {
console.log('GET all Data works');
CUSTOMERS = posts;
});
}

Testing Observable Callback to Change Object State

I have a service that does the following:
updateProperties(properties: any) {
return this.http.put(environment.adminApiURLPrefix+'api/v1/properties', properties);
}
In my component I have two objects, providers and providerProperties. Providers contains a key value pair of API providers and their status, whilst providerProperties is the actual response that is returned back from the API (has to be stored as any subsequent requests need the entire object).
import { Component, OnInit } from '#angular/core';
import { ProviderService } from '../../services/provider.service';
import { Status } from 'src/app/models/status.enum';
import { AlertService } from 'src/app/services/alert.service';
#Component({
selector: 'app-providers',
templateUrl: './providers.component.html',
styleUrls: ['./providers.component.scss']
})
export class ProvidersComponent implements OnInit {
providers: any;
providerProperties: any;
status: Status;
constructor(private providerService: ProviderService, private alertService: AlertService) {
this.providers = {
'API1': false,
'API2': false,
'API3': false
}
this.status = Status.Loading;
}
ngOnInit() {
this.providerService.getProperties().subscribe((response: any) => {
this.setProviderValues(response);
this.status = Status.Ready;
},
error => {
this.alertService.error('Error');
this.status = Status.Error;
})
}
setProviderValues(response: any) {
this.providerProperties = response;
Object.keys(this.providers).forEach(key => {
let providerStatus: string = this.providerProperties[key.toLowerCase() + '.enabled'];
if(providerStatus == 'true' || providerStatus == 'false') {
this.providers[key] = providerStatus == 'true' ? true : false;
} else {
this.alertService.error('Error.');
this.status = Status.Error;
return;
}
});
}
changeProviderProperty(provider: string, isEnabled: boolean) {
let providerKey: string = this.providerDisplayNameToPropertyKey(provider);
let tempProviderProperties = Object.assign({}, this.providerProperties);
tempProviderProperties[providerKey] = isEnabled ? true : false;
this.providerService.updateProperties(tempProviderProperties).subscribe(
response => {
this.setProviderValues(response);
this.alertService.success('Successfully '+ (isEnabled ? 'Enabled ' : 'Disabled ') + provider);
},
err => {
if(err['status'] == 0) {
this.alertService.error('Error');
} else {
let errorArray = err['error']['Errors']['Error'];
for(let errorKey in errorArray) {
let errorValue = errorArray[errorKey];
this.alertService.error('Error Updating Properties: ' + errorValue['ReasonCode'] + ' ' + errorValue['Description']);
}
}
}
)
}
providerDisplayNameToPropertyKey(provider: string) {
return provider.toLowerCase() + '.enabled';
}
}
I have the following test:
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '#angular/core/testing';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { ProvidersComponent } from './providers.component';
import { ProviderService } from 'src/app/services/provider.service';
import { of, Observable, throwError } from 'rxjs';
import { AlertComponent } from '../shared/alert/alert.component';
import { RouterTestingModule } from '#angular/router/testing';
import { AlertService } from 'src/app/services/alert.service';
import { HttpErrorResponse } from '#angular/common/http';
describe('ProvidersComponent', () => {
let component: ProvidersComponent;
let providerService: ProviderService;
let alertService: AlertService;
let fixture: ComponentFixture<ProvidersComponent>;
let getPropertiesSpy: jasmine.Spy<() => Observable<String>>;
let changeProviderPropertySpy: jasmine.Spy<(provider: string, isEnabled: boolean) => Promise<void>>;
let providerProperties:any;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
ProvidersComponent,
AlertComponent
],
imports: [
HttpClientTestingModule,
RouterTestingModule
],
providers: [
ProviderService,
AlertService
]
})
.compileComponents();
}));
beforeEach(() => {
providerService = TestBed.get(ProviderService);
alertService = TestBed.get(AlertService);
fixture = TestBed.createComponent(ProvidersComponent);
providerProperties = {
'api1.enabled': 'true',
'api2.enabled': 'true',
'api3.enabled': 'false',
};
component = fixture.componentInstance;
component.providers = {
'API1': false,
'API2': false,
'API3': false
};
getPropertiesSpy = spyOn(providerService, 'getProperties').and.callFake(() => {
return of(providerProperties);
});
changeProviderPropertySpy = spyOn(component, 'changeProviderProperty');
fixture.detectChanges();
});
it('should enable provider', fakeAsync(() => {
spyOn(providerService, 'updateProperties').and.returnValue(of({
'api1.enabled': 'true',
'api2.enabled': 'false',
'api3.enabled': 'false'
}));
component.changeProviderProperty('API1', true);
fixture.detectChanges();
expect(component.providers['API1']).toEqual(true);
}));
});
For some reason the state of the objects won't change. I'm fairly sure the observable isn't being subscribed to or I am not waiting for the response.
Update
After spying on the provider service and checking to see if it had been called after I call changeProviderProperties, it turns out that it was never called.
Update 2
Spied on the providerDisplayNameToPropertyKey method and found that it's not being called for whatever reason.
The method component.changeProviderProperty contains asynchronous code. Because you're aware of that, you're running your test in the fakeAsync zone. What you're missing is a call to tick that simulates the asynchronous passage of time for the timers in the fakeAsync zone.
it('should enable provider', fakeAsync(() => {
...
component.changeProviderProperty('API1', true);
tick();
...
}));

How to inject Input() in unit testing?

So i try to do some unit testing on a component. But I have some problems with Input() parameter. And then especially a component in the component
SO I have this component:
export class EcheqDisplayComponent implements OnInit {
echeq: EcheqSubmissionApi;
constructor(
private route: ActivatedRoute
) {
this.echeq = this.route.snapshot.data['submission'];
}
ngOnInit() {
}
getAnswers(page: EcheqPageApi): any[] {
return page.elements.map(element => this.echeq.answers[element.name]);
}
}
and the template:
<div class="echeq-display" *ngIf="echeq">
<header class="echeq-display-header header">
<div class="echeq-display-info">
<h1 class="heading echeq-display-heading">
{{ echeq.definition.title }}
</h1>
<div class="sub-heading echeq-display-subheading">
<span class="echeq-display-creator">
Toegekend door:
{{ echeq.assignedByProfName ? echeq.assignedByProfName : 'Het Systeem' }}
</span>
<span class="echeq-display-date">{{
echeq.definition.createdOnUtc | date: 'dd MMM'
}}</span>
</div>
</div>
<app-meta-box
[metadata]="{
numPages: echeq.definition.numPages,
vPoints: echeq.definition.awardedVPoints
}"
></app-meta-box>
</header>
<main class="echeq-display-questions body">
<app-echeq-question
*ngFor="let page of echeq.definition.pages; let i = index"
[page]="page"
[readonly]="true"
[number]="i + 1"
[answers]="getAnswers(page)"
></app-echeq-question>
</main>
</div>
and the unit test:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { EcheqDisplayComponent } from './echeq-display.component';
import { ParticipantEcheqModule } from '../../participant-echeq.module';
import { RouterModule, ActivatedRoute } from '#angular/router';
import { MockActivatedRoute } from 'src/app/shared/mocks/MockActivatedRoute';
import { MetaData } from '../meta-box/meta-box.component';
describe('EcheqDisplayComponent', () => {
let component: EcheqDisplayComponent;
let fixture: ComponentFixture<EcheqDisplayComponent>;
const metaData: MetaData = new MetaData();
// const metaDataInfo = fixture.debugElement.componentInstance;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ],
providers: [
{ provide: ActivatedRoute, useClass: MockActivatedRoute }
],
imports:[
ParticipantEcheqModule
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EcheqDisplayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fit('should create component', () => {
metaData.numPages = 20;
expect(component).toBeTruthy();
});
});
But it stay saying:
TypeError: Cannot read property 'numPages' of undefined.
So what I have to change that it is working?
Thank you
So the MetaData is from the app-meta-box component.it looks like this:
export class MetaData {
numPages: number;
vPoints: number;
}
#Component({
selector: 'app-meta-box',
templateUrl: './meta-box.component.html',
styleUrls: ['./meta-box.component.scss']
})
export class MetaBoxComponent implements OnInit {
#Input() metadata: MetaData;
constructor() {}
ngOnInit() {}
}
This is the mock class:
export class MockActivatedRoute {
public snapshot = {
data: {
submission: {
answers: {}
}
}
};
}
I have it now like this:
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { EcheqDisplayComponent } from './echeq-display.component';
import { ParticipantEcheqModule } from '../../participant-echeq.module';
import { RouterModule, ActivatedRoute } from '#angular/router';
import { MockActivatedRoute } from 'src/app/shared/mocks/MockActivatedRoute';
import { MetaData } from '../meta-box/meta-box.component';
describe('EcheqDisplayComponent', () => {
let component: EcheqDisplayComponent;
let fixture: ComponentFixture<EcheqDisplayComponent>;
const metaData: MetaData = new MetaData();
// const metaDataInfo = fixture.debugElement.componentInstance;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ],
providers: [
{ provide: ActivatedRoute, useValue: new MockActivatedRoute().withData({submission:{ answers:{} } }) }
],
imports:[
ParticipantEcheqModule
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EcheqDisplayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
fit('should create component', () => {
component.echeq = {
definition: {
title: 'test title',
awardedVPoints: 0,
numPages: 9
}
}
expect(component).toBeTruthy();
});
});
But then I get this error:
Property 'answers' is missing in type '{ definition: { title: string; awardedVPoints: number; numPages: number; }; }' but required in type 'EcheqSubmissionApi'.ts(2741)
echeqSubmissionApi.ts(59, 5): 'answers' is declared here.
I have it now like this:
fit('should create component', () => {
component.echeq = {
definition: {
title: 'test title',
awardedVPoints: 0,
numPages:9
}
} as EcheqSubmissionApi;
expect(component).toBeTruthy();
});
export interface EcheqSubmissionApi {
/**
* Primary key of this submission (server set).
*/
id?: string;
definition?: EcheqDefinitionApi;
/**
* Id of the prof who assigned this eCheq (server set).
*/
assignedByProfId?: string;
/**
* Name of the prof who assigned this eCheq (server set). Only set on get operations. May be null if assigned by the system.
*/
assignedByProfName?: string;
/**
* Id of the organisation who assigned this eCheq (server set).
*/
assignedByOrgId?: number;
/**
* Participant ID of the patient this eCheq is assigned to (server set).
*/
assignedToId?: string;
/**
* When this submission was assigned (UTC, server set).
*/
assignedOnUtc?: Date;
/**
* If set until when the eCheq can be submitted.
*/
validUntilUtc?: Date;
/**
* Whether the eCheq has been started and whether it has been submitted (server set).
*/
status?: EcheqSubmissionApi.StatusEnum;
/**
* When this submission was completed (UTC, server set).
*/
submittedOnUtc?: Date;
/**
* Answers of form. In the form of a json object with name and value of the questions { \"nameOfQuestion\" : \"valueOfQuestion\" }
*/
answers: object;
/**
* initialValues of form. In the form of a json object with name and value of variables { \"nameOfQuestion\" : \"valueOfQuestion\" }
*/
initialValues?: object;
/**
* The page the participant is currently on
*/
currentPage?: number;
/**
* The progress of the echeq in percentage
*/
progress?: number;
}
export namespace EcheqSubmissionApi {
export type StatusEnum = 'New' | 'Active' | 'Submitted';
export const StatusEnum = {
New: 'New' as StatusEnum,
Active: 'Active' as StatusEnum,
Submitted: 'Submitted' as StatusEnum
};
}
But if I run the unit test, I still get this error:
TypeError: Cannot read property 'numPages' of undefined
To answer your question:
component.metadata = /// whatever you want this to be
You have your error with numPages: echeq.definition.numPages. Endeed, echeq is also undefined.
You can try :
component.echeq = {
definition: {
numPages: 9
}
}
Or better way would be to returns this value from this.route.snapshot.data['submission']; so from MockActivatedRoute
UPDATE:
And update MockActivatedRoute to allows dynamic parameters:
export class MockActivatedRoute {
snapshot = {
data: {}
};
constructor(){}
withData(data:any): MockActivatedRoute {
this.snapchot.data = data;
return this;
}
}
So now in your test, you can use it :
{ provide: ActivatedRoute, useValue: new MockActivatedRoute().withData({submission:{ answers:{} } }) }
No problem. It was an easy:
beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
],
imports:[
ParticipantEcheqModule,
RouterTestingModule
]
})
.compileComponents();
}));

Testing Kendo Grid in Angular

I'm trying to get my Karma tests working with the kendo grid in a brand new Angular project. The specs for this component look like
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { RateTableProviderService } from "../_services/rate-table-provider.service";
import { RateTableComponent } from './rate-table.component';
import { Observable } from 'rxjs/Rx';
import { Response, ResponseOptions } from "#angular/http";
import { IRateTableListViewModel } from "./models/IRateTableListViewModel";
import { GridModule } from '#progress/kendo-angular-grid';
describe('RateTableComponent', () => {
let component: RateTableComponent;
let fixture: ComponentFixture<RateTableComponent>;
let observableSource = [
{
id: "3482cd2f-16f8-4d62-8d5d-d761de35e737",
name: "Rate table 1",
enabled: false,
effectiveDate: new Date(),
creditTierSetName: "",
loanProducts: ""
},
{
id: "3482cd2f-16f8-4d62-8d5d-d761de35e739",
name: "Another rate table",
enabled: false,
effectiveDate: new Date(),
creditTierSetName: "",
loanProducts: ""
}
];
class MockRateTableProviderService extends RateTableProviderService {
constructor() {
super(null);
}
getRateTableData(): Observable<Array<IRateTableListViewModel>> {
return Observable.from([observableSource]);
}
}
beforeEach(async(() => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RateTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
The tests run fine against chrome but in PhantomJS the tests hang.
PhantomJS 2.1.1 (Windows 8 0.0.0) RateTableComponent should be created FAILED
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Everything works fine when running the tests against Chrome. I suspect that I'm not managing the asynchronous import when setting up the test bed correctly. If I pull out the kendo completely then the tests complete. I tried passing in a done call as part of the beforeEach but that also didn't work
beforeEach(async((done) => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents().then(done);
}));
Have you tried defining your fixture and component in the resolve of .compileComponents() as this will then be executed async as well as the setup of your testbed?
`.compileComponents.()
.then(() => {
fixture = TestBed.createComponent(RateTableComponent);
component = fixture.componentInstance;
});`
Try adding a second before each like so:
beforeEach(async(() => {
let mockRateTableProviderService = new MockRateTableProviderService();
TestBed.configureTestingModule({
declarations: [RateTableComponent],
providers: [
{ provide: RateTableProviderService, useValue: mockRateTableProviderService }
],
imports: [GridModule]
})
.compileComponents().then(done);
}));
beforeEach(() => {
done();
}));
not as elegant as my main man simons answer above but should do the trick

Categories