import {TestBed, ComponentFixture, inject, fakeAsync, tick} from '@angular/core/testing'; import {createGenericTestComponent} from '../test/common'; import {Component} from '@angular/core'; import {NgbPaginationModule} from './pagination.module'; import {NgbPagination} from './pagination'; import {NgbPaginationConfig} from './pagination-config'; const createTestComponent = (html: string) => createGenericTestComponent(html, TestComponent) as ComponentFixture; function expectPages(nativeEl: HTMLElement, pagesDef: string[]): void { const pages = nativeEl.querySelectorAll('li'); expect(pages.length).toEqual(pagesDef.length); for (let i = 0; i < pagesDef.length; i++) { let pageDef = pagesDef[i]; let classIndicator = pageDef.charAt(0); if (classIndicator === '+') { expect(pages[i]).toHaveCssClass('active'); expect(pages[i]).not.toHaveCssClass('disabled'); expect(normalizeText(pages[i].textContent)).toEqual(pageDef.substr(1) + ' (current)'); } else if (classIndicator === '-') { expect(pages[i]).not.toHaveCssClass('active'); expect(pages[i]).toHaveCssClass('disabled'); expect(normalizeText(pages[i].textContent)).toEqual(pageDef.substr(1)); if (normalizeText(pages[i].textContent) !== '...') { expect(pages[i].querySelector('a').getAttribute('tabindex')).toEqual('-1'); } } else { expect(pages[i]).not.toHaveCssClass('active'); expect(pages[i]).not.toHaveCssClass('disabled'); expect(normalizeText(pages[i].textContent)).toEqual(pageDef); if (normalizeText(pages[i].textContent) !== '...') { expect(pages[i].querySelector('a').hasAttribute('tabindex')).toBeFalsy(); } } } } function getLink(nativeEl: HTMLElement, idx: number): HTMLAnchorElement { return nativeEl.querySelectorAll('li')[idx].querySelector('a'); } function getList(nativeEl: HTMLElement) { return nativeEl.querySelector('ul'); } function getSpan(nativeEl: HTMLElement): HTMLSpanElement { return nativeEl.querySelector('span'); } function normalizeText(txt: string): string { return txt.trim().replace(/\s+/g, ' '); } function expectSameValues(pagination: NgbPagination, config: NgbPaginationConfig) { expect(pagination.disabled).toBe(config.disabled); expect(pagination.boundaryLinks).toBe(config.boundaryLinks); expect(pagination.directionLinks).toBe(config.directionLinks); expect(pagination.ellipses).toBe(config.ellipses); expect(pagination.maxSize).toBe(config.maxSize); expect(pagination.pageSize).toBe(config.pageSize); expect(pagination.rotate).toBe(config.rotate); expect(pagination.size).toBe(config.size); } describe('ngb-pagination', () => { describe('business logic', () => { let pagination: NgbPagination; beforeEach(() => { pagination = new NgbPagination(new NgbPaginationConfig()); }); it('should initialize inputs with default values', () => { const defaultConfig = new NgbPaginationConfig(); expectSameValues(pagination, defaultConfig); }); it('should calculate and update no of pages (default page size)', () => { pagination.collectionSize = 100; pagination.ngOnChanges(null); expect(pagination.pages.length).toEqual(10); pagination.collectionSize = 200; pagination.ngOnChanges(null); expect(pagination.pages.length).toEqual(20); }); it('should calculate and update no of pages (custom page size)', () => { pagination.collectionSize = 100; pagination.pageSize = 20; pagination.ngOnChanges(null); expect(pagination.pages.length).toEqual(5); pagination.collectionSize = 200; pagination.ngOnChanges(null); expect(pagination.pages.length).toEqual(10); pagination.pageSize = 10; pagination.ngOnChanges(null); expect(pagination.pages.length).toEqual(20); }); it('should allow setting a page within a valid range (default page size)', () => { pagination.collectionSize = 100; pagination.page = 2; pagination.ngOnChanges(null); expect(pagination.page).toEqual(2); }); it('should auto-correct page no if outside of valid range (default page size)', () => { pagination.collectionSize = 100; pagination.page = 100; pagination.ngOnChanges(null); expect(pagination.page).toEqual(10); pagination.page = -100; pagination.ngOnChanges(null); expect(pagination.page).toEqual(1); pagination.page = 5; pagination.collectionSize = 10; pagination.ngOnChanges(null); expect(pagination.page).toEqual(1); }); it('should allow setting a page within a valid range (custom page size)', () => { pagination.collectionSize = 100; pagination.pageSize = 20; pagination.page = 2; pagination.ngOnChanges(null); expect(pagination.page).toEqual(2); }); }); describe('UI logic', () => { beforeEach(() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbPaginationModule.forRoot()]}); }); it('should render and respond to collectionSize change', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); fixture.componentInstance.collectionSize = 40; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '»']); }); it('should render and respond to pageSize change', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.componentInstance.pageSize = 5; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '6', '»']); fixture.componentInstance.pageSize = 10; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); }); it('should render and respond to active page change', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.componentInstance.page = 2; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '3', '»']); fixture.componentInstance.page = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '-»']); }); it('should update selected page model on page no click', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.componentInstance.page = 2; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '3', '»']); getLink(fixture.nativeElement, 0).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); getLink(fixture.nativeElement, 3).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '-»']); }); it('should update selected page model on prev / next click', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.detectChanges(); expectPages(fixture.nativeElement, ['+1', '2', '3']); fixture.componentInstance.directionLinks = true; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); getLink(fixture.nativeElement, 0).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); getLink(fixture.nativeElement, 4).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '3', '»']); getLink(fixture.nativeElement, 4).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '-»']); getLink(fixture.nativeElement, 4).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '-»']); }); it('should update selected page model on first / last click', () => { const html = ``; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); fixture.componentInstance.boundaryLinks = true; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-««', '-«', '+1', '2', '3', '»', '»»']); getLink(fixture.nativeElement, 0).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['-««', '-«', '+1', '2', '3', '»', '»»']); getLink(fixture.nativeElement, 6).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['««', '«', '1', '2', '+3', '-»', '-»»']); getLink(fixture.nativeElement, 3).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['««', '«', '1', '+2', '3', '»', '»»']); getLink(fixture.nativeElement, 0).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['-««', '-«', '+1', '2', '3', '»', '»»']); // maxSize < number of pages fixture.componentInstance.collectionSize = 70; fixture.componentInstance.maxSize = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-««', '-«', '+1', '2', '3', '-...', '7', '»', '»»']); getLink(fixture.nativeElement, 8).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['««', '«', '1', '-...', '+7', '-»', '-»»']); getLink(fixture.nativeElement, 0).click(); fixture.detectChanges(); expectPages(fixture.nativeElement, ['-««', '-«', '+1', '2', '3', '-...', '7', '»', '»»']); }); it('should update page when it becomes out of range', fakeAsync(() => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 30; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); getLink(fixture.nativeElement, 3).click(); fixture.detectChanges(); tick(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '-»']); expect(fixture.componentInstance.page).toBe(3); fixture.componentInstance.collectionSize = 20; fixture.detectChanges(); tick(); expectPages(fixture.nativeElement, ['«', '1', '+2', '-»']); expect(fixture.componentInstance.page).toBe(2); })); it('should render and respond to size change', () => { const html = ''; const fixture = createTestComponent(html); const listEl = getList(fixture.nativeElement); // default case expectPages(fixture.nativeElement, ['-«', '+1', '2', '»']); expect(listEl).toHaveCssClass('pagination'); // large size fixture.componentInstance.size = 'lg'; fixture.detectChanges(); expect(listEl).toHaveCssClass('pagination'); expect(listEl).toHaveCssClass('pagination-lg'); // removing large size fixture.componentInstance.size = ''; fixture.detectChanges(); expect(listEl).toHaveCssClass('pagination'); expect(listEl).not.toHaveCssClass('pagination-lg'); // arbitrary string fixture.componentInstance.size = '123'; fixture.detectChanges(); expect(listEl).toHaveCssClass('pagination'); expect(listEl).toHaveCssClass('pagination-123'); }); it('should render and respond to maxSize change correctly', () => { const html = ''; const fixture = createTestComponent(html); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '6', '7', '»']); // disabling ellipsis fixture.componentInstance.ellipses = false; // limiting to 3 page numbers fixture.componentInstance.maxSize = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); fixture.componentInstance.page = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '»']); fixture.componentInstance.page = 4; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '+4', '5', '6', '»']); fixture.componentInstance.page = 7; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '+7', '-»']); // checking that maxSize > total pages works fixture.componentInstance.maxSize = 100; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '3', '4', '5', '6', '+7', '-»']); }); it('should render and rotate pages correctly', () => { const html = ``; const fixture = createTestComponent(html); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '6', '7', '»']); // disabling ellipsis fixture.componentInstance.ellipses = false; // limiting to 3 (odd) page numbers fixture.componentInstance.maxSize = 3; fixture.componentInstance.rotate = true; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '»']); fixture.componentInstance.page = 2; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '3', '»']); fixture.componentInstance.page = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '2', '+3', '4', '»']); fixture.componentInstance.page = 7; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '5', '6', '+7', '-»']); // limiting to 4 (even) page numbers fixture.componentInstance.maxSize = 4; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '4', '5', '6', '+7', '-»']); fixture.componentInstance.page = 5; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '3', '4', '+5', '6', '»']); fixture.componentInstance.page = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '4', '»']); }); it('should display ellipsis correctly', () => { const html = ``; const fixture = createTestComponent(html); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '6', '7', '»']); // limiting to 3 page numbers fixture.componentInstance.maxSize = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '-...', '7', '»']); fixture.componentInstance.page = 4; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '+4', '5', '6', '7', '»']); fixture.componentInstance.page = 7; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '+7', '-»']); // enabling rotation fixture.componentInstance.rotate = true; fixture.componentInstance.page = 1; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '-...', '7', '»']); fixture.componentInstance.page = 2; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '3', '-...', '7', '»']); fixture.componentInstance.page = 3; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '+3', '4', '-...', '7', '»']); fixture.componentInstance.page = 4; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '3', '+4', '5', '-...', '7', '»']); fixture.componentInstance.page = 5; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '4', '+5', '6', '7', '»']); fixture.componentInstance.page = 6; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '5', '+6', '7', '»']); fixture.componentInstance.page = 7; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '-...', '5', '6', '+7', '-»']); // no ellipsis when maxPage > total pages fixture.componentInstance.maxSize = 100; fixture.componentInstance.page = 5; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '2', '3', '4', '+5', '6', '7', '»']); }); it('should handle edge "maxSize" values', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.maxSize = 2; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '-...', '5', '»']); fixture.componentInstance.maxSize = 0; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '»']); fixture.componentInstance.maxSize = 100; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '»']); fixture.componentInstance.maxSize = NaN; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '»']); fixture.componentInstance.maxSize = null; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '3', '4', '5', '»']); }); it('should handle edge "collectionSize" values', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.collectionSize = 0; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); fixture.componentInstance.collectionSize = NaN; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); fixture.componentInstance.collectionSize = null; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); }); it('should handle edge "pageSize" values', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.pageSize = 0; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); fixture.componentInstance.pageSize = NaN; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); fixture.componentInstance.pageSize = null; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '-»']); }); it('should handle edge "page" values', () => { const html = ''; const fixture = createTestComponent(html); fixture.componentInstance.page = 0; fixture.detectChanges(); expectPages(fixture.nativeElement, ['-«', '+1', '2', '»']); fixture.componentInstance.page = 2016; fixture.detectChanges(); expectPages(fixture.nativeElement, ['«', '1', '+2', '-»']); fixture.componentInstance.page = NaN; expectPages(fixture.nativeElement, ['«', '1', '+2', '-»']); fixture.componentInstance.page = null; expectPages(fixture.nativeElement, ['«', '1', '+2', '-»']); }); it('should not emit "pageChange" for incorrect input values', fakeAsync(() => { const html = ``; const fixture = createTestComponent(html); tick(); spyOn(fixture.componentInstance, 'onPageChange'); fixture.componentInstance.collectionSize = NaN; fixture.detectChanges(); tick(); fixture.componentInstance.maxSize = NaN; fixture.detectChanges(); tick(); fixture.componentInstance.pageSize = NaN; fixture.detectChanges(); tick(); expect(fixture.componentInstance.onPageChange).not.toHaveBeenCalled(); })); it('should not emit "pageChange" when collection size is not set', fakeAsync(() => { const html = ``; const fixture = createTestComponent(html); tick(); spyOn(fixture.componentInstance, 'onPageChange'); fixture.componentInstance.page = 5; fixture.detectChanges(); tick(); expect(fixture.componentInstance.onPageChange).not.toHaveBeenCalled(); })); it('should set classes correctly for disabled state', fakeAsync(() => { const html = ``; const fixture = createTestComponent(html); tick(); const buttons = fixture.nativeElement.querySelectorAll('li'); for (let i = 0; i < buttons.length; i++) { expect(buttons[i]).toHaveCssClass('disabled'); } })); }); describe('Custom config', () => { let config: NgbPaginationConfig; beforeEach(() => { TestBed.configureTestingModule({imports: [NgbPaginationModule.forRoot()]}); }); beforeEach(inject([NgbPaginationConfig], (c: NgbPaginationConfig) => { config = c; config.boundaryLinks = true; config.directionLinks = false; config.ellipses = false; config.maxSize = 42; config.pageSize = 7; config.rotate = true; config.size = 'sm'; })); it('should initialize inputs with provided config', () => { const fixture = TestBed.createComponent(NgbPagination); fixture.detectChanges(); let pagination = fixture.componentInstance; expectSameValues(pagination, config); }); }); describe('Custom config as provider', () => { let config = new NgbPaginationConfig(); config.disabled = true; config.boundaryLinks = true; config.directionLinks = false; config.ellipses = false; config.maxSize = 42; config.pageSize = 7; config.rotate = true; config.size = 'sm'; beforeEach(() => { TestBed.configureTestingModule( {imports: [NgbPaginationModule.forRoot()], providers: [{provide: NgbPaginationConfig, useValue: config}]}); }); it('should initialize inputs with provided config as provider', () => { const fixture = TestBed.createComponent(NgbPagination); fixture.detectChanges(); let pagination = fixture.componentInstance; expectSameValues(pagination, config); }); }); }); @Component({selector: 'test-cmp', template: ''}) class TestComponent { pageSize = 10; collectionSize = 100; page = 1; boundaryLinks = false; directionLinks = false; size = ''; maxSize = 0; ellipses = true; rotate = false; onPageChange = () => {}; }