I have a component which uses Mat paginator for pagination purpose, the component is working fine but while running the command npx jest -- layout.component.spec.ts
(I am are using jest framework)the below test case is getting failed with below is the error.
Test Case getting failed :
it("should create", () => {
expect(component).toBeTruthy();
});
ERROR:
should create (1070ms)
● LayoutComponent › should create
TypeError: Cannot read property 'page' of undefined
at LayoutComponent.ngAfterViewInit (src/app/modules/adverse-media/card-layout/card-layout.component.ts:2015:20)
at ngAfterViewInit (node_modules/@angular/core/bundles/core.umd.js:21557:22)
at callProviderLifecycles (node_modules/@angular/core/bundles/core.umd.js:21531:17)
at callElementProvidersLifecycles (node_modules/@angular/core/bundles/core.umd.js:21521:33)
at callLifecycleHooksChildrenFirst (node_modules/@angular/core/bundles/core.umd.js:29563:9)
at apply (node_modules/@angular/core/bundles/core.umd.js:30424:29)
at Object.callWithDebugContext [as checkAndUpdateView] (node_modules/@angular/core/bundles/core.umd.js:30126:16) at ViewRef_.checkAndUpdateView [as detectChanges] (node_modules/@angular/core/bundles/core.umd.js:20829:26)
at ComponentFixture._tick (../../packages/core/testing/src/component_fixture.ts:107:28)
at ../../packages/core/testing/src/component_fixture.ts:120:36
at ZoneDelegate.apply [as invoke] (node_modules/zone.js/dist/zone.js:391:26)
at ProxyZoneSpec.invoke [as onInvoke] (node_modules/zone.js/dist/proxy.js:129:39)
at ZoneDelegate.onInvoke [as invoke] (node_modules/zone.js/dist/zone.js:390:52)
at Object.invoke [as onInvoke] (node_modules/@angular/core/bundles/core.umd.js:26371:37)
at ZoneDelegate.onInvoke [as invoke] (node_modules/zone.js/dist/zone.js:390:52)
at Zone.invoke [as run] (node_modules/zone.js/dist/zone.js:150:43)
at NgZone.run (node_modules/@angular/core/bundles/core.umd.js:26285:32)
at ComponentFixture.detectChanges (../../packages/core/testing/src/component_fixture.ts:120:19)
at src/app/modules/adverse-media/card-layout/card-layout.component.spec.ts:55:13
at ZoneDelegate.apply [as invoke] (node_modules/zone.js/dist/zone.js:391:26)
at ProxyZoneSpec.invoke [as onInvoke] (node_modules/zone.js/dist/proxy.js:129:39)
at ZoneDelegate.onInvoke [as invoke] (node_modules/zone.js/dist/zone.js:390:52)
at Zone.invoke [as run] (node_modules/zone.js/dist/zone.js:150:43)
Component Code / usage of paginator:
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
ngAfterViewInit(): void {
this.paginator.page.subscribe(() => {
this.characterDatabase
.getCharacters("", "", this.paginator.pageIndex)
.subscribe((response: HttpRequest) => {
this.characterDataSource = new MatTableDataSource(
response.results as any[]
);
this.resultsLength = response.info.count;
this.characters$ = this.characterDataSource.connect();
});
});
}
Definition of page being used in the ngAfterViewInit here this.paginator.page.subscribe
paginator.d.ts
/** Event emitted when the paginator changes the page size or page index. */
readonly page: EventEmitter<PageEvent>;
Spec.ts File Code :-
import { HttpClientTestingModule } from "@angular/common/http/testing";
import { ViewChild } from "@angular/core";
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import {
MatCardModule,
MatChipsModule,
MatIconModule,
MatPaginator,
MatPaginatorModule,
MatSortModule,
MatTableModule,
PageEvent,
} from "@angular/material";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { PipesModule } from "@app/shared/pipes/pipes.module";
import { LayoutComponent } from "./card-layout.component";
describe("LayoutComponent", () => {
let component: LayoutComponent;
let fixture: ComponentFixture<LayoutComponent>;
//let someServiceMock;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LayoutComponent],
imports: [
PipesModule,
PipesModule,
MatCardModule,
MatChipsModule,
MatPaginatorModule,
MatIconModule,
HttpClientTestingModule,
MatTableModule,
MatSortModule,
BrowserAnimationsModule,
],
providers: [{ provide: MatPaginator, useValue: MatPaginator }],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it("should create", () => {
expect(component).toBeTruthy();
});
});
What I have tried till now to mock that read only page event emitter :-
//let someServiceMock;
//const paginator = jasmine.createSpyObj('ChildComponent', ['childMethod']);
//someServiceMock = {testEmitter: {subscribe: spyOn('testEmitter subscribe')}};
fixture = TestBed.createComponent(CardLayoutComponent);
component = fixture.componentInstance;
//Object.defineProperty(component.paginator.page, PageEvent , Writable);
//spyOn(component.paginator.page, "subscribe").and.returnValue("");
//spyOn(component.paginator, 'page', 'subscribe').and.returnValue(1);
//expect(component.paginator.page.emit).toReturn();
fixture.detectChanges();
it("should create", () => {
//spyOn(component.paginator.page, "subscribe").and.returnValue("");
expect(component).toBeTruthy();
});
something that I have tried from my end, I was not able to post everything what I tried but a help is appreciated as I am much new to jest, i am not able to figure this out. Please let me know how to mock/define this item so that test should pass. sorry for typos
The line below is incorrect, it is doing nothing:
providers: [{ provide: MatPaginator, useValue: MatPaginator }],
What I think is happening, is that the pagination component (@ViewChild()
) is being blocked by an *ngIf
and therefore this.paginator
does not exist in the ngAfterViewInit
hook.
For a super quick unblock, try this:
ngAfterViewInit(): void {
if (this.paginator) {
// !! the rest of the logic here
}
}
You should also try to find out why this.paginator
is undefined (why MatPaginator is not in the HTML) when you unit test.