From a3dd0ddba16566b4bbb7badb3748e04daeadc06f Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Mon, 15 Feb 2021 10:20:15 -0500 Subject: [PATCH] fix(backend): incorrect item with GraphQL and useLocalFiltering --- .../grid-graphql-nopage.component.html | 4 +- .../angular-slickgrid-constructor.spec.ts | 1 + .../components/angular-slickgrid.component.ts | 2 +- .../filter-conditions/filterUtilities.ts | 4 +- .../formatters/decimalFormatter.ts | 1 - .../services/filter.service.ts | 4 +- test/cypress/integration/example27.spec.js | 157 ++++++++++++++++++ 7 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 test/cypress/integration/example27.spec.js diff --git a/src/app/examples/grid-graphql-nopage.component.html b/src/app/examples/grid-graphql-nopage.component.html index d9ac319a1..42b954a06 100644 --- a/src/app/examples/grid-graphql-nopage.component.html +++ b/src/app/examples/grid-graphql-nopage.component.html @@ -13,7 +13,7 @@

{{title}}

- + diff --git a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts index 94f35c64c..375ddc306 100644 --- a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts +++ b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts @@ -172,6 +172,7 @@ const mockDataView = { beginUpdate: jest.fn(), endUpdate: jest.fn(), getItem: jest.fn(), + getItemCount: jest.fn(), getItems: jest.fn(), getLength: jest.fn(), getItemMetadata: jest.fn(), diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts index efc0b1b42..33acc2e27 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts @@ -772,7 +772,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn startTime: new Date(), endTime: new Date(), itemCount: itemCount, - totalItemCount: Array.isArray(this.dataset) ? this.dataset.length : 0 + totalItemCount: this.dataView && this.dataView.getItemCount() || 0 }; // when using local (in-memory) dataset, we'll display a warning message when filtered data is empty diff --git a/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts b/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts index 302c64022..9baf37a89 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts @@ -73,13 +73,13 @@ export const testFilterCondition = (operator: OperatorString, value1: any, value return ((value2 && Array.isArray(value2 as string[])) ? (!value2.includes(value1)) : false); case 'IN_CONTAINS': if (value2 && Array.isArray(value2) && typeof value1 === 'string') { - return value2.some(item => value1.split(',').includes(item)); + return value2.some(item => value1.split(/[\s,]+/).includes(item)); } return false; case 'NIN_CONTAINS': case 'NOT_IN_CONTAINS': if (value2 && Array.isArray(value2) && typeof value1 === 'string') { - return !value2.some(item => value1.split(',').includes(item)); + return !value2.some(item => value1.split(/[\s,]+/).includes(item)); } return false; } diff --git a/src/app/modules/angular-slickgrid/formatters/decimalFormatter.ts b/src/app/modules/angular-slickgrid/formatters/decimalFormatter.ts index 6e8aa2e26..9004bfb7f 100644 --- a/src/app/modules/angular-slickgrid/formatters/decimalFormatter.ts +++ b/src/app/modules/angular-slickgrid/formatters/decimalFormatter.ts @@ -26,4 +26,3 @@ export const decimalFormatter: Formatter = (row: number, cell: number, value: an } return value; }; - diff --git a/src/app/modules/angular-slickgrid/services/filter.service.ts b/src/app/modules/angular-slickgrid/services/filter.service.ts index a836df12e..f648a03f3 100644 --- a/src/app/modules/angular-slickgrid/services/filter.service.ts +++ b/src/app/modules/angular-slickgrid/services/filter.service.ts @@ -23,7 +23,7 @@ import { SlickEventHandler, } from './../models/index'; import { executeBackendCallback, refreshBackendDataset } from './backend-utilities'; -import { deepCopy, getDescendantProperty, mapOperatorByFieldType } from './utilities'; +import { deepCopy, getDescendantProperty, mapOperatorByFieldType, sanitizeHtmlToText } from './utilities'; import { FilterConditions, getParsedSearchTermsByFieldType } from './../filter-conditions'; import { FilterFactory } from '../filters/filterFactory'; import { SharedService } from './shared.service'; @@ -472,11 +472,13 @@ export class FilterService { } // when using localization (i18n), we should use the formatter output to search as the new cell value + // we will also sanitize/remove HTML tags out of the text (which might be added by multiple-select) if (columnDef && columnDef.params && columnDef.params.useFormatterOuputToFilter) { const dataView = grid.getData(); const idPropName = this._gridOptions.datasetIdPropertyName || 'id'; const rowIndex = (dataView && typeof dataView.getIdxById === 'function') ? dataView.getIdxById(item[idPropName]) : 0; cellValue = (columnDef && typeof columnDef.formatter === 'function') ? columnDef.formatter(rowIndex || 0, columnIndex, cellValue, columnDef, item, this._grid) : ''; + cellValue = sanitizeHtmlToText(cellValue); // also remove any html tag } // make sure cell value is always a string diff --git a/test/cypress/integration/example27.spec.js b/test/cypress/integration/example27.spec.js new file mode 100644 index 000000000..35d34f8eb --- /dev/null +++ b/test/cypress/integration/example27.spec.js @@ -0,0 +1,157 @@ +/// + +describe('Example 27 - GraphQL Basic API without Pagination', () => { + const GRID_ROW_HEIGHT = 35; + const fullPreTitles = ['Country', 'Language', 'Continent']; + const fullTitles = ['Code', 'Name', 'Native', 'Phone Area Code', 'Currency', 'Emoji', 'Names', 'Native', 'Codes', 'Name', 'Code']; + + it('should display Example title', () => { + cy.visit(`${Cypress.config('baseExampleUrl')}/graphql-nopage`); + cy.get('h2').should('contain', 'Example 27: GraphQL Basic API without Pagination'); + }); + + it('should display a processing alert which will change to done', () => { + cy.get('[data-test=status]').should('contain', 'processing'); + cy.get('[data-test=status]').should('contain', 'done'); + }); + + it('should have exact Column Pre-Header & Column Header Titles in the grid', () => { + cy.get('#grid27') + .find('.slick-header-columns:nth(0)') + .children() + .each(($child, index) => expect($child.text()).to.eq(fullPreTitles[index])); + + cy.get('#grid27') + .find('.slick-header-columns:nth(1)') + .children() + .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); + }); + + it('should expect first 3 rows to be an exact match of data provided by the external GraphQL API', () => { + cy.get('.right-footer.metrics') + .contains('250 of 250 items'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'AD'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'Andorra'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'Andorra'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(3)`).should('contain', '376'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(4)`).should('contain', 'EUR'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(6)`).should('contain', 'Catalan'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(7)`).should('contain', 'Català'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(8)`).should('contain', 'ca'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(9)`).should('contain', 'Europe'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(10)`).should('contain', 'EU'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'AE'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'United Arab Emirates'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', 'دولة الإمارات العربية المتحدة'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(3)`).should('contain', '971'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(4)`).should('contain', 'AED'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(6)`).should('contain', 'Arabic'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(7)`).should('contain', 'العربية'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(8)`).should('contain', 'ar'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(9)`).should('contain', 'Asia'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(10)`).should('contain', 'AS'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(0)`).should('contain', 'AF'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).should('contain', 'Afghanistan'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).should('contain', 'افغانستان'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(3)`).should('contain', '93'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(4)`).should('contain', 'AFN'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(6)`).should('contain', 'Pashto, Uzbek, Turkmen'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(7)`).should('contain', 'پښتو, Ўзбек, Туркмен / تركمن'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(8)`).should('contain', 'ps, uz, tk'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(9)`).should('contain', 'Asia'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(10)`).should('contain', 'AS'); + }); + + it('should sort by country name and expect first 2 rows as Afghanistan and Albania', () => { + cy.get(`.slick-header-columns:nth(1) .slick-header-column:nth-child(2)`).contains('Name').click(); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'AF'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'Afghanistan'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'افغانستان'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(3)`).should('contain', '93'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'AL'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'Albania'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', 'Shqipëria'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(3)`).should('contain', '355'); + }); + + it('should filter by Language Codes "fr, de" and expect 2 rows of data in the grid', () => { + cy.get('.search-filter.filter-languageCode') + .type('fr, de'); + + cy.get('.right-footer.metrics') + .contains('2 of 250 items'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'BE'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'Belgium'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'België'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(3)`).should('contain', '32'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(4)`).should('contain', 'EUR'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(6)`).should('contain', 'Dutch, French, German'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(7)`).should('contain', 'Nederlands, Français, Deutsch'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(8)`).should('contain', 'nl, fr, de'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(9)`).should('contain', 'Europe'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(10)`).should('contain', 'EU'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'LU'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'Luxembourg'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', 'Luxembourg'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(3)`).should('contain', '352'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(4)`).should('contain', 'EUR'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(6)`).should('contain', 'French, German, Luxembourgish'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(7)`).should('contain', 'Français, Deutsch, Lëtzebuergesch'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(8)`).should('contain', 'fr, de, lb'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(9)`).should('contain', 'Europe'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(10)`).should('contain', 'EU'); + }); + + it('should Clear all Filters and expect all rows to be back', () => { + cy.get('#grid27') + .find('button.slick-gridmenu-button') + .trigger('click') + .click(); + + cy.get(`.slick-gridmenu:visible`) + .find('.slick-gridmenu-item') + .first() + .find('span') + .contains('Clear all Filters') + .click(); + + cy.get('.right-footer.metrics') + .contains('250 of 250 items'); + }); + + it('should filter Language Native with "Aymar" and expect only 1 row in the grid', () => { + cy.get('div.ms-filter.filter-languageNative') + .trigger('click'); + + cy.get('.ms-search:visible') + .type('Aymar'); + + cy.get('.ms-drop:visible') + .contains('Aymar') + .click(); + + cy.get('.ms-ok-button:visible') + .click(); + + cy.get('.right-footer.metrics') + .contains('1 of 250 items'); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'BO'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'Bolivia'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'Bolivia'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(3)`).should('contain', '591'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(4)`).should('contain', 'BOB,BOV'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(6)`).should('contain', 'Spanish, Aymara, Quechua'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(7)`).should('contain', 'Español, Aymar, Runa Simi'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(8)`).should('contain', 'es, ay, qu'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(9)`).should('contain', 'South America'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(10)`).should('contain', 'SA'); + }); +});