diff --git a/src/capacitor/index.ts b/src/capacitor/index.ts index 9ab1f96..3da4f9c 100644 --- a/src/capacitor/index.ts +++ b/src/capacitor/index.ts @@ -102,10 +102,13 @@ export class FluxCapacitor extends EventEmitter { resize(pageSize: number, resetOffset?: boolean): Promise { this.query.withPageSize(pageSize); - const skip = resetOffset ? 0 : Math.floor(this.page.fromResult / pageSize) * pageSize; - this.query.skip(skip); - this.emit(Events.PAGE_CHANGED, { pageNumber: this.page.currentPage }); - return this.search(); + if (resetOffset) { + return this.page.switchPage(1); + } else { + const total = this.page.restrictTotalRecords(this.page.fromResult, pageSize); + const page = this.page.getPage(total); + return this.page.switchPage(page); + } } sort(sort: Sort, clearSorts: Sort[] = [sort]): Promise { diff --git a/src/capacitor/pager.ts b/src/capacitor/pager.ts index 99db0cd..a330736 100644 --- a/src/capacitor/pager.ts +++ b/src/capacitor/pager.ts @@ -2,6 +2,8 @@ import { Results } from '../models/response'; import { Events, FluxCapacitor } from './index'; import range = require('lodash.range'); +const MAX_RECORDS = 10000; + export class Pager { constructor(private flux: FluxCapacitor) { } @@ -23,7 +25,7 @@ export class Pager { } get currentPage(): number { - return Math.ceil(this.fromResult / this.pageSize); + return this.getPage(this.fromResult); } get previousPage(): number | null { @@ -39,7 +41,7 @@ export class Pager { } get finalPage(): number { - return Math.max(Math.ceil(this.totalRecords / this.pageSize), 1); + return Math.max(this.getPage(this.restrictTotalRecords(this.totalRecords, this.pageSize)), 1); } get fromResult(): number { @@ -78,6 +80,24 @@ export class Pager { } } + restrictTotalRecords(total: number, pageSize: number): number { + if (total > MAX_RECORDS) { + return MAX_RECORDS - (MAX_RECORDS % pageSize); + } else if ((total + pageSize) > MAX_RECORDS) { + if (MAX_RECORDS % pageSize === 0) { + return MAX_RECORDS; + } else { + return total - (total % pageSize); + } + } else { + return total; + } + } + + getPage(record: number): number { + return Math.ceil(record / this.pageSize); + } + private transformPages(limit: number): (value: number) => number { const border = Math.ceil(limit / 2); return (value: number): number => { diff --git a/test/capacitor.ts b/test/capacitor.ts index 41c624e..94d2dfa 100644 --- a/test/capacitor.ts +++ b/test/capacitor.ts @@ -289,6 +289,19 @@ describe('FluxCapacitor', function() { it('should resize the page and keep skip', (done) => { flux.query.withPageSize(10); flux.query.skip(20); + flux.page.pageExists = () => true; + mock.post(SEARCH_URL, (req, res) => { + expect(JSON.parse(req.body()).skip).to.eq(20); + expect(JSON.parse(req.body()).pageSize).to.eq(20); + done(); + }); + flux.resize(20, false); + }); + + it('should resize the page and keep skip on the same page', (done) => { + flux.query.withPageSize(10); + flux.query.skip(30); + flux.page.pageExists = () => true; mock.post(SEARCH_URL, (req, res) => { expect(JSON.parse(req.body()).skip).to.eq(20); expect(JSON.parse(req.body()).pageSize).to.eq(20); @@ -300,6 +313,7 @@ describe('FluxCapacitor', function() { it('should resize the page and bring skip to 0', (done) => { flux.query.withPageSize(10); flux.query.skip(20); + flux.page.pageExists = () => true; mock.post(SEARCH_URL, (req, res) => { expect(JSON.parse(req.body()).skip).to.eq(0); expect(JSON.parse(req.body()).pageSize).to.eq(30); @@ -307,6 +321,30 @@ describe('FluxCapacitor', function() { }); flux.resize(30, true); }); + + it('should resize from smaller to larger and keep skip when total near max', (done) => { + flux.query.withPageSize(12); + flux.query.skip(9984); + flux.page.pageExists = () => true; + mock.post(SEARCH_URL, (req, res) => { + expect(JSON.parse(req.body()).skip).to.eq(9960); + expect(JSON.parse(req.body()).pageSize).to.eq(24); + done(); + }); + flux.resize(24, false); + }); + + it('should resize from larger to smaller and keep skip when total near max', (done) => { + flux.query.withPageSize(50); + flux.query.skip(9950); + flux.page.pageExists = () => true; + mock.post(SEARCH_URL, (req, res) => { + expect(JSON.parse(req.body()).skip).to.eq(9947); + expect(JSON.parse(req.body()).pageSize).to.eq(49); + done(); + }); + flux.resize(49, false); + }); }); describe('rewrite()', () => { diff --git a/test/pager.ts b/test/pager.ts index df2d382..ac599aa 100644 --- a/test/pager.ts +++ b/test/pager.ts @@ -88,6 +88,14 @@ describe('Pager', function() { expect(mockFlux.query.raw.skip).to.eq(2); }); + it('should not change skip when cannot page forward', () => { + const mockFlux = flux(29, () => null); + const pager = new Pager(mockFlux); + expect(mockFlux.query.raw.skip).to.eq(29); + pager.next(); + expect(mockFlux.query.raw.skip).to.eq(29); + }); + it('should reset the skip to 0', (done) => { new Pager(flux(2, function() { expect(this.query.raw.skip).to.eq(0); @@ -116,42 +124,11 @@ describe('Pager', function() { })).last(); }); - describe('realigned paging behaviour', () => { - // it('should not realign the pages on page next', () => { - // new Pager(flux({ start: 13, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(23); - // })).next(); - // }); - // - // it('should realign the pages on page next', () => { - // new Pager(flux({ start: 13, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(20); - // })).next(true); - // }); - // - // it('should not skip past last page', () => { - // new Pager(flux({ start: 43, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(40); - // })).next(true); - // }); - // - // it('should not realign the pages on page previous', () => { - // new Pager(flux({ start: 23, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(13); - // })).prev(); - // }); - // - // it('should realign the pages on page previous', () => { - // new Pager(flux({ start: 23, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(10); - // })).prev(true); - // }); - // - // it('should not realign to a negative value', () => { - // new Pager(flux({ start: 3, total: 45 }, function() { - // expect(this.query.raw.skip).to.eq(0); - // })).prev(true); - // }); + it('max skip should not go beyond 10000 records', (done) => { + new Pager(flux({ start: 1, total: 14000 }, function() { + expect(this.query.raw.skip).to.eq(9990); + done(); + })).last(); }); describe('pageExists()', () => { @@ -166,7 +143,7 @@ describe('Pager', function() { }); }); - describe('switchPage behaviour', () => { + describe('switchPage() behaviour', () => { it('should switchPage to the first page', () => { const pager = new Pager(flux({ start: 10, total: 200 }, function() { expect(this.query.raw.skip).to.eq(0); @@ -202,6 +179,19 @@ describe('Pager', function() { }); }); + describe('restrictTotalRecords()', () => { + it('should return total records that is less than MAX_RECORDS', () => { + const pager = new Pager({}); + expect(pager.restrictTotalRecords(20000, 10)).to.eq(10000); + expect(pager.restrictTotalRecords(20000, 12)).to.eq(9996); + expect(pager.restrictTotalRecords(20000, 24)).to.eq(9984); + expect(pager.restrictTotalRecords(20000, 50)).to.eq(10000); + expect(pager.restrictTotalRecords(9999, 13)).to.eq(9997); + expect(pager.restrictTotalRecords(9960, 50)).to.eq(10000); + expect(pager.restrictTotalRecords(100, 20)).to.eq(100); + }); + }); + describe('current page behaviour', () => { it('should return the current page', () => { expect(new Pager(flux(1)).currentPage).to.eq(1); @@ -230,7 +220,7 @@ describe('Pager', function() { }); }); - describe('pages behaviour', () => { + describe('page numbers behaviour', () => { it('should return an array of beginning at 1', () => { expect(new Pager(flux({ start: 1, total: 100 })).pageNumbers()).to.eql([1, 2, 3, 4, 5]); });