- NOTE: The last column (filter & sort) will always throw an error and its only purpose is to demo what would happen
- when you encounter a backend server error (the UI should rollback to previous state before you did the action).
+ NOTE: For demo purposes, the last column (filter & sort) will always throw an
+ error and its only purpose is to demo what would happen when you encounter a backend server error
+ (the UI should rollback to previous state before you did the action).
+ Also changing Page Size to 50,000 will also throw which again is for demo purposes.
@@ -26,6 +28,11 @@
+
@@ -35,10 +42,10 @@
diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example09.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example09.ts
index 2bbf7996e..4fdb05447 100644
--- a/examples/webpack-demo-vanilla-bundle/src/examples/example09.ts
+++ b/examples/webpack-demo-vanilla-bundle/src/examples/example09.ts
@@ -21,6 +21,7 @@ export class Example09 {
errorStatusClass = 'hidden';
status = '';
statusClass = 'is-success';
+ isPageErrorTest = false;
constructor() {
this._bindingEventService = new BindingEventService();
@@ -36,7 +37,7 @@ export class Example09 {
// this._bindingEventService.bind(gridContainerElm, 'onafterexporttoexcel', () => console.log('onAfterExportToExcel'));
this.sgb = new Slicker.GridBundle(gridContainerElm, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, []);
- // you can optionally cancel the Sort, Filter
+ // you can optionally cancel the Filtering, Sorting or Pagination with code shown below
// this._bindingEventService.bind(gridContainerElm, 'onbeforesort', (e) => {
// e.preventDefault();
// return false;
@@ -46,6 +47,10 @@ export class Example09 {
// this.sgb.filterService.resetToPreviousSearchFilters(); // optionally reset filter input value
// return false;
// });
+ // this._bindingEventService.bind(gridContainerElm, 'onbeforepaginationchange', (e) => {
+ // e.preventDefault();
+ // return false;
+ // });
}
dispose() {
@@ -100,7 +105,7 @@ export class Example09 {
enableRowSelection: true,
enablePagination: true, // you could optionally disable the Pagination
pagination: {
- pageSizes: [10, 20, 50, 100, 500],
+ pageSizes: [10, 20, 50, 100, 500, 50000],
pageSize: defaultPageSize,
},
presets: {
@@ -192,9 +197,17 @@ export class Example09 {
let countTotalItems = 100;
const columnFilters = {};
+ if (this.isPageErrorTest) {
+ this.isPageErrorTest = false;
+ throw new Error('Server timed out trying to retrieve data for the last page');
+ }
+
for (const param of queryParams) {
if (param.includes('$top=')) {
top = +(param.substring('$top='.length));
+ if (top === 50000) {
+ throw new Error('Server timed out retrieving 50,000 rows');
+ }
}
if (param.includes('$skip=')) {
skip = +(param.substring('$skip='.length));
@@ -234,14 +247,14 @@ export class Example09 {
// simular a backend error when trying to sort on the "Company" field
if (filterBy.includes('company')) {
- throw new Error('Cannot filter by the field "Company"');
+ throw new Error('Server could not filter using the field "Company"');
}
}
}
// simular a backend error when trying to sort on the "Company" field
if (orderBy.includes('company')) {
- throw new Error('Cannot sort by the field "Company"');
+ throw new Error('Server could not sort using the field "Company"');
}
const sort = orderBy.includes('asc')
@@ -342,6 +355,11 @@ export class Example09 {
]);
}
+ throwPageChangeError() {
+ this.isPageErrorTest = true;
+ this.sgb.paginationService.goToLastPage();
+ }
+
// THE FOLLOWING METHODS ARE ONLY FOR DEMO PURPOSES DO NOT USE THIS CODE
// ---
diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example15.html b/examples/webpack-demo-vanilla-bundle/src/examples/example15.html
index 53ba2fb42..74eba5c01 100644
--- a/examples/webpack-demo-vanilla-bundle/src/examples/example15.html
+++ b/examples/webpack-demo-vanilla-bundle/src/examples/example15.html
@@ -10,9 +10,11 @@
- NOTE: The last column (filter & sort) will always throw an error and its only purpose is to demo what would happen
- when you encounter a backend server error (the UI should rollback to previous state before you did the action).
-
+ NOTE: For demo purposes, the last column (filter & sort) will always throw an
+ error and its only purpose is to demo what would happen when you encounter a backend server error
+ (the UI should rollback to previous state before you did the action).
+ Also changing Page Size to 50,000 will also throw which again is for demo purposes.
+
s
+
@@ -39,10 +46,10 @@
diff --git a/examples/webpack-demo-vanilla-bundle/src/examples/example15.ts b/examples/webpack-demo-vanilla-bundle/src/examples/example15.ts
index 0445638d2..7c5b1eb91 100644
--- a/examples/webpack-demo-vanilla-bundle/src/examples/example15.ts
+++ b/examples/webpack-demo-vanilla-bundle/src/examples/example15.ts
@@ -26,6 +26,7 @@ export class Example15 {
status = '';
statusClass = 'is-success';
isOtherGenderAdded = false;
+ isPageErrorTest = false;
genderCollection = [{ value: 'male', label: 'male' }, { value: 'female', label: 'female' }];
constructor() {
@@ -108,7 +109,7 @@ export class Example15 {
enableRowSelection: true,
enablePagination: true, // you could optionally disable the Pagination
pagination: {
- pageSizes: [10, 20, 50, 100, 500],
+ pageSizes: [10, 20, 50, 100, 500, 50000],
pageSize: defaultPageSize,
},
presets: {
@@ -227,9 +228,17 @@ export class Example15 {
let countTotalItems = 100;
const columnFilters = {};
+ if (this.isPageErrorTest) {
+ this.isPageErrorTest = false;
+ throw new Error('Server timed out trying to retrieve data for the last page');
+ }
+
for (const param of queryParams) {
if (param.includes('$top=')) {
top = +(param.substring('$top='.length));
+ if (top === 50000) {
+ throw new Error('Server timed out retrieving 50,000 rows');
+ }
}
if (param.includes('$skip=')) {
skip = +(param.substring('$skip='.length));
@@ -269,14 +278,14 @@ export class Example15 {
// simular a backend error when trying to sort on the "Company" field
if (filterBy.includes('company')) {
- throw new Error('Cannot filter by the field "Company"');
+ throw new Error('Server could not filter using the field "Company"');
}
}
}
// simular a backend error when trying to sort on the "Company" field
if (orderBy.includes('company')) {
- throw new Error('Cannot sort by the field "Company"');
+ throw new Error('Server could not sort using the field "Company"');
}
const sort = orderBy.includes('asc')
@@ -378,6 +387,11 @@ export class Example15 {
]);
}
+ throwPageChangeError() {
+ this.isPageErrorTest = true;
+ this.sgb.paginationService.goToLastPage();
+ }
+
// THE FOLLOWING METHODS ARE ONLY FOR DEMO PURPOSES DO NOT USE THIS CODE
// ---
diff --git a/packages/common/src/services/__tests__/pagination.service.spec.ts b/packages/common/src/services/__tests__/pagination.service.spec.ts
index fd3e64ad2..071191be3 100644
--- a/packages/common/src/services/__tests__/pagination.service.spec.ts
+++ b/packages/common/src/services/__tests__/pagination.service.spec.ts
@@ -326,17 +326,18 @@ describe('PaginationService', () => {
expect(spy).toHaveBeenCalledWith(4, undefined);
});
- it('should not expect "processOnPageChanged" method to be called when we are already on same page', () => {
+ it('should not expect "processOnPageChanged" method to be called when we are already on same page', async () => {
const spy = jest.spyOn(service, 'processOnPageChanged');
mockGridOption.pagination!.pageNumber = 2;
service.init(gridStub, mockGridOption.pagination as Pagination, mockGridOption.backendServiceApi);
- service.goToPageNumber(2);
+ const output = await service.goToPageNumber(2);
expect(service.dataFrom).toBe(26);
expect(service.dataTo).toBe(50);
expect(service.getCurrentPageNumber()).toBe(2);
expect(spy).not.toHaveBeenCalled();
+ expect(output).toBeFalsy();
});
});
@@ -348,12 +349,14 @@ describe('PaginationService', () => {
options: {
columnDefinitions: [{ id: 'name', field: 'name' }] as Column[],
datasetName: 'user',
- }
+ },
+ onError: jest.fn(),
};
});
afterEach(() => {
jest.clearAllMocks();
+ jest.spyOn(mockPubSub, 'publish').mockReturnValue(true);
});
it('should execute "preProcess" method when defined', () => {
@@ -366,10 +369,25 @@ describe('PaginationService', () => {
expect(spy).toHaveBeenCalled();
});
- it('should execute "process" method and catch error when process Promise rejects', async () => {
+ it('should NOT execute anything and return a Promise with Pagination before calling the change', async () => {
+ const pubSubSpy = jest.spyOn(mockPubSub, 'publish').mockReturnValue(false);
+
+ const preProcessSpy = jest.fn();
+ mockGridOption.backendServiceApi!.preProcess = preProcessSpy;
+
+ service.init(gridStub, mockGridOption.pagination as Pagination, mockGridOption.backendServiceApi);
+ const output = await service.processOnPageChanged(1);
+
+ expect(output).toBeTruthy();
+ expect(pubSubSpy).toHaveBeenCalled();
+ expect(preProcessSpy).not.toHaveBeenCalled();
+ });
+
+ it('should execute "process" method and catch error when process Promise rejects and there is no "onError" defined', async () => {
const mockError = { error: '404' };
const postSpy = jest.fn();
mockGridOption.backendServiceApi!.process = postSpy;
+ mockGridOption.backendServiceApi!.onError = undefined;
jest.spyOn(mockBackendService, 'processOnPaginationChanged').mockReturnValue('backend query');
jest.spyOn(mockGridOption.backendServiceApi as BackendServiceApi, 'process').mockReturnValue(Promise.reject(mockError));
const backendErrorSpy = jest.spyOn(backendUtilityServiceStub, 'onBackendError');
@@ -385,6 +403,7 @@ describe('PaginationService', () => {
it('should execute "process" method and catch error when process Observable fails', async () => {
const mockError = 'observable error';
const postSpy = jest.fn();
+ mockGridOption.backendServiceApi!.onError = undefined;
mockGridOption.backendServiceApi!.process = postSpy;
jest.spyOn(mockBackendService, 'processOnPaginationChanged').mockReturnValue('backend query');
jest.spyOn(mockGridOption.backendServiceApi as BackendServiceApi, 'process').mockReturnValue(throwError(mockError));
@@ -591,6 +610,32 @@ describe('PaginationService', () => {
});
});
+ describe('resetToPreviousPagination method', () => {
+ it('should call "changeItemPerPage" when page size is different', () => {
+ const changeItemSpy = jest.spyOn(service, 'changeItemPerPage');
+ const refreshSpy = jest.spyOn(service, 'refreshPagination');
+
+ service.init(gridStub, mockGridOption.pagination as Pagination, mockGridOption.backendServiceApi);
+ service.changeItemPerPage(100, null, false); // change without triggering event to simulate a change
+ service.resetToPreviousPagination();
+
+ expect(changeItemSpy).toHaveBeenCalled();
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+
+ it('should call "goToPageNumber" when page size is different', () => {
+ const changeItemSpy = jest.spyOn(service, 'goToPageNumber');
+ const refreshSpy = jest.spyOn(service, 'refreshPagination');
+
+ service.init(gridStub, mockGridOption.pagination as Pagination, mockGridOption.backendServiceApi);
+ service.goToPageNumber(100, null, false); // change without triggering event to simulate a change
+ service.resetToPreviousPagination();
+
+ expect(changeItemSpy).toHaveBeenCalled();
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+ });
+
// processOnItemAddedOrRemoved is private but we can spy on recalculateFromToIndexes
describe('processOnItemAddedOrRemoved private method', () => {
afterEach(() => {
diff --git a/packages/common/src/services/backendUtility.service.ts b/packages/common/src/services/backendUtility.service.ts
index 90798e93f..96ddc849c 100644
--- a/packages/common/src/services/backendUtility.service.ts
+++ b/packages/common/src/services/backendUtility.service.ts
@@ -43,7 +43,7 @@ export class BackendUtilityService {
/** On a backend service api error, we will run the "onError" if there is 1 provided or just throw back the error when nothing is provided */
onBackendError(e: any, backendApi: BackendServiceApi) {
- if (backendApi?.onError) {
+ if (typeof backendApi?.onError === 'function') {
backendApi.onError(e);
} else {
throw e;
diff --git a/packages/common/src/services/pagination.service.ts b/packages/common/src/services/pagination.service.ts
index c76f127c1..485f258a4 100644
--- a/packages/common/src/services/pagination.service.ts
+++ b/packages/common/src/services/pagination.service.ts
@@ -33,6 +33,7 @@ export class PaginationService {
protected _totalItems = 0;
protected _availablePageSizes: number[] = [];
protected _paginationOptions!: Pagination;
+ protected _previousPagination?: Pagination;
protected _subscriptions: EventSubscription[] = [];
/** SlickGrid Grid object */
@@ -43,7 +44,7 @@ export class PaginationService {
/** Getter of SlickGrid DataView object */
get dataView(): SlickDataView | undefined {
- return (this.grid?.getData && this.grid.getData()) as SlickDataView;
+ return this.grid?.getData?.() ?? {} as SlickDataView;
}
set paginationOptions(paginationOptions: Pagination) {
@@ -109,6 +110,7 @@ export class PaginationService {
(this._eventHandler as SlickEventHandler>).subscribe(onPagingInfoChangedHandler, (_e, pagingInfo) => {
if (this._totalItems !== pagingInfo.totalRows) {
this.updateTotalItems(pagingInfo.totalRows);
+ this._previousPagination = { pageNumber: pagingInfo.pageNum, pageSize: pagingInfo.pageSize, pageSizes: this.availablePageSizes, totalItems: pagingInfo.totalRows };
}
});
setTimeout(() => {
@@ -126,13 +128,16 @@ export class PaginationService {
// Subscribe to any dataview row count changed so that when Adding/Deleting item(s) through the DataView
// that would trigger a refresh of the pagination numbers
if (this.dataView) {
- this._subscriptions.push(this.pubSubService.subscribe(`onItemAdded`, (items: any | any[]) => {
- this.processOnItemAddedOrRemoved(items, true);
- }));
+ this._subscriptions.push(this.pubSubService.subscribe(`onItemAdded`, (items: any | any[]) => this.processOnItemAddedOrRemoved(items, true)));
this._subscriptions.push(this.pubSubService.subscribe(`onItemDeleted`, (items: any | any[]) => this.processOnItemAddedOrRemoved(items, false)));
}
this.refreshPagination(false, false, true);
+
+ // also keep reference to current pagination in case we need to rollback
+ const pagination = this.getFullPagination();
+ this._previousPagination = { pageNumber: pagination.pageNumber, pageSize: pagination.pageSize, pageSizes: pagination.pageSizes, totalItems: this.totalItems };
+
this._initialized = true;
}
@@ -174,32 +179,32 @@ export class PaginationService {
return this._itemsPerPage;
}
- changeItemPerPage(itemsPerPage: number, event?: any): Promise {
+ changeItemPerPage(itemsPerPage: number, event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = 1;
this._pageCount = Math.ceil(this._totalItems / itemsPerPage);
this._itemsPerPage = itemsPerPage;
- return this.processOnPageChanged(this._pageNumber, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- goToFirstPage(event?: any): Promise {
+ goToFirstPage(event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = 1;
- return this.processOnPageChanged(this._pageNumber, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- goToLastPage(event?: any): Promise {
+ goToLastPage(event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = this._pageCount || 1;
- return this.processOnPageChanged(this._pageNumber || 1, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber || 1, event) : Promise.resolve(this.getFullPagination());
}
- goToNextPage(event?: any): Promise {
+ goToNextPage(event?: any, triggerChangeEvent = true): Promise {
if (this._pageNumber < this._pageCount) {
this._pageNumber++;
- return this.processOnPageChanged(this._pageNumber, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- return new Promise(resolve => resolve(false));
+ return Promise.resolve(false);
}
- goToPageNumber(pageNumber: number, event?: any): Promise {
+ goToPageNumber(pageNumber: number, event?: any, triggerChangeEvent = true): Promise {
const previousPageNumber = this._pageNumber;
if (pageNumber < 1) {
@@ -211,17 +216,17 @@ export class PaginationService {
}
if (this._pageNumber !== previousPageNumber) {
- return this.processOnPageChanged(this._pageNumber, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- return new Promise(resolve => resolve(false));
+ return Promise.resolve(false);
}
- goToPreviousPage(event?: any): Promise {
+ goToPreviousPage(event?: any, triggerChangeEvent = true): Promise {
if (this._pageNumber > 1) {
this._pageNumber--;
- return this.processOnPageChanged(this._pageNumber, event);
+ return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- return new Promise(resolve => resolve(false));
+ return Promise.resolve(false);
}
refreshPagination(isPageNumberReset = false, triggerChangedEvent = true, triggerInitializedEvent = false) {
@@ -277,6 +282,8 @@ export class PaginationService {
if (triggerInitializedEvent && !dequal(previousPagination, this.getFullPagination())) {
this.pubSubService.publish(`onPaginationPresetsInitialized`, this.getFullPagination());
}
+ const pagination = this.getFullPagination();
+ this._previousPagination = { pageNumber: pagination.pageNumber, pageSize: pagination.pageSize, pageSizes: pagination.pageSizes, totalItems: this.totalItems };
}
/** Reset the Pagination to first page and recalculate necessary numbers */
@@ -319,6 +326,11 @@ export class PaginationService {
}
processOnPageChanged(pageNumber: number, event?: Event | undefined): Promise {
+ if (this.pubSubService.publish('onBeforePaginationChange', this.getFullPagination()) === false) {
+ this.resetToPreviousPagination();
+ return Promise.resolve(this.getFullPagination());
+ }
+
return new Promise((resolve, reject) => {
this.recalculateFromToIndexes();
@@ -347,21 +359,31 @@ export class PaginationService {
process
.then((processResult: any) => {
this.backendUtilities?.executeBackendProcessesCallback(startTime, processResult, this._backendServiceApi as BackendServiceApi, this._totalItems);
+ const pagination = this.getFullPagination();
+ this._previousPagination = { pageNumber: pagination.pageNumber, pageSize: pagination.pageSize, pageSizes: pagination.pageSizes, totalItems: this.totalItems };
resolve(this.getFullPagination());
})
.catch((error) => {
+ this.resetToPreviousPagination();
this.backendUtilities?.onBackendError(error, this._backendServiceApi as BackendServiceApi);
- reject(process);
+ if (!this._backendServiceApi?.onError || !this.backendUtilities?.onBackendError) {
+ reject(process);
+ }
});
} else if (this.rxjs?.isObservable(process)) {
this._subscriptions.push(
(process as Observable).subscribe(
(processResult: any) => {
+ const pagination = this.getFullPagination();
+ this._previousPagination = { pageNumber: pagination.pageNumber, pageSize: pagination.pageSize, pageSizes: pagination.pageSizes, totalItems: this.totalItems };
resolve(this.backendUtilities?.executeBackendProcessesCallback(startTime, processResult, this._backendServiceApi as BackendServiceApi, this._totalItems));
},
(error: any) => {
+ this.resetToPreviousPagination();
this.backendUtilities?.onBackendError(error, this._backendServiceApi as BackendServiceApi);
- reject(process);
+ if (!this._backendServiceApi?.onError || !this.backendUtilities?.onBackendError) {
+ reject(process);
+ }
}
)
);
@@ -396,6 +418,29 @@ export class PaginationService {
}
}
+ /**
+ * Reset (revert) to previous pagination, it could be because you prevented `onBeforePaginationChange`, `onBeforePagingInfoChanged` from DataView OR a Backend Error was thrown.
+ * It will reapply the previous filter state in the UI.
+ */
+ resetToPreviousPagination() {
+ const hasPageNumberChange = this._previousPagination?.pageNumber !== this.getFullPagination().pageNumber;
+ const hasPageSizeChange = this._previousPagination?.pageSize !== this.getFullPagination().pageSize;
+
+ if (hasPageSizeChange) {
+ this.changeItemPerPage(this._previousPagination?.pageSize ?? 0, null, false);
+ }
+ if (hasPageNumberChange) {
+ this.goToPageNumber(this._previousPagination?.pageNumber ?? 0, null, false);
+ }
+
+ // refresh the pagination in the UI
+ // and re-update the Backend query string without triggering an actual query
+ if (hasPageNumberChange || hasPageSizeChange) {
+ this.refreshPagination();
+ this._backendServiceApi?.service?.updatePagination?.(this._previousPagination?.pageNumber ?? 0, this._previousPagination?.pageSize ?? 0);
+ }
+ }
+
updateTotalItems(totalItems: number, triggerChangedEvent = false) {
this._totalItems = totalItems;
if (this._paginationOptions) {
diff --git a/packages/pagination-component/src/slick-pagination.component.ts b/packages/pagination-component/src/slick-pagination.component.ts
index 1c0bfc365..aeb69cd9a 100644
--- a/packages/pagination-component/src/slick-pagination.component.ts
+++ b/packages/pagination-component/src/slick-pagination.component.ts
@@ -37,15 +37,15 @@ export class SlickPaginationComponent {
this._bindingHelper.querySelectorPrefix = `.${this.gridUid} `;
this.currentPagination = this.paginationService.getFullPagination();
- this._enableTranslate = this.gridOptions && this.gridOptions.enableTranslate || false;
- this._locales = this.gridOptions && this.gridOptions.locales || Constants.locales;
+ this._enableTranslate = this.gridOptions?.enableTranslate ?? false;
+ this._locales = this.gridOptions?.locales ?? Constants.locales;
if (this._enableTranslate && (!this.translaterService || !this.translaterService.translate)) {
throw new Error('[Slickgrid-Universal] requires a Translate Service to be installed and configured when the grid option "enableTranslate" is enabled.');
}
this.translatePaginationTexts(this._locales);
- if (this._enableTranslate && this.pubSubService && this.pubSubService.subscribe) {
+ if (this._enableTranslate && this.pubSubService?.subscribe) {
const translateEventName = this.translaterService?.eventName ?? 'onLanguageChange';
this._subscriptions.push(
this.pubSubService.subscribe(translateEventName, () => this.translatePaginationTexts(this._locales))
diff --git a/test/cypress/integration/example09.spec.js b/test/cypress/integration/example09.spec.js
index c9facbb7f..a0f6f2c24 100644
--- a/test/cypress/integration/example09.spec.js
+++ b/test/cypress/integration/example09.spec.js
@@ -640,7 +640,7 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => {
.should('not.exist');
// wait for the query to finish
- cy.get('[data-test=error-status]').should('contain', 'Cannot sort by the field "Company"');
+ cy.get('[data-test=error-status]').should('contain', 'Server could not sort using the field "Company"');
cy.get('[data-test=status]').should('contain', 'ERROR!!');
// same query string as prior test
@@ -676,7 +676,7 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => {
.type('Core');
// wait for the query to finish
- cy.get('[data-test=error-status]').should('contain', 'Cannot filter by the field "Company"');
+ cy.get('[data-test=error-status]').should('contain', 'Server could not filter using the field "Company"');
cy.get('[data-test=status]').should('contain', 'ERROR!!');
cy.get('[data-test=odata-query-result]')
@@ -710,6 +710,107 @@ describe('Example 09 - OData Grid', { retries: 1 }, () => {
.should(($span) => {
expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
});
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should display error when clicking on the "Throw Error..." button and not expect query and page to change', () => {
+ cy.get('[data-test="throw-page-error-btn"]').click({ force: true });
+ cy.wait(50);
+
+ cy.get('[data-test=error-status]').should('contain', 'Server timed out trying to retrieve data for the last page');
+ cy.get('[data-test=status]').should('contain', 'ERROR!!');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should display error when trying to change items per to 50,000 items and expect query & page to remain the same', () => {
+ cy.get('#items-per-page-label').select('50000');
+
+ cy.get('[data-test=error-status]').should('contain', 'Server timed out retrieving 50,000 rows');
+ cy.get('[data-test=status]').should('contain', 'ERROR!!');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should now go to next page without anymore problems and query & page should change as normal', () => {
+ cy.get('.icon-seek-next').click();
+
+ // wait for the query to finish
+ cy.get('[data-test=status]').should('contain', 'finished');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$skip=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('2'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('11');
+
+ cy.get('[data-test=item-to]')
+ .contains('20');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
});
});
});
diff --git a/test/cypress/integration/example15.spec.js b/test/cypress/integration/example15.spec.js
index 83445f4e4..bdad6cd0a 100644
--- a/test/cypress/integration/example15.spec.js
+++ b/test/cypress/integration/example15.spec.js
@@ -743,7 +743,7 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => {
.should('not.exist');
// wait for the query to finish
- cy.get('[data-test=error-status]').should('contain', 'Cannot sort by the field "Company"');
+ cy.get('[data-test=error-status]').should('contain', 'Server could not sort using the field "Company"');
cy.get('[data-test=status]').should('contain', 'ERROR!!');
// same query string as prior test
@@ -779,7 +779,7 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => {
.type('Core');
// wait for the query to finish
- cy.get('[data-test=error-status]').should('contain', 'Cannot filter by the field "Company"');
+ cy.get('[data-test=error-status]').should('contain', 'Server could not filter using the field "Company"');
cy.get('[data-test=status]').should('contain', 'ERROR!!');
cy.get('[data-test=odata-query-result]')
@@ -814,6 +814,107 @@ describe('Example 15 - OData Grid using RxJS', { retries: 1 }, () => {
.should(($span) => {
expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
});
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should display error when clicking on the "Throw Error..." button and not expect query and page to change', () => {
+ cy.get('[data-test="throw-page-error-btn"]').click({ force: true });
+ cy.wait(50);
+
+ cy.get('[data-test=error-status]').should('contain', 'Server timed out trying to retrieve data for the last page');
+ cy.get('[data-test=status]').should('contain', 'ERROR!!');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should display error when trying to change items per to 50,000 items and expect query & page to remain the same', () => {
+ cy.get('#items-per-page-label').select('50000');
+
+ cy.get('[data-test=error-status]').should('contain', 'Server timed out retrieving 50,000 rows');
+ cy.get('[data-test=status]').should('contain', 'ERROR!!');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('1'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('1');
+
+ cy.get('[data-test=item-to]')
+ .contains('10');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
+ });
+
+ it('should now go to next page without anymore problems and query & page should change as normal', () => {
+ cy.get('.icon-seek-next').click();
+
+ // wait for the query to finish
+ cy.get('[data-test=status]').should('contain', 'finished');
+
+ cy.get('[data-test=odata-query-result]')
+ .should(($span) => {
+ expect($span.text()).to.eq(`$top=10&$skip=10&$orderby=Name desc&$filter=(Gender eq 'female')`);
+ });
+
+ cy.get('[data-test=page-number-input]')
+ .invoke('val')
+ .then(pageNumber => expect(pageNumber).to.eq('2'));
+
+ cy.get('[data-test=page-count]')
+ .contains('5');
+
+ cy.get('[data-test=item-from]')
+ .contains('11');
+
+ cy.get('[data-test=item-to]')
+ .contains('20');
+
+ cy.get('[data-test=total-items]')
+ .contains('50');
});
});
});