diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts index e64b3466b..616fd8a3b 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts @@ -45,6 +45,6 @@ describe('getParsedSearchTermsByFieldType method', () => { const input = 'world'; const result = getParsedSearchTermsByFieldType([input], FieldType.string); - expect(result).toBe(input); + expect(result).toEqual([input]); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/stringFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/stringFilterCondition.spec.ts index 387245fb8..8f7df384c 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/stringFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/stringFilterCondition.spec.ts @@ -128,4 +128,46 @@ describe('executeStringFilterCondition method', () => { const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); + + it('should return True when input value is in the range of search terms using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'c', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return True when input value is on the inclusive limit range of search terms using 2 dots (..) notation AND no operator provided except a defaultFilterRangeOperator is rangeInclusive', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeInclusive, cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return False when input value is on the inclusive limit range of search terms using 2 dots (..) notation AND no operator provided except a defaultFilterRangeOperator is rangeExclusive', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', defaultFilterRangeOperator: OperatorType.rangeExclusive, cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); + + it('should return False when input value is not in the range of search terms using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'g', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); + + it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive" using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive" using 2 dots (..) notation', () => { + const searchTerms = ['b..e']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: 'b', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(false); + }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts b/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts index 34f7e6734..bfd03bc9d 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts @@ -38,7 +38,7 @@ export const executeFilterConditionTest: FilterCondition = (options: FilterCondi case 'text': default: // the parsedSearchTerms should be single value (result came from getFilterParsedText() method) - return executeStringFilterCondition(options, parsedSearchTerms as SearchTerm); + return executeStringFilterCondition(options, (parsedSearchTerms || []) as string[]); } }; @@ -67,7 +67,7 @@ export function getParsedSearchTermsByFieldType(inputSearchTerms: SearchTerm[] | parsedSearchValues = getFilterParsedObjectResult(inputSearchTerms); break; case 'text': - parsedSearchValues = getFilterParsedText(inputSearchTerms); + parsedSearchValues = getFilterParsedText(inputSearchTerms) as SearchTerm[]; break; } return parsedSearchValues; diff --git a/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts index 286c486f5..dab58b439 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts @@ -1,9 +1,12 @@ -import { FilterCondition, FilterConditionOption, OperatorType, SearchTerm } from '../models/index'; +import { FilterCondition, FilterConditionOption, OperatorString, OperatorType, SearchTerm } from '../models/index'; import { testFilterCondition } from './filterUtilities'; + /** Execute filter condition check on each cell */ -export const executeStringFilterCondition: FilterCondition = ((options: FilterConditionOption, parsedSearchValue: string | undefined) => { - if (parsedSearchValue === undefined && !options.operator) { +export const executeStringFilterCondition: FilterCondition = ((options: FilterConditionOption, parsedSearchValues: string[]) => { + let [searchValue1, searchValue2] = parsedSearchValues; + + if (searchValue1 === undefined && !options.operator) { return true; } @@ -12,28 +15,64 @@ export const executeStringFilterCondition: FilterCondition = ((options: FilterCo // make both the cell value and search value lower for case insensitive comparison const cellValue = options.cellValue.toLowerCase(); - if (typeof parsedSearchValue === 'string') { - parsedSearchValue = parsedSearchValue.toLowerCase(); + if (typeof searchValue1 === 'string') { + searchValue1 = searchValue1.toLowerCase(); + } + if (typeof searchValue2 === 'string') { + searchValue2 = searchValue2.toLowerCase(); } - if (options.operator === '*' || options.operator === OperatorType.endsWith) { - return cellValue.endsWith(parsedSearchValue); - } else if ((options.operator === '' && options.searchInputLastChar === '*') || options.operator === OperatorType.startsWith) { - return cellValue.startsWith(parsedSearchValue); - } else if (options.operator === '' || options.operator === OperatorType.contains) { - return (cellValue.indexOf(parsedSearchValue) > -1); - } else if (options.operator === '<>' || options.operator === OperatorType.notContains) { - return (cellValue.indexOf(parsedSearchValue) === -1); + if (searchValue1 !== undefined && searchValue2 !== undefined) { + let operator = options && options.operator || options.defaultFilterRangeOperator; + if (operator !== OperatorType.rangeInclusive && operator !== OperatorType.rangeExclusive) { + operator = options.defaultFilterRangeOperator; + } + const isInclusive = operator === OperatorType.rangeInclusive; + const searchResult1 = testStringCondition((isInclusive ? '>=' : '>'), cellValue, searchValue1, options.searchInputLastChar); + const searchResult2 = testStringCondition((isInclusive ? '<=' : '<'), cellValue, searchValue2, options.searchInputLastChar); + return searchResult1 && searchResult2; } - return testFilterCondition(options.operator || '==', cellValue, parsedSearchValue); + const searchResult1 = testStringCondition(options.operator, cellValue, searchValue1, options.searchInputLastChar); + return searchResult1; }) as FilterCondition; /** * From our search filter value(s), get the parsed value(s). * This is called only once per filter before running the actual filter condition check on each cell */ -export function getFilterParsedText(inputSearchTerms: SearchTerm[] | undefined): SearchTerm { - let parsedSearchValue = (Array.isArray(inputSearchTerms) && inputSearchTerms.length > 0) ? inputSearchTerms[0] : ''; - parsedSearchValue = parsedSearchValue === undefined || parsedSearchValue === null ? '' : `${parsedSearchValue}`; // make sure it's a string - return parsedSearchValue; +export function getFilterParsedText(inputSearchTerms: SearchTerm[] | undefined): SearchTerm[] { + const defaultSearchTerm = ''; // when nothing is provided, we'll default to 0 + const searchTerms = Array.isArray(inputSearchTerms) && inputSearchTerms || [defaultSearchTerm]; + const parsedSearchValues: string[] = []; + let searchValue1; + let searchValue2; + if (searchTerms.length === 2 || (typeof searchTerms[0] === 'string' && (searchTerms[0] as string).indexOf('..') > 0)) { + const searchValues = (searchTerms.length === 2) ? searchTerms : (searchTerms[0] as string).split('..'); + searchValue1 = `${Array.isArray(searchValues) ? searchValues[0] : ''}`; + searchValue2 = `${Array.isArray(searchValues) ? searchValues[1] : ''}`; + } else { + const parsedSearchValue = (Array.isArray(inputSearchTerms) && inputSearchTerms.length > 0) ? inputSearchTerms[0] : ''; + searchValue1 = parsedSearchValue === undefined || parsedSearchValue === null ? '' : `${parsedSearchValue}`; // make sure it's a string + } + + if (searchValue1 !== undefined && searchValue2 !== undefined) { + parsedSearchValues.push(searchValue1 as string, searchValue2 as string); + } else if (searchValue1 !== undefined) { + parsedSearchValues.push(searchValue1 as string); + } + return parsedSearchValues; +} + +/** Execute the filter string test condition, returns a boolean */ +function testStringCondition(operator: OperatorType | OperatorString, cellValue: string, searchValue: string, searchInputLastChar?: string): boolean { + if (operator === '*' || operator === OperatorType.endsWith) { + return cellValue.endsWith(searchValue); + } else if ((operator === '' && searchInputLastChar === '*') || operator === OperatorType.startsWith) { + return cellValue.startsWith(searchValue); + } else if (operator === '' || operator === OperatorType.contains) { + return (cellValue.indexOf(searchValue) > -1); + } else if (operator === '<>' || operator === OperatorType.notContains) { + return (cellValue.indexOf(searchValue) === -1); + } + return testFilterCondition(operator || '==', cellValue, searchValue); } diff --git a/src/app/modules/angular-slickgrid/services/__tests__/filter.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/filter.service.spec.ts index edd53f862..59163c079 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/filter.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/filter.service.spec.ts @@ -314,7 +314,7 @@ describe('FilterService', () => { }); it('should execute the callback normally when a input change event is triggered and searchTerms are defined', () => { - const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; + const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; const spySearchChange = jest.spyOn(service.onSearchChange, 'notify'); service.init(gridStub); @@ -331,13 +331,13 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['John'], - parsedSearchTerms: 'John', + parsedSearchTerms: ['John'], grid: gridStub }, expect.anything()); }); it('should execute the callback normally when a input change event is triggered and the searchTerm comes from this event.target', () => { - const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; + const expectationColumnFilter = { columnDef: mockColumn, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; const spySearchChange = jest.spyOn(service.onSearchChange, 'notify'); service.init(gridStub); @@ -357,7 +357,7 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['John'], - parsedSearchTerms: 'John', + parsedSearchTerms: ['John'], grid: gridStub }, expect.anything()); }); @@ -428,7 +428,7 @@ describe('FilterService', () => { describe('clearFilterByColumnId method', () => { it('should clear the filter by passing a column id as argument on a backend grid', () => { - const filterExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string }; + const filterExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string }; const newEvent = new Event('mouseup'); const spyClear = jest.spyOn(service.getFiltersMetadata()[0], 'clear'); const spyFilterChange = jest.spyOn(service, 'onBackendFilterChange'); @@ -447,8 +447,8 @@ describe('FilterService', () => { }); it('should not call "onBackendFilterChange" method when the filter is previously empty', () => { - const filterFirstExpectation = { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string }; - const filterLastExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string }; + const filterFirstExpectation = { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string }; + const filterLastExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string }; const newEvent = new Event('mouseup'); const spyClear = jest.spyOn(service.getFiltersMetadata()[2], 'clear'); const spyFilterChange = jest.spyOn(service, 'onBackendFilterChange'); @@ -576,7 +576,7 @@ describe('FilterService', () => { expect(spyClear).toHaveBeenCalled(); expect(filterCountBefore).toBe(2); expect(filterCountAfter).toBe(1); - expect(service.getColumnFilters()).toEqual({ lastName: { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string } }); + expect(service.getColumnFilters()).toEqual({ lastName: { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string } }); expect(spyEmitter).toHaveBeenCalledWith('local'); }); }); @@ -599,7 +599,7 @@ describe('FilterService', () => { const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); - expect(columnFilters.firstName['parsedSearchTerms']).toBe('John'); + expect(columnFilters.firstName['parsedSearchTerms']).toEqual(['John']); expect(output).toBe(true); }); @@ -864,7 +864,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John Doe'], parsedSearchTerms: 'John Doe', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John Doe'], parsedSearchTerms: ['John Doe'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -876,7 +876,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: 'John', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['John'], parsedSearchTerms: ['John'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); @@ -888,7 +888,7 @@ describe('FilterService', () => { jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string } }; + const columnFilters = { name: { columnDef: mockColumn1, columnId: 'name', operator: 'EQ', searchTerms: ['Doe'], parsedSearchTerms: ['Doe'], type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); @@ -1104,7 +1104,7 @@ describe('FilterService', () => { expect(emitSpy).toHaveBeenCalledWith('local'); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); }); @@ -1122,7 +1122,7 @@ describe('FilterService', () => { expect(emitSpy).not.toHaveBeenCalled(); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); }); @@ -1148,7 +1148,7 @@ describe('FilterService', () => { expect(backendProcessSpy).not.toHaveBeenCalled(); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); expect(clearSpy).toHaveBeenCalledWith(false); @@ -1177,7 +1177,7 @@ describe('FilterService', () => { expect(mockRefreshBackendDataset).not.toHaveBeenCalled(); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: 'Jane', type: FieldType.string }, + firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith', parsedSearchTerms: ['Jane'], type: FieldType.string }, isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ', parsedSearchTerms: false, type: FieldType.boolean } }); expect(clearSpy).toHaveBeenCalledWith(false); @@ -1499,7 +1499,7 @@ describe('FilterService', () => { gridStub.onHeaderRowCellRendered.notify(mockArgs1, new Slick.EventData(), gridStub); gridStub.onHeaderRowCellRendered.notify(mockArgs2, new Slick.EventData(), gridStub); - const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: 'map', type: FieldType.string } }; + const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: ['map'], type: FieldType.string } }; service.updateFilters([{ columnId: 'file', operator: '', searchTerms: ['map'] }], true, true, true); const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); @@ -1523,7 +1523,7 @@ describe('FilterService', () => { gridStub.onHeaderRowCellRendered.notify(mockArgs1 as any, new Slick.EventData(), gridStub); gridStub.onHeaderRowCellRendered.notify(mockArgs2 as any, new Slick.EventData(), gridStub); - const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: 'map', type: FieldType.string } }; + const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', operator: 'Contains', searchTerms: ['map'], parsedSearchTerms: ['map'], type: FieldType.string } }; service.updateFilters([{ columnId: 'file', operator: '', searchTerms: ['map'] }], true, true, true); const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); @@ -1553,7 +1553,7 @@ describe('FilterService', () => { expect(spyRxjs).toHaveBeenCalledWith([{ columnId: 'file', operator: 'Contains', searchTerms: ['unknown'] }]); expect(output).toBe(false); - expect(preFilterSpy).toHaveBeenCalledWith(dataset, { ...columnFilters, file: { ...columnFilters.file, operator: 'Contains', parsedSearchTerms: 'unknown', type: 'string' } }); // it will use Contains by default + expect(preFilterSpy).toHaveBeenCalledWith(dataset, { ...columnFilters, file: { ...columnFilters.file, operator: 'Contains', parsedSearchTerms: ['unknown'], type: 'string' } }); // it will use Contains by default expect(preFilterSpy).toHaveReturnedWith([]); }); }); diff --git a/test/cypress/integration/example27.spec.js b/test/cypress/integration/example27.spec.js index aff4a57d0..afe7cf213 100644 --- a/test/cypress/integration/example27.spec.js +++ b/test/cypress/integration/example27.spec.js @@ -126,6 +126,14 @@ describe('Example 27 - GraphQL Basic API without Pagination', () => { .contains('250 of 250 items'); }); + it('should be able to filter "Country of Origin" with a text range filter "b..e" and expect to see only Canada showing up', () => { + cy.get('input.search-filter.filter-countryName').type('b..e'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'Bosnia and Herzegovina'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'Barbados'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).should('contain', 'Bangladesh'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(1)`).should('contain', 'Belgium'); + }); + it('should filter Language Native with "Aymar" and expect only 1 row in the grid', () => { cy.get('div.ms-filter.filter-languageNative') .trigger('click');