import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Component, ViewChild, Type, Provider } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ThemePalette } from '@angular/material/core'; import { MatPaginatorModule, MatPaginator, MatPaginatorIntl } from '@angular/material/paginator'; import { PaginatorDirective } from './pagination.directive'; describe('Test custom paginator directive', () => { let fixture: ComponentFixture<MatPaginatorApp>; let component: MatPaginatorApp; let paginatorElement: HTMLElement; beforeEach(() => { fixture = createComponent(MatPaginatorApp); component = fixture.componentInstance; paginatorElement = getPaginatorElement(fixture); }); it('Should show the right range numbers', () => { const rangeElement = getRangeLabelElement(fixture); component.length = 100; component.pageSize = 10; component.pageIndex = 1; fixture.detectChanges(); expect(rangeElement.innerHTML.trim()).toBe('Showing 11 – 20 of 100 records'); }); it('Should remove the two dotted skip buttons when total pages is less than 6', () => { component.length = 60; component.pageSize = 10; component.pageIndex = 1; fixture.detectChanges(); const dottedButtons = getElementsByText(paginatorElement, 'button', '•••'); // Search for dotted skip buttons expect(dottedButtons.length).toEqual(0); // No dotted button elements should be present }); it('Should disable the previous button and button [1] must be highlighted and disabled when current page is 1', () => { component.length = 100; component.pageSize = 10; component.pageIndex = 0; fixture.detectChanges(); const firstPageButton = getElementByText(paginatorElement, 'button', '1'); expect(firstPageButton.hasAttribute('disabled')).toBe(true); // Must be disabled from clicking it expect(firstPageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Disabled styles must be present expect(getPreviousButton(fixture).hasAttribute('disabled')).toBe(true); // Must be disabled from clicking it }); it('Should disable the next button and lastPage button must be highlighted and disabled when current page is the last page', () => { component.length = 100; component.pageSize = 10; component.pageIndex = 9; fixture.detectChanges(); const firstPageButton = getElementByText(paginatorElement, 'button', '10'); expect(firstPageButton.hasAttribute('disabled')).toBe(true); // Must be disabled from clicking it expect(firstPageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Disabled styles must be present expect(getNextButton(fixture).hasAttribute('disabled')).toBe(true); // Must be disabled from clicking it }); it('Should not display the first dotted skip button when current page is not greater than 3', () => { component.length = 100; component.pageSize = 10; component.pageIndex = 2; fixture.detectChanges(); const dottedButtons = getElementsByText(paginatorElement, 'button', '•••'); expect(dottedButtons.length).toEqual(1); // Only the last dotted button must be present expect(dottedButtons[0].nextSibling.textContent).toEqual('10'); // It must be the last one }); it('Should not display the second dotted skip button when current page is not lesser than [total pages - 3]', () => { component.length = 1000; component.pageSize = 10; component.pageIndex = 96; fixture.detectChanges(); const dottedButtons = getElementsByText(paginatorElement, 'button', '•••'); expect(dottedButtons[0].previousSibling.textContent).toEqual('1'); // Only first dotted button must be present expect(dottedButtons.length).toEqual(1); // To Ensure that its the first one }); it('Should highlight the current page button and it must be disabled', () => { component.length = 100; component.pageSize = 10; component.pageIndex = 5; fixture.detectChanges(); const pageButton = getElementByText(paginatorElement, 'button', '6'); expect(pageButton).toBeDefined(); // Current page must be present expect(pageButton.hasAttribute('disabled')).toBe(true); // Current page must be disabled from clicking it expect(pageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Current page must be the highlighted }); it('Should show total page count by the division of [Length by PageSize].', () => { component.length = 1000; component.pageSize = 11; component.pageIndex = 0; fixture.detectChanges(); const rangeElement = getRangeLabelElement(fixture); expect(rangeElement.innerHTML.trim()).toBe('Showing 1 – 11 of 1000 records'); const lastPageButton = getNextButton(fixture).previousSibling.textContent; // Must show the correct total Page count (Length / PageSize) expect(lastPageButton).toBe(`${Math.ceil(component.length / component.pageSize)}`); }); it('Should emit correct page index value and page size value when they are changed', () => { const paginator = component.paginator; component.length = 100; component.pageSize = 10; component.pageIndex = 0; fixture.detectChanges(); paginator.pageSize = 20; const pageButton = getNativeElementsByText(fixture, 'button', '3'); pageButton[0].click(); // Click the 3 button element fixture.detectChanges(); expect(component.pageEvent).toHaveBeenCalledWith(jasmine.objectContaining({ length: 100, pageIndex: 2, // Changed the page index pageSize: 20, // Changed the page size previousPageIndex: 0 })); // Check the emitted event }); it('Should show first dotted skip pages button after page button 1 when current page is greater than 4', () => { component.length = 100; component.pageSize = 10; component.pageIndex = 5; fixture.detectChanges(); const dottedButtons = getElementsByText(paginatorElement, 'button', '•••'); expect(dottedButtons.length).toEqual(2); // Both the dotted buttons must be present expect(dottedButtons[0].previousSibling.textContent).toEqual('1'); }); it('Should show second dotted skip pages button before total pages button when page count is smaller than [total pages - 4]', () => { component.length = 1000; component.pageSize = 10; component.pageIndex = 95; // Must be smaller than 96 fixture.detectChanges(); const dottedButtons = getElementsByText(paginatorElement, 'button', '•••'); expect(dottedButtons.length).toEqual(2); // Both first and last skip button must be present expect(dottedButtons[1].nextSibling.textContent).toEqual('100'); }); it('Should skip 2 pages forward when second dotted skip page button is clicked.', () => { component.length = 1000; component.pageSize = 10; component.pageIndex = 5; // Must be greater than 4 fixture.detectChanges(); const dottedNativeButtons = getNativeElementsByText(fixture, 'button', '•••'); dottedNativeButtons[1].click(); // Click the skip buttons element fixture.detectChanges(); const currentPageButton = getElementByText(paginatorElement, 'button', '8'); // Current page must be ((5 + 1) + 2 ) expect(currentPageButton).toBeDefined(); // Page button must be present // 8 Must be the current page (So must it be disabled from clicking it) expect(currentPageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Current page must be in the middle expect(currentPageButton.previousSibling.textContent).toEqual('7'); // Previous page element must be present expect(currentPageButton.nextSibling.textContent).toEqual('9'); // Next page element must be present }); it('Should skip 2 pages backward when first dotted skip page button is clicked.', () => { component.length = 1000; component.pageSize = 10; component.pageIndex = 95; // Must be smaller than 96 fixture.detectChanges(); const dottedNativeButtons = getNativeElementsByText(fixture, 'button', '•••'); dottedNativeButtons[0].click(); // Click the skip buttons element fixture.detectChanges(); const currentPageButton = getElementByText(paginatorElement, 'button', '94'); // Current page must be 94 ((95 + 1) - 2 ) expect(currentPageButton).toBeDefined(); // Page button must be present // 94 Must be the current page (So must it be disabled from clicking it) expect(currentPageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Current page must be in the middle expect(currentPageButton.previousSibling.textContent).toEqual('93'); // Previous page element must be present expect(currentPageButton.nextSibling.textContent).toEqual('95'); // Next page element must be present }); it('Should not allow a negative page size', () => { const paginator = component.paginator; paginator.pageSize = -777; expect(paginator.pageSize).toBeGreaterThanOrEqual(0); // Must be greater than 0 }); it('Should not allow a negative page index', () => { const paginator = component.paginator; paginator.pageIndex = -77; const firstPageButton = getElementByText(paginatorElement, 'button', '1'); expect(paginator.pageIndex).toBeGreaterThanOrEqual(0); // Must not be a negative value expect(firstPageButton.hasAttribute('disabled')).toBe(true); // Must be disabled from clicking it expect(firstPageButton.classList.contains('custom-paginator-page-disabled')).toBe(true); // Disabled styles must be present }); }); @Component({ template: ` <mat-paginator appPagination [pageIndex]="pageIndex" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [hidePageSize]="hidePageSize" [showFirstLastButtons]="showFirstLastButtons" [length]="length" [color]="color" [disabled]="disabled" (page)="pageEvent($event)"> </mat-paginator> `, }) // tslint:disable-next-line: component-class-suffix class MatPaginatorApp { pageIndex = 0; pageSize = 10; pageSizeOptions = [5, 10, 25, 100]; hidePageSize = false; showFirstLastButtons = false; length = 100; disabled: boolean; pageEvent = jasmine.createSpy('Page Event'); color: ThemePalette; @ViewChild(MatPaginator) paginator: MatPaginator; } function createComponent<T>(type: Type<T>, providers: Provider[] = []): ComponentFixture<T> { TestBed.configureTestingModule({ imports: [MatPaginatorModule, NoopAnimationsModule], declarations: [type, PaginatorDirective], providers: [MatPaginatorIntl, ...providers] }).compileComponents(); const fixture = TestBed.createComponent(type); fixture.detectChanges(); return fixture; } function getRangeLabelElement(fixture: ComponentFixture<MatPaginatorApp>): HTMLElement { return fixture.nativeElement.querySelector('.mat-paginator-range-label'); } function getPaginatorElement(fixture: ComponentFixture<MatPaginatorApp>): HTMLElement { return fixture.nativeElement.querySelector('.mat-paginator-range-actions'); } function getPreviousButton(fixture: ComponentFixture<MatPaginatorApp>): HTMLElement { return fixture.nativeElement.querySelector('.mat-paginator-navigation-previous'); } function getNextButton(fixture: ComponentFixture<MatPaginatorApp>): HTMLElement { return fixture.nativeElement.querySelector('.mat-paginator-navigation-next'); } function getNativeElementsByText(fixture: ComponentFixture<MatPaginatorApp>, type: string, text: string): HTMLElement[] { const nativeElements: HTMLElement[] = Array.from(fixture.nativeElement.querySelectorAll(type)); return nativeElements.filter(element => element.innerHTML === text); } function getElementsByText(paginatorElement: HTMLElement, type: string, text: string): Element[] { return Array.from(paginatorElement.querySelectorAll(type)) .filter(element => element.textContent === text); } function getElementByText(paginatorElement: HTMLElement, type: string, text: string): Element { return Array.from(paginatorElement.querySelectorAll(type)) .find(element => element.textContent === text); }