From bd75eb086b657a570f858f1c564e077656f04b64 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Fri, 12 Feb 2021 14:06:43 -0500 Subject: [PATCH 1/2] feat(perf): huge filtering execution speed improvements The problem was that previous code was parsing the search values at the same time as processing the filtering check and that is totally unnecessary, the search value once provided will never change when comparing to each cell. This PR is rewriting the Filter Service to process the search values only once (before looping through all cells). Parsing Dates are the biggest performance killer because we take dates, that could be in any format, and we parse (convert) them to MomentJS object and that is expensive on 50,000 or more rows (see below for perf logs before/after). --- .../__tests__/booleanFilterCondition.spec.ts | 44 ++- .../collectionSearchFilterCondition.spec.ts | 40 +-- .../__tests__/dateEuroFilterCondition.spec.ts | 76 ++-- .../dateEuroShortFilterCondition.spec.ts | 76 ++-- .../__tests__/dateFilterCondition.spec.ts | 76 ++-- .../__tests__/dateIsoFilterCondition.spec.ts | 338 +++++++++++++----- .../__tests__/dateUsFilterCondition.spec.ts | 83 +++-- .../dateUsShortFilterCondition.spec.ts | 83 +++-- .../__tests__/dateUtcFilterCondition.spec.ts | 76 ++-- .../filterConditionProcesses.spec.ts | 50 +++ .../__tests__/filterUtilities.spec.ts | 3 +- .../__tests__/numberFilterCondition.spec.ts | 129 ++++--- .../__tests__/objectFilterCondition.spec.ts | 84 +++-- .../__tests__/stringFilterCondition.spec.ts | 84 +++-- .../booleanFilterCondition.ts | 17 +- .../collectionSearchFilterCondition.ts | 8 +- .../filter-conditions/dateFilterCondition.ts | 73 ++++ .../executeMappedCondition.ts | 123 ------- .../filterConditionProcesses.ts | 127 +++++++ .../filterConditions.index.ts | 15 + .../filter-conditions/filterUtilities.ts | 3 +- .../filter-conditions/index.ts | 24 +- .../numberFilterCondition.ts | 54 ++- .../objectFilterCondition.ts | 26 +- .../stringFilterCondition.ts | 35 +- .../filters/filters.index.ts | 2 +- .../models/column.interface.ts | 7 +- .../models/columnFilters.interface.ts | 4 +- .../models/filterCondition.interface.ts | 3 +- .../models/filterConditionOption.interface.ts | 28 +- .../modules/angular-slickgrid/models/index.ts | 1 + .../models/searchColumnFilter.interface.ts | 34 ++ .../services/__tests__/filter.service.spec.ts | 173 +++++---- .../services/__tests__/utilities.spec.ts | 28 ++ .../services/filter.service.ts | 261 +++++++++----- .../services/graphql.service.ts | 2 +- .../services/grid-odata.service.ts | 2 +- .../angular-slickgrid/services/utilities.ts | 13 + test/cypress/integration/example03.spec.js | 26 ++ test/cypress/package.json | 2 +- 40 files changed, 1557 insertions(+), 776 deletions(-) create mode 100644 src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts create mode 100644 src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts delete mode 100644 src/app/modules/angular-slickgrid/filter-conditions/executeMappedCondition.ts create mode 100644 src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts create mode 100644 src/app/modules/angular-slickgrid/filter-conditions/filterConditions.index.ts create mode 100644 src/app/modules/angular-slickgrid/models/searchColumnFilter.interface.ts diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/booleanFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/booleanFilterCondition.spec.ts index b5d56b171..4ad54c7d7 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/booleanFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/booleanFilterCondition.spec.ts @@ -1,55 +1,63 @@ import { FieldType, FilterConditionOption } from '../../models/index'; -import { booleanFilterCondition } from '../booleanFilterCondition'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { executeBooleanFilterCondition, getFilterParsedBoolean } from '../booleanFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; /** will return True in all cases with only 1 exception when the only searchTerm is inversed to the cell value */ -describe('booleanFilterCondition method', () => { +describe('executeBooleanFilterCondition method', () => { it('should return True when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.boolean } as FilterConditionOption; - const output = booleanFilterCondition(options); + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); it('should return True when any cell value is provided', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.boolean } as FilterConditionOption; - const output = booleanFilterCondition(options); + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); - it('should return True when boolean value True is provided as cell value and called from the "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when boolean value True is provided as cell value and called from the "executeFilterConditionTest"', () => { + const searchTerms = ['true']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); it('should return True when boolean value True is provided as cell value', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption; - const output = booleanFilterCondition(options); + const searchTerms = ['true']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); it('should return True when boolean value provided is equal to the searchTerms even when it is a string type', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption; - const output = booleanFilterCondition(options); + const searchTerms = ['true']; + const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); it('should return True when the cell value is equal to at least 1 of the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms: ['true', 'false'] } as FilterConditionOption; - const output = booleanFilterCondition(options); + const searchTerms = ['true', 'false']; + const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(true); }); it('should return False when cell value is inversed to the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: false, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption; - const output = booleanFilterCondition(options); + const searchTerms = ['true']; + const options = { dataKey: '', operator: 'EQ', cellValue: false, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: 'NE', cellValue: false, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption; - const output = booleanFilterCondition(options); + const searchTerms = ['true']; + const options = { dataKey: '', operator: 'NE', cellValue: false, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption; + const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/collectionSearchFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/collectionSearchFilterCondition.spec.ts index 9ed43294a..fcd3bae4d 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/collectionSearchFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/collectionSearchFilterCondition.spec.ts @@ -1,59 +1,59 @@ import { FieldType, FilterConditionOption } from '../../models/index'; -import { collectionSearchFilterCondition } from '../collectionSearchFilterCondition'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { executeCollectionSearchFilterCondition } from '../collectionSearchFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; -describe('collectionSearchFilterCondition method', () => { +describe('executeCollectionSearchFilterCondition method', () => { it('should return False when searchTerms is empty', () => { const options = { dataKey: '', operator: 'IN', cellValue: 3, fieldType: FieldType.string } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(false); }); it('should return True when input value is in the searchTerms', () => { const options = { dataKey: '', operator: 'IN', cellValue: 3, fieldType: FieldType.string, searchTerms: ['3'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms', () => { const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms even though there are no Operator provided (it will use EQ as default)', () => { const options = { dataKey: '', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(true); }); it('should return True when the cell value is equal to at least 1 of the searchTerms', () => { const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar', 'foo', 'John'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(true); }); - it('should return True when the cell value is equal to at least 1 of the searchTerms and called by the "executeMappedCondition"', () => { + it('should return True when the cell value is equal to at least 1 of the searchTerms and called by the "executeFilterConditionTest"', () => { const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar', 'foo', 'John'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options); expect(output).toBe(true); }); it('should return False when cell value is not within the searchTerms', () => { const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(false); }); it('should return False even when Operator is Not IN because condition is always a strict equal check', () => { const options = { dataKey: '', operator: 'NOT_IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(false); }); it('should return False even when Operator is NIN because condition is always a strict equal check', () => { const options = { dataKey: '', operator: 'NIN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(false); }); @@ -61,8 +61,8 @@ describe('collectionSearchFilterCondition method', () => { const options1 = { dataKey: '', operator: 'NIN', cellValue: 'bar', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; const options2 = { dataKey: '', operator: 'NOT_IN', cellValue: 'bar', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output1 = collectionSearchFilterCondition(options1); - const output2 = collectionSearchFilterCondition(options2); + const output1 = executeCollectionSearchFilterCondition(options1); + const output2 = executeCollectionSearchFilterCondition(options2); expect(output1).toBe(true); expect(output2).toBe(true); @@ -70,15 +70,15 @@ describe('collectionSearchFilterCondition method', () => { it('should return True when input value contains searchTerms content', () => { const options = { dataKey: '', operator: 'IN_CONTAINS', cellValue: 'Task2,Task3', fieldType: FieldType.string, searchTerms: ['Task2', 'Task3'] } as FilterConditionOption; - const output = collectionSearchFilterCondition(options); + const output = executeCollectionSearchFilterCondition(options); expect(output).toBe(true); }); it('should return True when input value is not found when using "not in contains" searchTerms content', () => { const options1 = { dataKey: '', operator: 'NIN_CONTAINS', cellValue: 'Task11,Task22,Task33', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption; const options2 = { dataKey: '', operator: 'NOT_IN_CONTAINS', cellValue: 'Task11,Task22,Task33', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption; - const output1 = collectionSearchFilterCondition(options1); - const output2 = collectionSearchFilterCondition(options2); + const output1 = executeCollectionSearchFilterCondition(options1); + const output2 = executeCollectionSearchFilterCondition(options2); expect(output1).toBe(true); expect(output2).toBe(true); @@ -87,8 +87,8 @@ describe('collectionSearchFilterCondition method', () => { it('should return False when input value not in contains searchTerms content', () => { const options1 = { dataKey: '', operator: 'NIN_CONTAINS', cellValue: 'Task1,Task3', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption; const options2 = { dataKey: '', operator: 'NOT_IN_CONTAINS', cellValue: 'Task1,Task3', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption; - const output1 = collectionSearchFilterCondition(options1); - const output2 = collectionSearchFilterCondition(options2); + const output1 = executeCollectionSearchFilterCondition(options1); + const output2 = executeCollectionSearchFilterCondition(options2); expect(output1).toBe(false); expect(output2).toBe(false); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroFilterCondition.spec.ts index 07030607c..58dc5c6cf 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroFilterCondition.spec.ts @@ -1,94 +1,110 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateEuroFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/2000' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/2000', searchTerms: ['14/25/2000'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/25/2000']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/2000', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['25/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/1993']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['14/03/2003'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/03/2003']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['14/03/2003', '25/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/03/2003', '25/12/1993']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['25/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/1993']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['25/12/2002'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/2002']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms: ['27/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['27/12/1993']; + const options = { dataKey: '', fieldType: FieldType.dateEuro, cellValue: '25/12/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '25/12/1993', fieldType: FieldType.dateEuro, searchTerms: ['01/12/1993..31/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/1993..31/12/1993']; + const options = { dataKey: '', operator: 'EQ', cellValue: '25/12/1993', fieldType: FieldType.dateEuro, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '25/11/1993', fieldType: FieldType.dateEuro, searchTerms: ['01/12/1993..31/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/1993..31/12/1993']; + const options = { dataKey: '', operator: 'EQ', cellValue: '25/11/1993', fieldType: FieldType.dateEuro, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '01/12/1993', fieldType: FieldType.dateEuro, searchTerms: ['01/12/1993..31/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/1993..31/12/1993']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '01/12/1993', fieldType: FieldType.dateEuro, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '01/12/1993', fieldType: FieldType.dateEuro, searchTerms: ['01/12/1993..31/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/1993..31/12/1993']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '01/12/1993', fieldType: FieldType.dateEuro, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '05/12/1993', fieldType: FieldType.dateEuro, searchTerms: ['01/12/1993..60/12/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/1993..60/12/1993']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '05/12/1993', fieldType: FieldType.dateEuro, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuro)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroShortFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroShortFilterCondition.spec.ts index 0193d1cc5..f60a52762 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroShortFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateEuroShortFilterCondition.spec.ts @@ -1,94 +1,110 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateEuroShortFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/00' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/00', searchTerms: ['14/25/00'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/25/00']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/00', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['25/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/93']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['14/03/03'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/03/03']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['14/03/2003', '25/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['14/03/2003', '25/12/93']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['25/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/93']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['25/12/02'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/12/02']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms: ['27/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['27/12/93']; + const options = { dataKey: '', fieldType: FieldType.dateEuroShort, cellValue: '25/12/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '25/12/93', fieldType: FieldType.dateEuroShort, searchTerms: ['01/12/93..31/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/93..31/12/93']; + const options = { dataKey: '', operator: 'EQ', cellValue: '25/12/93', fieldType: FieldType.dateEuroShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '25/11/93', fieldType: FieldType.dateEuroShort, searchTerms: ['01/12/93..31/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/93..31/12/93']; + const options = { dataKey: '', operator: 'EQ', cellValue: '25/11/93', fieldType: FieldType.dateEuroShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '01/12/93', fieldType: FieldType.dateEuroShort, searchTerms: ['01/12/93..31/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/93..31/12/93']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '01/12/93', fieldType: FieldType.dateEuroShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '01/12/93', fieldType: FieldType.dateEuroShort, searchTerms: ['01/12/93..31/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/93..31/12/93']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '01/12/93', fieldType: FieldType.dateEuroShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '05/12/93', fieldType: FieldType.dateEuroShort, searchTerms: ['01/12/93..60/12/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['01/12/93..60/12/93']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '05/12/93', fieldType: FieldType.dateEuroShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateEuroShort)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateFilterCondition.spec.ts index a035ef364..3a201395f 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateFilterCondition.spec.ts @@ -1,94 +1,110 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '2000-12-25' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '2000-12-25', searchTerms: ['2000-14-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2000-14-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '2000-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['2003-03-14'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2003-03-14']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['2003-03-14', '1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2003-03-14', '1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['2002-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2002-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms: ['1993-12-27'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-27']; + const options = { dataKey: '', fieldType: FieldType.date, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.date, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.date, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25', fieldType: FieldType.date, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25', fieldType: FieldType.date, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01', fieldType: FieldType.date, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01', fieldType: FieldType.date, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-01', fieldType: FieldType.date, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-01', fieldType: FieldType.date, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05', fieldType: FieldType.date, searchTerms: ['1993-12-01..1993-12-60'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01..1993-12-60']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05', fieldType: FieldType.date, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.date)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts index f8d60aa9a..df2eebdae 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateIsoFilterCondition.spec.ts @@ -1,113 +1,269 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { executeDateFilterCondition, getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest, getParsedSearchTermsByFieldType } from '../filterConditionProcesses'; describe('dateIsoFilterCondition method', () => { - it('should return False when no cell value is provided, neither search terms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + describe('when using executeFilterConditionTest method', () => { + describe('single date filtering', () => { + it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '' } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False when any cell value is provided without any search terms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25' } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25' } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25', searchTerms: ['2000-14-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when search term is not a valid date', () => { + const searchTerms = ['2000-14-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True when input value provided is equal to the searchTerms', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True when input value provided is equal to the searchTerms', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in cell value, and is called by "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: new Date('1993-12-25T14:02:02.103Z'), searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in cell value', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: new Date('1993-12-25T14:02:02.103Z'), searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in search value, and is called by "executeMappedCondition"', () => { - // @ts-ignore - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: [new Date('1993-12-25T14:02:02.103Z')] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in search value', () => { + const searchTerms = [new Date('1993-12-25T14:02:02.103Z')]; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['2003-03-14'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when cell value is not the same value as the searchTerm', () => { + const searchTerms = ['2003-03-14']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['2003-03-14', '1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { + const searchTerms = ['2003-03-14', '1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['1993-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False even when Operator is Not Equal and cell value equals the search term', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['2002-12-25'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { + const searchTerms = ['2002-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when there are no search term and no operator', () => { + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms: ['1993-12-27'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { + const searchTerms = ['1993-12-27']; + const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + }); - it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.dateIso, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + describe('date range', () => { + it('should return True when input value is in the range of search terms', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); - it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25', fieldType: FieldType.dateIso, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); - }); + it('should return False when input value is not in the range of search terms', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(true); - }); + it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); + + it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); - it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms: ['1993-12-01..1993-12-31'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); + it('should return False when any of the 2 search term value is not a valid date', () => { + const searchTerms = ['1993-12-01..1993-12-60']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + }); }); - it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05', fieldType: FieldType.dateIso, searchTerms: ['1993-12-01..1993-12-60'] } as FilterConditionOption; - const output = executeMappedCondition(options); - expect(output).toBe(false); + describe('when using executeDateFilterCondition method', () => { + describe('single date filtering', () => { + it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = [undefined]; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '' } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = [undefined]; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25' } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return False when search term is not a valid date', () => { + const searchTerms = ['2000-14-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '2000-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return True when input value provided is equal to the searchTerms', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(true); + }); + + it('should return True when input value provided is equal to the searchTerms', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(true); + }); + + it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in cell value', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: new Date('1993-12-25T14:02:02.103Z'), searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(true); + }); + + it('should return True when input value provided is equal to the searchTerms, even when passing Date+Time in search value', () => { + const searchTerms = [new Date('1993-12-25T14:02:02.103Z')]; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(true); + }); + + it('should return False when cell value is not the same value as the searchTerm', () => { + const searchTerms = ['2003-03-14']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { + const searchTerms = ['2003-03-14', '1993-12-25']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return False even when Operator is Not Equal and cell value equals the search term', () => { + const searchTerms = ['1993-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { + const searchTerms = ['2002-12-25']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(true); + }); + + it('should return False when there are no search term and no operator', () => { + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + + it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { + const searchTerms = ['1993-12-27']; + const options = { dataKey: '', fieldType: FieldType.dateIso, cellValue: '1993-12-25', searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getParsedSearchTermsByFieldType(searchTerms, FieldType.dateIso) as any[]); + expect(output).toBe(false); + }); + }); + + describe('date range', () => { + it('should return True when input value is in the range of search terms', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); + + it('should return False when input value is not in the range of search terms', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + + it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(true); + }); + + it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { + const searchTerms = ['1993-12-01..1993-12-31']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-01', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + + it('should return False when any of the 2 search term value is not a valid date', () => { + const searchTerms = ['1993-12-01..1993-12-60']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05', fieldType: FieldType.dateIso, searchTerms } as FilterConditionOption; + const output = executeDateFilterCondition(options, getFilterParsedDates(searchTerms, FieldType.dateIso)); + expect(output).toBe(false); + }); + }); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsFilterCondition.spec.ts index 7a82c006b..0a78e3f02 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsFilterCondition.spec.ts @@ -1,100 +1,117 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateUsFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/2000' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/2000', searchTerms: ['25/14/2000'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/14/2000']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/2000', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['12/25/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/1993']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['12/25/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest"', () => { + const searchTerms = ['12/25/1993']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['03/03/2003'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['03/03/2003']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['03/14/2003', '12/25/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['03/14/2003', '12/25/1993']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['12/25/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/1993']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['12/25/2002'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/2002']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms: ['12/27/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/27/1993']; + const options = { dataKey: '', fieldType: FieldType.dateUs, cellValue: '12/25/1993', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '12/25/1993', fieldType: FieldType.dateUs, searchTerms: ['12/01/1993..12/31/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/1993..12/31/1993']; + const options = { dataKey: '', operator: 'EQ', cellValue: '12/25/1993', fieldType: FieldType.dateUs, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '11/25/1993', fieldType: FieldType.dateUs, searchTerms: ['12/01/1993..12/31/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/1993..12/31/1993']; + const options = { dataKey: '', operator: 'EQ', cellValue: '11/25/1993', fieldType: FieldType.dateUs, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '12/01/1993', fieldType: FieldType.dateUs, searchTerms: ['12/01/1993..12/31/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/1993..12/31/1993']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '12/01/1993', fieldType: FieldType.dateUs, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/01/1993', fieldType: FieldType.dateUs, searchTerms: ['12/01/1993..12/31/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/1993..12/31/1993']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/01/1993', fieldType: FieldType.dateUs, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/05/1993', fieldType: FieldType.dateUs, searchTerms: ['12/01/1993..12/60/1993'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/1993..12/60/1993']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/05/1993', fieldType: FieldType.dateUs, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUs)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsShortFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsShortFilterCondition.spec.ts index dc6367ca3..6a99cd3c3 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsShortFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUsShortFilterCondition.spec.ts @@ -1,100 +1,117 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateUsShortFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/00' } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/00', searchTerms: ['25/14/00'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['25/14/00']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/00', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['12/25/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/93']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['12/25/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest"', () => { + const searchTerms = ['12/25/93']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['03/03/2003'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['03/03/2003']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['03/14/03', '12/25/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['03/14/03', '12/25/93']; + const options = { dataKey: '', operator: 'EQ', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['12/25/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/93']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['12/25/02'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/25/02']; + const options = { dataKey: '', operator: 'NE', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms: ['12/27/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/27/93']; + const options = { dataKey: '', fieldType: FieldType.dateUsShort, cellValue: '12/25/93', searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '12/25/93', fieldType: FieldType.dateUsShort, searchTerms: ['12/01/93..12/31/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/93..12/31/93']; + const options = { dataKey: '', operator: 'EQ', cellValue: '12/25/93', fieldType: FieldType.dateUsShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '11/25/93', fieldType: FieldType.dateUsShort, searchTerms: ['12/01/93..12/31/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/93..12/31/93']; + const options = { dataKey: '', operator: 'EQ', cellValue: '11/25/93', fieldType: FieldType.dateUsShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '12/01/93', fieldType: FieldType.dateUsShort, searchTerms: ['12/01/93..12/31/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/93..12/31/93']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '12/01/93', fieldType: FieldType.dateUsShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/01/93', fieldType: FieldType.dateUsShort, searchTerms: ['12/01/93..12/31/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/93..12/31/93']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/01/93', fieldType: FieldType.dateUsShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/05/93', fieldType: FieldType.dateUsShort, searchTerms: ['12/01/93..12/60/93'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['12/01/93..12/60/93']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '12/05/93', fieldType: FieldType.dateUsShort, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUsShort)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUtcFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUtcFilterCondition.spec.ts index 79c99428f..8045a00a5 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUtcFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/dateUtcFilterCondition.spec.ts @@ -1,94 +1,110 @@ -import { FilterConditionOption, FieldType } from '../../models/index'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { getFilterParsedDates } from '../dateFilterCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; describe('dateUtcFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.dateUtc } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: '2000-12-25T23:01:52.103Z', fieldType: FieldType.dateUtc } as FilterConditionOption; - const output = executeMappedCondition(options); + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False when search term is not a valid date', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '2000-12-25T23:01:52.103Z', fieldType: FieldType.dateUtc, searchTerms: ['2000-14-25T18:50:02.25Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2000-14-25T18:50:02.25Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '2000-12-25T23:01:52.103Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.124Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-25T10:50:50.124Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-25T10:50:50.124Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.124Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(true); }); it('should return False when cell value is not the same value as the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['2003-03-14T13:03:03.003Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2003-03-14T13:03:03.003Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False even when the cell value is found in the searchTerms since it only compares first term', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['2003-03-14T13:03:03.003Z', '1993-12-25T10:50:50.108Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['2003-03-14T13:03:03.003Z', '1993-12-25T10:50:50.108Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal and cell value equals the search term', () => { - const options = { dataKey: '', operator: 'NE', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-25T10:50:50.108Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-25T10:50:50.108Z']; + const options = { dataKey: '', operator: 'NE', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal and cell value is different than the search term', () => { - const options = { dataKey: '', operator: 'NE', cellValue: '1993-12-25T10:50:50.115Z', fieldType: FieldType.dateUtc, searchTerms: ['1995-12-25T10:50:50.115Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1995-12-25T10:50:50.115Z']; + const options = { dataKey: '', operator: 'NE', cellValue: '1993-12-25T10:50:50.115Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(true); }); it('should return False when there are no search term and no operator', () => { - const options = { dataKey: '', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: [null] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = [null as any]; + const options = { dataKey: '', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False when search and cell values are different and there are no operator passed, it will use default EQ operator', () => { - const options = { dataKey: '', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-27T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-27T12:27:27.127Z']; + const options = { dataKey: '', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-12-25T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25T11:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z']; + const options = { dataKey: '', operator: 'EQ', cellValue: '1993-11-25T11:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01T10:22:33.128Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1993-12-01T10:22:33.128Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-07:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01T10:22:33.128Z..1993-12-31T12:27:27.127Z']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-07:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); it('should return False when any of the 2 search term value is not a valid date', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms: ['1993-12-01T10:22:33.128Z..1993-12-60T12:27:27.127Z'] } as FilterConditionOption; - const output = executeMappedCondition(options); + const searchTerms = ['1993-12-01T10:22:33.128Z..1993-12-60T12:27:27.127Z']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1993-12-05T10:50:50.108Z', fieldType: FieldType.dateUtc, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedDates(searchTerms, FieldType.dateUtc)); expect(output).toBe(false); }); }); 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 new file mode 100644 index 000000000..e64b3466b --- /dev/null +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterConditionProcesses.spec.ts @@ -0,0 +1,50 @@ +import * as moment from 'moment-mini'; +import 'jest-extended'; + +import { getParsedSearchTermsByFieldType } from '../filterConditionProcesses'; +import { FieldType } from '../../models/fieldType.enum'; + +describe('getParsedSearchTermsByFieldType method', () => { + it('should get parsed result as the first array item boolean when a boolean field type is provided', () => { + const input1 = ['1']; + const input2 = [true]; + const result1 = getParsedSearchTermsByFieldType(input1, FieldType.boolean); + const result2 = getParsedSearchTermsByFieldType(input2, FieldType.boolean); + + expect(result1).toEqual(true); + expect(result2).toEqual(true); + }); + + it('should get a moment date object when parsing any date type', () => { + const inputDate = '2001-03-03T10:11:22.456Z'; + const result = getParsedSearchTermsByFieldType([inputDate], FieldType.dateUtc); + + expect(result[0]).toBeObject(); + expect(moment.isMoment(result[0])).toBeTrue(); + expect(result[0].format('YYYY-MM-DD')).toBe('2001-03-03'); + }); + + it('should get parsed result as a number array when providing an array of searchTerms that are string of numbers', () => { + const input1 = ['0']; + const input2 = ['0', 12]; + const result1 = getParsedSearchTermsByFieldType(input1, FieldType.number); + const result2 = getParsedSearchTermsByFieldType(input2, FieldType.number); + + expect(result1).toEqual([0]); + expect(result2).toEqual([0, 12]); + }); + + it('should get parsed result as the first array item when an object field type is provided', () => { + const input = 'world'; + const result = getParsedSearchTermsByFieldType([input], FieldType.object); + + expect(result).toBe(input); + }); + + it('should get parsed result as the first array item text when a string field type is provided', () => { + const input = 'world'; + const result = getParsedSearchTermsByFieldType([input], FieldType.string); + + expect(result).toBe(input); + }); +}); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterUtilities.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterUtilities.spec.ts index aab703e01..8043442ce 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterUtilities.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/filterUtilities.spec.ts @@ -35,8 +35,7 @@ describe('filterUtilities', () => { describe('testFilterCondition method', () => { it('should return true when operator is not in any of the case', () => { - // @ts-ignore - const output = testFilterCondition('<==', 30, 10); + const output = testFilterCondition('<==' as any, 30, 10); expect(output).toBeTruthy(); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts index 94a1acb00..eb0032f7e 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/numberFilterCondition.spec.ts @@ -1,131 +1,166 @@ import { FieldType, FilterConditionOption } from '../../models/index'; -import { numberFilterCondition } from '../numberFilterCondition'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; +import { executeNumberFilterCondition, getFilterParsedNumbers } from '../numberFilterCondition'; -describe('numberFilterCondition method', () => { +describe('executeNumberFilterCondition method', () => { it('should return False when no cell value is provided, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.number } as FilterConditionOption; - const output = numberFilterCondition(options); + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return True when input cell value equals the default search term', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 0, fieldType: FieldType.string } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = undefined; + const options = { dataKey: '', operator: 'EQ', cellValue: 0, fieldType: FieldType.number } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); + expect(output).toBe(true); + }); + + it('should return True when first searchTerm is undefined provided neither an operator when executing "executeFilterConditionTest" method', () => { + const searchTerms = [undefined]; + const options = { dataKey: '', cellValue: 0, fieldType: FieldType.number } as FilterConditionOption; + const output = executeFilterConditionTest(options, searchTerms); expect(output).toBe(true); }); it('should return False when any cell value is provided without any search terms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.number } as FilterConditionOption; - const output = numberFilterCondition(options); + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return True when input value True is provided as cell value', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms: [3] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [3]; + const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms: [3] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [3]; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition" with fieldType.number', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms: [3] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest" with fieldType.number', () => { + const searchTerms = [3]; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition" with fieldType.float', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.float, searchTerms: [3] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest" with fieldType.float', () => { + const searchTerms = [3]; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.float, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition" with fieldType.integer', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.integer, searchTerms: [3] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest" with fieldType.integer', () => { + const searchTerms = [3]; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.integer, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return False when the cell value is not equal to the first search term', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms: ['1', '2', '3'] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = ['1', '2', '3']; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return False when cell value is inversed to the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 2, fieldType: FieldType.number, searchTerms: [1] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1]; + const options = { dataKey: '', operator: 'EQ', cellValue: 2, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: 'NE', cellValue: 2, fieldType: FieldType.number, searchTerms: [2] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [2]; + const options = { dataKey: '', operator: 'NE', cellValue: 2, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); + expect(output).toBe(false); + }); + + it('should return False when there are no operator and the searchTerm is not equal to cell value', () => { + const searchTerms = [0]; + const options = { dataKey: '', cellValue: 2, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); - it('should return True when there are no search term and no operator', () => { - const options = { dataKey: '', cellValue: 2, fieldType: FieldType.number, searchTerms: [0] } as FilterConditionOption; - const output = numberFilterCondition(options); + it('should return True when there are no search term and no operator and the cell value is 0 which equals the default search term', () => { + const searchTerms = undefined; + const options = { dataKey: '', cellValue: 0, fieldType: FieldType.number } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return False when there are a valid number search term but without operator', () => { - const options = { dataKey: '', cellValue: 2, fieldType: FieldType.number, searchTerms: [1] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1]; + const options = { dataKey: '', cellValue: 2, fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms using 2 dots (..) notation', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms: ['1..5'] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = ['1..5']; + const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms using 2 dots (..) notation', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '15', fieldType: FieldType.number, searchTerms: ['1..5'] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = ['1..5']; + const options = { dataKey: '', operator: 'EQ', cellValue: '15', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(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 options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1', fieldType: FieldType.number, searchTerms: ['1..5'] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = ['1..5']; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(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 options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1', fieldType: FieldType.number, searchTerms: ['1..5'] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = ['1..5']; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return True when input value is in the range of search terms array', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms: [1, 5] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1, 5]; + const options = { dataKey: '', operator: 'EQ', cellValue: '3', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return False when input value is not in the range of search terms array', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: '15', fieldType: FieldType.number, searchTerms: [1, 5] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1, 5]; + const options = { dataKey: '', operator: 'EQ', cellValue: '15', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); it('should return True when input value equals the search terms min (first array term) inclusive value and operator is set to "rangeInclusive"', () => { - const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1', fieldType: FieldType.number, searchTerms: [1, 5] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1, 5]; + const options = { dataKey: '', operator: 'RangeInclusive', cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(true); }); it('should return False when input value equals the search terms min (first array term) inclusive value and operator is set to "RangeExclusive"', () => { - const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1', fieldType: FieldType.number, searchTerms: [1, 5] } as FilterConditionOption; - const output = numberFilterCondition(options); + const searchTerms = [1, 5]; + const options = { dataKey: '', operator: 'RangeExclusive', cellValue: '1', fieldType: FieldType.number, searchTerms } as FilterConditionOption; + const output = executeNumberFilterCondition(options, getFilterParsedNumbers(searchTerms)); expect(output).toBe(false); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/objectFilterCondition.spec.ts b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/objectFilterCondition.spec.ts index d1ebf59fe..64c774141 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/__tests__/objectFilterCondition.spec.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/__tests__/objectFilterCondition.spec.ts @@ -1,8 +1,8 @@ -import { FieldType, FilterConditionOption, OperatorType } from '../../models/index'; -import { objectFilterCondition } from '../objectFilterCondition'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { FieldType, FilterConditionOption } from '../../models/index'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; +import { executeObjectFilterCondition, getFilterParsedObjectResult } from '../objectFilterCondition'; -describe('objectFilterCondition method', () => { +describe('executeObjectFilterCondition method', () => { let mockRow; beforeEach(() => { @@ -10,92 +10,114 @@ describe('objectFilterCondition method', () => { }); it('should return True when there are no input value neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', cellValue: '', fieldType: FieldType.object } as FilterConditionOption; - const output = objectFilterCondition(options); + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when no cell input value is provided which is equal to the default search term, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.object } as FilterConditionOption; - const output = objectFilterCondition(options); + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when cell input value is null and is equal to the default search term, neither search terms', () => { + const searchTerms = undefined; const options = { dataKey: '', operator: 'EQ', cellValue: null, fieldType: FieldType.object } as FilterConditionOption; - const output = objectFilterCondition(options); + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); + expect(output).toBe(true); + }); + + it('should return True when first searchTerm is undefined provided neither an operator when executing "executeFilterConditionTest" method', () => { + const searchTerms = undefined; + const options = { dataKey: '', cellValue: mockRow, fieldType: FieldType.object } as FilterConditionOption; + const output = executeFilterConditionTest(options, searchTerms); expect(output).toBe(true); }); it('should return False when any cell input value is provided without any search terms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object } as FilterConditionOption; - const output = objectFilterCondition(options); + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); it('should return True when input value provided is equal, using "=" to the searchTerms', () => { - const options = { dataKey: '', operator: '=', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: '=', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal, using "==" to the searchTerms', () => { - const options = { dataKey: '', operator: '==', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: '==', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal, using "EQ" to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when using the "dataKey" to compare the object with the searchTerms', () => { - const options = { dataKey: 'code', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: 'code', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); - it('should return True when using the "dataKey" to compare the object with the searchTerms and is called by "executeMappedCondition"', () => { - const options = { dataKey: 'code', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when using the "dataKey" to compare the object with the searchTerms and is called by "executeFilterConditionTest"', () => { + const searchTerms = [mockRow]; + const options = { dataKey: 'code', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms even though there are no Operator provided (it will use EQ as default)', () => { - const options = { dataKey: '', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(true); }); it('should return False when the cell value is equal to at least 1 of the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms: ['bar', mockRow, 'John'] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = ['bar', mockRow, 'John']; + const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); it('should return False when cell value is inversed to the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms: ['bar'] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = ['bar']; + const options = { dataKey: '', operator: 'EQ', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); it('should return True even when Operator is "!=" because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: '!=', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: '!=', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); it('should return True even when Operator is "<>" because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: '<>', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: '<>', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); it('should return True even when Operator is Not Equal because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: 'NE', cellValue: mockRow, fieldType: FieldType.object, searchTerms: [mockRow] } as FilterConditionOption; - const output = objectFilterCondition(options); + const searchTerms = [mockRow]; + const options = { dataKey: '', operator: 'NE', cellValue: mockRow, fieldType: FieldType.object, searchTerms } as FilterConditionOption; + const output = executeObjectFilterCondition(options, getFilterParsedObjectResult(searchTerms)); expect(output).toBe(false); }); }); 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 735d1ac8a..3e672fe34 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 @@ -1,95 +1,117 @@ import { FieldType, FilterConditionOption, OperatorType } from '../../models/index'; -import { stringFilterCondition } from '../stringFilterCondition'; -import { executeMappedCondition } from '../executeMappedCondition'; +import { executeFilterConditionTest } from '../filterConditionProcesses'; +import { executeStringFilterCondition, getFilterParsedText } from '../stringFilterCondition'; -describe('stringFilterCondition method', () => { +describe('executeStringFilterCondition method', () => { it('should return True when no cell input value is provided which is equal to the default search term, neither search terms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.string } as FilterConditionOption; - const output = stringFilterCondition(options); + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when cell input value is null and is equal to the default search term, neither search terms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: null, fieldType: FieldType.string } as FilterConditionOption; - const output = stringFilterCondition(options); + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); + expect(output).toBe(true); + }); + + it('should return True when first searchTerm is undefined provided neither an operator when executing "executeFilterConditionTest" method', () => { + const searchTerms = undefined; + const options = { dataKey: '', cellValue: 'foo', fieldType: FieldType.string } as FilterConditionOption; + const output = executeFilterConditionTest(options, searchTerms); expect(output).toBe(true); }); it('should return False when any cell input value is provided without any search terms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string } as FilterConditionOption; - const output = stringFilterCondition(options); + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(false); }); it('should return True when input value True is provided as cell value', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.string, searchTerms: ['3'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['3']; + const options = { dataKey: '', operator: 'EQ', cellValue: 3, fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['foo']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); - it('should return True when input value provided is equal to the searchTerms and is called by "executeMappedCondition"', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = executeMappedCondition(options); + it('should return True when input value provided is equal to the searchTerms and is called by "executeFilterConditionTest"', () => { + const searchTerms = ['foo']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeFilterConditionTest(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided is equal to the searchTerms even though there are no Operator provided (it will use EQ as default)', () => { - const options = { dataKey: '', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['foo']; + const options = { dataKey: '', cellValue: 'foo', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return False when the cell value is equal to at least 1 of the searchTerms', () => { + const searchTerms = []; const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar', 'foo', 'John'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(false); }); it('should return False when cell value is inversed to the searchTerm', () => { - const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['bar']; + const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(false); }); it('should return False even when Operator is Not Equal because condition is always a strict equal check', () => { - const options = { dataKey: '', operator: 'NE', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['foo']; + const options = { dataKey: '', operator: 'NE', cellValue: 'foo', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(false); }); it('should return True when input value provided starts with same substring and the operator is startsWith', () => { - const options = { dataKey: '', operator: OperatorType.startsWith, cellValue: 'abbostford', fieldType: FieldType.string, searchTerms: ['abb'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['abb']; + const options = { dataKey: '', operator: OperatorType.startsWith, cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when search term is a substring of the cell value and the operator is Contains', () => { - const options = { dataKey: '', operator: 'Contains', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms: ['bost'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['bost']; + const options = { dataKey: '', operator: 'Contains', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided starts with same substring and the operator is empty string', () => { - const options = { dataKey: '', operator: '', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms: ['abb'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['abb']; + const options = { dataKey: '', operator: '', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); - it('should return True when input value provided starts with same substring and the operator is empty string & option "cellValueLastChar" is asterisk (*)', () => { - const options = { dataKey: '', operator: '', cellValueLastChar: '*', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms: ['abb'] } as FilterConditionOption; - const output = stringFilterCondition(options); + it('should return True when input value provided starts with same substring and the operator is empty string & option "searchInputLastChar" is asterisk (*)', () => { + const searchTerms = ['abb']; + const options = { dataKey: '', operator: '', searchInputLastChar: '*', cellValue: 'abbostford', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); it('should return True when input value provided ends with same substring and the operator is endsWith', () => { - const options = { dataKey: '', operator: OperatorType.endsWith, cellValue: 'John Smith', fieldType: FieldType.string, searchTerms: ['Smith'] } as FilterConditionOption; - const output = stringFilterCondition(options); + const searchTerms = ['Smith']; + const options = { dataKey: '', operator: OperatorType.endsWith, cellValue: 'John Smith', fieldType: FieldType.string, searchTerms } as FilterConditionOption; + const output = executeStringFilterCondition(options, getFilterParsedText(searchTerms)); expect(output).toBe(true); }); }); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/booleanFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/booleanFilterCondition.ts index 9bb938a0b..e7f7887ae 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/booleanFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/booleanFilterCondition.ts @@ -1,7 +1,16 @@ -import { FilterCondition, FilterConditionOption } from './../models/index'; +import { FilterCondition, FilterConditionOption, SearchTerm } from './../models/index'; import { parseBoolean } from '../services/utilities'; -export const booleanFilterCondition: FilterCondition = (options: FilterConditionOption) => { - const searchTerm = Array.isArray(options.searchTerms) && options.searchTerms[0] || ''; - return parseBoolean(options.cellValue) === parseBoolean(searchTerm); +/** Execute filter condition check on each cell */ +export const executeBooleanFilterCondition: FilterCondition = (options: FilterConditionOption, parsedSearchValue: boolean | undefined) => { + return parseBoolean(options.cellValue) === parseBoolean(parsedSearchValue); }; + +/** + * 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 getFilterParsedBoolean(inputSearchTerms: SearchTerm[] | undefined): boolean { + const searchTerm = Array.isArray(inputSearchTerms) && inputSearchTerms[0] || false; + return parseBoolean(searchTerm); +} diff --git a/src/app/modules/angular-slickgrid/filter-conditions/collectionSearchFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/collectionSearchFilterCondition.ts index f8c50d358..5efb77b54 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/collectionSearchFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/collectionSearchFilterCondition.ts @@ -1,9 +1,13 @@ import { FilterCondition, FilterConditionOption } from '../models/index'; import { testFilterCondition } from './filterUtilities'; -export const collectionSearchFilterCondition: FilterCondition = (options: FilterConditionOption) => { +/** + * Execute filter condition check on each cell. + * This is used only by the Select Single/Multiple Filter which uses the "multiple-select.js" 3rd party lib which always provide values as strings + */ +export const executeCollectionSearchFilterCondition: FilterCondition = (options: FilterConditionOption) => { // multiple-select will always return text, so we should make our cell values text as well - const cellValue = options.cellValue + ''; + const cellValue = (options.cellValue === undefined || options.cellValue === null) ? '' : `${options.cellValue}`; return testFilterCondition(options.operator || 'IN', cellValue, options.searchTerms || []); }; diff --git a/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts new file mode 100644 index 000000000..adc5d4e01 --- /dev/null +++ b/src/app/modules/angular-slickgrid/filter-conditions/dateFilterCondition.ts @@ -0,0 +1,73 @@ +import * as moment_ from 'moment-mini'; +const moment = moment_['default'] || moment_; // patch to fix rollup "moment has no default export" issue, document here https://github.com/rollup/rollup/issues/670 + +import { FieldType, FilterConditionOption, OperatorType, SearchTerm } from '../models/index'; +import { mapMomentDateFormatWithFieldType } from '../services/utilities'; +import { testFilterCondition } from './filterUtilities'; + +/** + * Execute Date filter condition check on each cell and use correct date format depending on it's field type (or filterSearchType when that is provided) + */ +export function executeDateFilterCondition(options: FilterConditionOption, parsedSearchDates: any[]): boolean { + const filterSearchType = options && (options.filterSearchType || options.fieldType) || FieldType.dateIso; + const FORMAT = mapMomentDateFormatWithFieldType(filterSearchType); + const [searchDate1, searchDate2] = parsedSearchDates; + + // cell value in moment format + const dateCell = moment(options.cellValue, FORMAT, true); + + // return when cell value is not a valid date + if ((!searchDate1 && !searchDate2) || !dateCell.isValid()) { + return false; + } + + // when comparing with Dates only (without time), we need to disregard the time portion, we can do so by setting our time to start at midnight + // ref, see https://stackoverflow.com/a/19699447/1212166 + const dateCellTimestamp = FORMAT.toLowerCase().includes('h') ? dateCell.valueOf() : dateCell.clone().startOf('day').valueOf(); + + // having 2 search dates, we assume that it's a date range filtering and we'll compare against both dates + if (searchDate1 && searchDate2) { + const isInclusive = options.operator && options.operator === OperatorType.rangeInclusive; + const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), dateCellTimestamp, searchDate1.valueOf()); + const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), dateCellTimestamp, searchDate2.valueOf()); + return (resultCondition1 && resultCondition2); + } + + // comparing against a single search date + const dateSearchTimestamp1 = FORMAT.toLowerCase().includes('h') ? searchDate1.valueOf() : searchDate1.clone().startOf('day').valueOf(); + return testFilterCondition(options.operator || '==', dateCellTimestamp, dateSearchTimestamp1); +} + +/** + * From our search filter value(s), get the parsed value(s), they are parsed as Moment object(s). + * This is called only once per filter before running the actual filter condition check on each cell + */ +export function getFilterParsedDates(inputSearchTerms: SearchTerm[] | undefined, inputFilterSearchType: typeof FieldType[keyof typeof FieldType]): SearchTerm[] { + const searchTerms = Array.isArray(inputSearchTerms) && inputSearchTerms || []; + const filterSearchType = inputFilterSearchType || FieldType.dateIso; + const FORMAT = mapMomentDateFormatWithFieldType(filterSearchType); + + const parsedSearchValues: any[] = []; + + 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('..'); + const searchValue1 = (Array.isArray(searchValues) && searchValues[0] || '') as Date | string; + const searchValue2 = (Array.isArray(searchValues) && searchValues[1] || '') as Date | string; + const searchDate1 = moment(searchValue1, FORMAT, true); + const searchDate2 = moment(searchValue2, FORMAT, true); + + // return if any of the 2 values are invalid dates + if (!searchDate1.isValid() || !searchDate2.isValid()) { + return []; + } + parsedSearchValues.push(searchDate1, searchDate2); + } else { + // return if the search term is an invalid date + const searchDate1 = moment(searchTerms[0] as Date | string, FORMAT, true); + if (!searchDate1.isValid()) { + return []; + } + parsedSearchValues.push(searchDate1); + } + return parsedSearchValues; +} diff --git a/src/app/modules/angular-slickgrid/filter-conditions/executeMappedCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/executeMappedCondition.ts deleted file mode 100644 index 316d078c7..000000000 --- a/src/app/modules/angular-slickgrid/filter-conditions/executeMappedCondition.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { booleanFilterCondition } from './booleanFilterCondition'; -import { collectionSearchFilterCondition } from './collectionSearchFilterCondition'; -import { numberFilterCondition } from './numberFilterCondition'; -import { objectFilterCondition } from './objectFilterCondition'; -import { stringFilterCondition } from './stringFilterCondition'; - -import { FieldType, FilterCondition, FilterConditionOption, OperatorType } from '../models/index'; -import { mapMomentDateFormatWithFieldType } from './../services/utilities'; -import { isCollectionOperator, testFilterCondition } from './filterUtilities'; -import * as moment_ from 'moment-mini'; - -const moment = moment_; // patch to fix rollup "moment has no default export" issue, document here https://github.com/rollup/rollup/issues/670 - -export const executeMappedCondition: FilterCondition = (options: FilterConditionOption) => { - // when using a multi-select ('IN' operator) we will not use the field type but instead go directly with a collection search - if (isCollectionOperator(options.operator)) { - return collectionSearchFilterCondition(options); - } - - // execute the mapped type, or default to String condition check - switch (options.fieldType) { - case FieldType.boolean: - return booleanFilterCondition(options); - case FieldType.date: - case FieldType.dateIso: - case FieldType.dateUtc: - case FieldType.dateTime: - case FieldType.dateTimeIso: - case FieldType.dateTimeIsoAmPm: - case FieldType.dateTimeIsoAM_PM: - case FieldType.dateTimeShortIso: - case FieldType.dateEuro: - case FieldType.dateEuroShort: - case FieldType.dateTimeShortEuro: - case FieldType.dateTimeEuro: - case FieldType.dateTimeEuroAmPm: - case FieldType.dateTimeEuroAM_PM: - case FieldType.dateTimeEuroShort: - case FieldType.dateTimeEuroShortAmPm: - case FieldType.dateTimeEuroShortAM_PM: - case FieldType.dateUs: - case FieldType.dateUsShort: - case FieldType.dateTimeShortUs: - case FieldType.dateTimeUs: - case FieldType.dateTimeUsAmPm: - case FieldType.dateTimeUsAM_PM: - case FieldType.dateTimeUsShort: - case FieldType.dateTimeUsShortAmPm: - case FieldType.dateTimeUsShortAM_PM: - return executeAssociatedDateCondition(options); - case FieldType.integer: - case FieldType.float: - case FieldType.number: - return numberFilterCondition(options); - case FieldType.object: - return objectFilterCondition(options); - case FieldType.string: - case FieldType.text: - case FieldType.password: - case FieldType.readonly: - default: - return stringFilterCondition(options); - } -}; - -/** - * Execute Date filter condition and use correct date format depending on it's field type (or filterSearchType when that is provided) - * @param options - */ -function executeAssociatedDateCondition(options: FilterConditionOption): boolean { - const filterSearchType = options && (options.filterSearchType || options.fieldType) || FieldType.dateIso; - const FORMAT = mapMomentDateFormatWithFieldType(filterSearchType); - const searchTerms = Array.isArray(options.searchTerms) && options.searchTerms || []; - - let isRangeSearch = false; - let dateSearch1: any; - let dateSearch2: any; - - // return when cell value is not a valid date - if (searchTerms.length === 0 || searchTerms[0] === '' || searchTerms[0] === null || !moment(options.cellValue, FORMAT, true).isValid()) { - return false; - } - - // cell value in moment format - const dateCell = moment(options.cellValue, FORMAT, true); - - if (searchTerms.length === 2 || (typeof searchTerms[0] === 'string' && (searchTerms[0] as string).indexOf('..') > 0)) { - isRangeSearch = true; - const searchValues = (searchTerms.length === 2) ? searchTerms : (searchTerms[0] as string).split('..'); - const searchValue1 = (Array.isArray(searchValues) && searchValues[0] || '') as Date | string; - const searchValue2 = (Array.isArray(searchValues) && searchValues[1] || '') as Date | string; - const searchTerm1 = moment(searchValue1, FORMAT, true); - const searchTerm2 = moment(searchValue2, FORMAT, true); - - // return if any of the 2 values are invalid dates - if (!moment(searchTerm1, FORMAT, true).isValid() || !moment(searchTerm2, FORMAT, true).isValid()) { - return false; - } - dateSearch1 = moment(searchTerm1, FORMAT, true); - dateSearch2 = moment(searchTerm2, FORMAT, true); - } else { - // return if the search term is an invalid date - if (!moment(searchTerms[0] as Date | string, FORMAT, true).isValid()) { - return false; - } - dateSearch1 = moment(searchTerms[0] as Date | string, FORMAT, true); - } - - // when comparing with Dates only (without time), we need to disregard the time portion, we can do so by setting our time to start at midnight - // ref, see https://stackoverflow.com/a/19699447/1212166 - const dateCellTimestamp = FORMAT.toLowerCase().includes('h') ? parseInt(dateCell.format('X'), 10) : parseInt(dateCell.clone().startOf('day').format('X'), 10); - - // run the filter condition with date in Unix Timestamp format - if (isRangeSearch) { - const isInclusive = options.operator && options.operator === OperatorType.rangeInclusive; - const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), dateCellTimestamp, parseInt(dateSearch1.format('X'), 10)); - const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), dateCellTimestamp, parseInt(dateSearch2.format('X'), 10)); - return (resultCondition1 && resultCondition2); - } - - const dateSearchTimestamp1 = FORMAT.toLowerCase().includes('h') ? parseInt(dateSearch1.format('X'), 10) : parseInt(dateSearch1.clone().startOf('day').format('X'), 10); - return testFilterCondition(options.operator || '==', dateCellTimestamp, dateSearchTimestamp1); -}; diff --git a/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts b/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts new file mode 100644 index 000000000..0c04e3ada --- /dev/null +++ b/src/app/modules/angular-slickgrid/filter-conditions/filterConditionProcesses.ts @@ -0,0 +1,127 @@ +import { FieldType, FilterCondition, FilterConditionOption, SearchTerm } from '../models/index'; +import { executeBooleanFilterCondition, getFilterParsedBoolean } from './booleanFilterCondition'; +import { executeCollectionSearchFilterCondition } from './collectionSearchFilterCondition'; +import { getFilterParsedNumbers, executeNumberFilterCondition } from './numberFilterCondition'; +import { executeDateFilterCondition, getFilterParsedDates } from './dateFilterCondition'; +import { executeObjectFilterCondition, getFilterParsedObjectResult } from './objectFilterCondition'; +import { executeStringFilterCondition, getFilterParsedText } from './stringFilterCondition'; +import { isCollectionOperator } from './filterUtilities'; + +/** + * General variable types, just 5x types instead of the multiple FieldType. + * For example all DateIso, DateUs are all "date", this makes it easier to know which filter condition to call + */ +export type GeneralizedVariableType = 'boolean' | 'date' | 'number' | 'object' | 'text'; + +/** Execute mapped condition (per field type) for each cell in the grid */ +export const executeFilterConditionTest: FilterCondition = (options: FilterConditionOption, parsedSearchTerms: SearchTerm | SearchTerm[]) => { + // when using a multi-select ('IN' operator) we will not use the field type but instead go directly with a collection search + if (isCollectionOperator(options.operator)) { + return executeCollectionSearchFilterCondition(options); + } + + // From a more specific field type (dateIso, dateEuro, text, readonly, ...), get the more generalized type (boolean, date, number, object, text) + const generalizedType = getGeneralizedVarTypeByFieldType(options.filterSearchType || options.fieldType); + + // execute the mapped type, or default to String condition check + switch (generalizedType) { + case 'boolean': + // the parsedSearchTerms should be single value (result came from getFilterParsedBoolean() method) + return executeBooleanFilterCondition(options, parsedSearchTerms as SearchTerm); + case 'date': + return executeDateFilterCondition(options, (parsedSearchTerms || []) as any[]); + case 'number': + return executeNumberFilterCondition(options, (parsedSearchTerms || []) as number[]); + case 'object': + // the parsedSearchTerms should be single value (result came from getFilterParsedObjectResult() method) + return executeObjectFilterCondition(options, parsedSearchTerms as SearchTerm); + case 'text': + default: + // the parsedSearchTerms should be single value (result came from getFilterParsedText() method) + return executeStringFilterCondition(options, parsedSearchTerms as SearchTerm); + } +}; + +/** + * From our search filter value(s), get their parsed value(s), for example a "dateIso" filter will be parsed as Moment object. + * Then later when we execute the filtering checks, we won't need to re-parse all search value(s) again and again. + * So this is called only once, for each search filter that is, prior to running the actual filter condition checks on each cell afterward. + */ +export function getParsedSearchTermsByFieldType(inputSearchTerms: SearchTerm[] | undefined, inputFilterSearchType: typeof FieldType[keyof typeof FieldType]): SearchTerm | SearchTerm[] | undefined { + const generalizedType = getGeneralizedVarTypeByFieldType(inputFilterSearchType); + let parsedSearchValues: SearchTerm | SearchTerm[] | undefined; + + // parse the search value(s), the Date & Numbers could be in a range and so we will return an array for them + // any other type will return a single search value + switch (generalizedType) { + case 'boolean': + parsedSearchValues = getFilterParsedBoolean(inputSearchTerms) as boolean; + break; + case 'date': + parsedSearchValues = getFilterParsedDates(inputSearchTerms, inputFilterSearchType) as SearchTerm[]; + break; + case 'number': + parsedSearchValues = getFilterParsedNumbers(inputSearchTerms) as SearchTerm[]; + break; + case 'object': + parsedSearchValues = getFilterParsedObjectResult(inputSearchTerms); + break; + case 'text': + parsedSearchValues = getFilterParsedText(inputSearchTerms); + break; + } + return parsedSearchValues; +} + + +/** + * From a more specific field type, let's return a simple and more general type (boolean, date, number, object, text) + * @param fieldType - specific field type + * @returns generalType - general field type + */ +function getGeneralizedVarTypeByFieldType(fieldType: typeof FieldType[keyof typeof FieldType]): GeneralizedVariableType { + // return general field type + switch (fieldType) { + case FieldType.boolean: + return 'boolean'; + case FieldType.date: + case FieldType.dateIso: + case FieldType.dateUtc: + case FieldType.dateTime: + case FieldType.dateTimeIso: + case FieldType.dateTimeIsoAmPm: + case FieldType.dateTimeIsoAM_PM: + case FieldType.dateTimeShortIso: + case FieldType.dateEuro: + case FieldType.dateEuroShort: + case FieldType.dateTimeShortEuro: + case FieldType.dateTimeEuro: + case FieldType.dateTimeEuroAmPm: + case FieldType.dateTimeEuroAM_PM: + case FieldType.dateTimeEuroShort: + case FieldType.dateTimeEuroShortAmPm: + case FieldType.dateTimeEuroShortAM_PM: + case FieldType.dateUs: + case FieldType.dateUsShort: + case FieldType.dateTimeShortUs: + case FieldType.dateTimeUs: + case FieldType.dateTimeUsAmPm: + case FieldType.dateTimeUsAM_PM: + case FieldType.dateTimeUsShort: + case FieldType.dateTimeUsShortAmPm: + case FieldType.dateTimeUsShortAM_PM: + return 'date'; + case FieldType.integer: + case FieldType.float: + case FieldType.number: + return 'number'; + case FieldType.object: + return 'object'; + case FieldType.string: + case FieldType.text: + case FieldType.password: + case FieldType.readonly: + default: + return 'text'; + } +} diff --git a/src/app/modules/angular-slickgrid/filter-conditions/filterConditions.index.ts b/src/app/modules/angular-slickgrid/filter-conditions/filterConditions.index.ts new file mode 100644 index 000000000..9aa50e264 --- /dev/null +++ b/src/app/modules/angular-slickgrid/filter-conditions/filterConditions.index.ts @@ -0,0 +1,15 @@ +import { executeBooleanFilterCondition } from './booleanFilterCondition'; +import { executeFilterConditionTest } from './filterConditionProcesses'; +import { executeCollectionSearchFilterCondition } from './collectionSearchFilterCondition'; +import { executeNumberFilterCondition } from './numberFilterCondition'; +import { executeStringFilterCondition } from './stringFilterCondition'; +import { testFilterCondition } from './filterUtilities'; + +export const FilterConditions = { + executeFilterConditionTest, + booleanFilter: executeBooleanFilterCondition, + collectionSearchFilter: executeCollectionSearchFilterCondition, + numberFilter: executeNumberFilterCondition, + stringFilter: executeStringFilterCondition, + testFilter: testFilterCondition +}; diff --git a/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts b/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts index 8ba15f076..302c64022 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/filterUtilities.ts @@ -43,8 +43,9 @@ export function isCollectionOperator(operator: OperatorString): boolean { } } +/** Execute the test on the filter condition given an operator and both values, returns a boolean */ export const testFilterCondition = (operator: OperatorString, value1: any, value2: any): boolean => { - switch (operator) { + switch (operator.toUpperCase()) { case '<': case 'LT': return (value1 < value2); diff --git a/src/app/modules/angular-slickgrid/filter-conditions/index.ts b/src/app/modules/angular-slickgrid/filter-conditions/index.ts index 2aafd263d..a1ae55aad 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/index.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/index.ts @@ -1,16 +1,8 @@ -import { FilterConditionOption } from './../models/filterConditionOption.interface'; -import { booleanFilterCondition } from './booleanFilterCondition'; -import { executeMappedCondition } from './executeMappedCondition'; -import { collectionSearchFilterCondition } from './collectionSearchFilterCondition'; -import { numberFilterCondition } from './numberFilterCondition'; -import { stringFilterCondition } from './stringFilterCondition'; -import { testFilterCondition } from './filterUtilities'; - -export const FilterConditions = { - executeMappedCondition, - booleanFilter: booleanFilterCondition, - collectionSearchFilter: collectionSearchFilterCondition, - numberFilter: numberFilterCondition, - stringFilter: stringFilterCondition, - testFilter: testFilterCondition -}; +export * from './stringFilterCondition'; +export * from './objectFilterCondition'; +export * from './numberFilterCondition'; +export * from './filterUtilities'; +export * from './filterConditions.index'; +export * from './filterConditionProcesses'; +export * from './collectionSearchFilterCondition'; +export * from './booleanFilterCondition'; diff --git a/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts index 55f7ec36d..48ee038cd 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/numberFilterCondition.ts @@ -1,32 +1,48 @@ -import { FilterCondition, FilterConditionOption, OperatorType } from '../models/index'; +import { FilterCondition, FilterConditionOption, OperatorType, SearchTerm } from '../models/index'; +import { isNumber } from '../services/utilities'; import { testFilterCondition } from './filterUtilities'; -export const numberFilterCondition: FilterCondition = (options: FilterConditionOption) => { +/** Execute filter condition check on each cell */ +export const executeNumberFilterCondition: FilterCondition = (options: FilterConditionOption, parsedSearchValues: number[]) => { const cellValue = parseFloat(options.cellValue); - const searchTerms = Array.isArray(options.searchTerms) && options.searchTerms || [0]; + const [searchValue1, searchValue2] = parsedSearchValues; - let isRangeSearch = false; + if (searchValue1 === undefined && !options.operator) { + return true; + } + + if (searchValue1 !== undefined && searchValue2 !== undefined) { + const isInclusive = (options && options.operator) === OperatorType.rangeInclusive; + const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), cellValue, +searchValue1); + const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), cellValue, +searchValue2); + return (resultCondition1 && resultCondition2); + } + return testFilterCondition(options.operator || '==', cellValue, +searchValue1); +}; + +/** + * 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 getFilterParsedNumbers(inputSearchTerms: SearchTerm[] | undefined): number[] { + const defaultSearchTerm = 0; // when nothing is provided, we'll default to 0 + const searchTerms = Array.isArray(inputSearchTerms) && inputSearchTerms || [defaultSearchTerm]; + const parsedSearchValues: number[] = []; let searchValue1; let searchValue2; if (searchTerms.length === 2 || (typeof searchTerms[0] === 'string' && (searchTerms[0] as string).indexOf('..') > 0)) { - isRangeSearch = true; const searchValues = (searchTerms.length === 2) ? searchTerms : (searchTerms[0] as string).split('..'); - searchValue1 = parseFloat(Array.isArray(searchValues) && searchValues[0] + ''); - searchValue2 = parseFloat(Array.isArray(searchValues) && searchValues[1] + ''); + searchValue1 = parseFloat(Array.isArray(searchValues) ? searchValues[0] as string : ''); + searchValue2 = parseFloat(Array.isArray(searchValues) ? searchValues[1] as string : ''); } else { - searchValue1 = parseFloat(searchTerms[0] + ''); - } - - if (!searchValue1 && !options.operator) { - return true; + searchValue1 = parseFloat(searchTerms[0] as string); } - if (isRangeSearch) { - const isInclusive = options.operator && options.operator === OperatorType.rangeInclusive; - const resultCondition1 = testFilterCondition((isInclusive ? '>=' : '>'), cellValue, searchValue1); - const resultCondition2 = testFilterCondition((isInclusive ? '<=' : '<'), cellValue, searchValue2); - return (resultCondition1 && resultCondition2); + if (isNumber(searchValue1, true) && isNumber(searchValue2, true)) { + parsedSearchValues.push(searchValue1 as number, searchValue2 as number); + } else if (isNumber(searchValue1, true)) { + parsedSearchValues.push(searchValue1 as number); } - return testFilterCondition(options.operator || '==', cellValue, searchValue1); -}; + return parsedSearchValues; +} diff --git a/src/app/modules/angular-slickgrid/filter-conditions/objectFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/objectFilterCondition.ts index b27c9add9..8a309192f 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/objectFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/objectFilterCondition.ts @@ -1,22 +1,32 @@ -import { FilterCondition, FilterConditionOption } from '../models/index'; +import { FilterCondition, FilterConditionOption, SearchTerm } from '../models/index'; import { compareObjects } from './filterUtilities'; -export const objectFilterCondition: FilterCondition = (options: FilterConditionOption) => { - const searchTerm = (Array.isArray(options.searchTerms) && options.searchTerms[0] || ''); - - if (!searchTerm && !options.operator) { +/** Execute filter condition check on each cell */ +export const executeObjectFilterCondition: FilterCondition = (options: FilterConditionOption, parsedSearchValue: SearchTerm | undefined) => { + if (parsedSearchValue === undefined && !options.operator) { return true; } - switch (options.operator) { + const operator = (options.operator || '').toUpperCase(); + + switch (operator) { case '!=': case '<>': case 'NE': - return !compareObjects(options.cellValue, searchTerm, options.dataKey); + return !compareObjects(options.cellValue, parsedSearchValue, options.dataKey); case '=': case '==': case 'EQ': default: - return compareObjects(options.cellValue, searchTerm, options.dataKey); + return compareObjects(options.cellValue, parsedSearchValue, options.dataKey); } }; + +/** + * 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 getFilterParsedObjectResult(inputSearchTerms: SearchTerm[] | undefined): SearchTerm { + const parsedSearchValue = (Array.isArray(inputSearchTerms) && inputSearchTerms.length > 0) ? inputSearchTerms[0] : ''; + return parsedSearchValue || ''; +} diff --git a/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts b/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts index d4d712bec..75e0fa91b 100644 --- a/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts +++ b/src/app/modules/angular-slickgrid/filter-conditions/stringFilterCondition.ts @@ -1,24 +1,37 @@ -import { FilterCondition, FilterConditionOption } from '../models/index'; +import { FilterCondition, FilterConditionOption, OperatorType, SearchTerm } from '../models/index'; import { testFilterCondition } from './filterUtilities'; -import { OperatorType } from '../models'; -export const stringFilterCondition: FilterCondition = (options: FilterConditionOption) => { +/** Execute filter condition check on each cell */ +export const executeStringFilterCondition: FilterCondition = (options: FilterConditionOption, parsedSearchValue: string | undefined) => { + if (parsedSearchValue === undefined && !options.operator) { + return true; + } + // make sure the cell value is a string by casting it when possible options.cellValue = (options.cellValue === undefined || options.cellValue === null) ? '' : options.cellValue.toString(); // make both the cell value and search value lower for case insensitive comparison const cellValue = options.cellValue.toLowerCase(); - let searchTerm = (Array.isArray(options.searchTerms) && options.searchTerms[0]) || ''; - if (typeof searchTerm === 'string') { - searchTerm = searchTerm.toLowerCase(); + if (typeof parsedSearchValue === 'string') { + parsedSearchValue = parsedSearchValue.toLowerCase(); } if (options.operator === '*' || options.operator === OperatorType.endsWith) { - return cellValue.endsWith(searchTerm); - } else if ((options.operator === '' && options.cellValueLastChar === '*') || options.operator === OperatorType.startsWith) { - return cellValue.startsWith(searchTerm); + 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(searchTerm) > -1); + return (cellValue.indexOf(parsedSearchValue) > -1); } - return testFilterCondition(options.operator || '==', cellValue, searchTerm); + return testFilterCondition(options.operator || '==', cellValue, parsedSearchValue); }; + +/** + * 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; +} diff --git a/src/app/modules/angular-slickgrid/filters/filters.index.ts b/src/app/modules/angular-slickgrid/filters/filters.index.ts index d0af93542..34883ab9c 100644 --- a/src/app/modules/angular-slickgrid/filters/filters.index.ts +++ b/src/app/modules/angular-slickgrid/filters/filters.index.ts @@ -41,7 +41,7 @@ export const Filters = { /** Range Date Filter (uses the Flactpickr Date picker with range option) */ dateRange: DateRangeFilter, - /** Alias to inputText, input type text filter */ + /** Alias to inputText, input type text filter (this is the default filter when no type is provided) */ input: InputFilter, /** diff --git a/src/app/modules/angular-slickgrid/models/column.interface.ts b/src/app/modules/angular-slickgrid/models/column.interface.ts index f564a773a..5f9239fd6 100644 --- a/src/app/modules/angular-slickgrid/models/column.interface.ts +++ b/src/app/modules/angular-slickgrid/models/column.interface.ts @@ -195,9 +195,10 @@ export interface Column { /** * When you do not know at hand the name of the Field to use for querying, - * the lib will run your callback to find out which Field name you want to use by the logic you defined. - * Useful when you only know the Field name by executing a certain logic in order to get the Field name to query from. - * @param {string} item data context + * the lib will run this callback when provided to find out which Field name you want to use by the logic you defined. + * Useful when you don't know in advance the field name to query from and/or is returned dynamically + * and can change on earch row while executing the code at that moment. + * @param {Object} dataContext - item data object * @return {string} name of the Field that will end up being used to query */ queryFieldNameGetterFn?: (dataContext: T) => string; diff --git a/src/app/modules/angular-slickgrid/models/columnFilters.interface.ts b/src/app/modules/angular-slickgrid/models/columnFilters.interface.ts index 866609af6..9e91e3b80 100644 --- a/src/app/modules/angular-slickgrid/models/columnFilters.interface.ts +++ b/src/app/modules/angular-slickgrid/models/columnFilters.interface.ts @@ -1,5 +1,5 @@ -import { ColumnFilter } from './columnFilter.interface'; +import { SearchColumnFilter } from './searchColumnFilter.interface'; export interface ColumnFilters { - [key: string]: ColumnFilter; + [key: string]: SearchColumnFilter; } diff --git a/src/app/modules/angular-slickgrid/models/filterCondition.interface.ts b/src/app/modules/angular-slickgrid/models/filterCondition.interface.ts index 3d8c61d37..0dac3390e 100644 --- a/src/app/modules/angular-slickgrid/models/filterCondition.interface.ts +++ b/src/app/modules/angular-slickgrid/models/filterCondition.interface.ts @@ -1,4 +1,5 @@ import { FilterConditionOption } from './filterConditionOption.interface'; +import { SearchTerm } from './searchTerm.type'; -export type FilterCondition = (options: FilterConditionOption) => boolean; +export type FilterCondition = (options: FilterConditionOption, parsedSearchTerms?: SearchTerm | SearchTerm[]) => boolean; diff --git a/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts b/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts index 6b5fa4c24..1844422c5 100644 --- a/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts +++ b/src/app/modules/angular-slickgrid/models/filterConditionOption.interface.ts @@ -1,12 +1,32 @@ import { FieldType } from './fieldType.enum'; import { OperatorString } from './operatorString'; +import { SearchTerm } from './searchTerm.type'; export interface FilterConditionOption { + /** optional object data key */ dataKey?: string; + + /** filter operator */ operator: OperatorString; + + /** cell value */ cellValue: any; - cellValueLastChar?: string; - fieldType: FieldType; - filterSearchType?: FieldType; - searchTerms?: string[] | number[]; + + /** last character of the cell value, which is helpful to know if we are dealing with "*" that would be mean startsWith */ + searchInputLastChar?: string; + + /** column field type */ + fieldType: typeof FieldType[keyof typeof FieldType]; + + /** filter search field type */ + filterSearchType?: typeof FieldType[keyof typeof FieldType]; + + /** + * Parsed Search Terms is similar to SearchTerms but is already parsed in the correct format, + * for example on a date field the searchTerms might be in string format but their respective parsedSearchTerms will be of type Date + */ + parsedSearchTerms?: SearchTerm[] | undefined; + + /** Search Terms provided by the user */ + searchTerms?: SearchTerm[] | undefined; } diff --git a/src/app/modules/angular-slickgrid/models/index.ts b/src/app/modules/angular-slickgrid/models/index.ts index 1976a86b9..fecc74e56 100644 --- a/src/app/modules/angular-slickgrid/models/index.ts +++ b/src/app/modules/angular-slickgrid/models/index.ts @@ -128,6 +128,7 @@ export * from './pagingInfo.interface'; export * from './queryArgument.interface'; export * from './rowDetailView.interface'; export * from './rowMoveManager.interface'; +export * from './searchColumnFilter.interface'; export * from './searchTerm.type'; export * from './selectedRange.interface'; export * from './selectOption.interface'; diff --git a/src/app/modules/angular-slickgrid/models/searchColumnFilter.interface.ts b/src/app/modules/angular-slickgrid/models/searchColumnFilter.interface.ts new file mode 100644 index 000000000..e7269075b --- /dev/null +++ b/src/app/modules/angular-slickgrid/models/searchColumnFilter.interface.ts @@ -0,0 +1,34 @@ +import { Column, } from './index'; +import { FieldType, OperatorString, OperatorType, SearchTerm } from '../models/index'; + +export interface SearchColumnFilter { + /** Column definition Id */ + columnId: string; + + /** Column definition */ + columnDef: Column; + + /** + * Parsed Search Terms is similar to SearchTerms but is already parsed in the correct format, + * for example on a date field the searchTerms might be in string format but their respective parsedSearchTerms will be of type Date + */ + parsedSearchTerms?: SearchTerm | SearchTerm[]; + + /** Search Terms to preload (collection), please note it is better to use the "presets" grid option which is more powerful. */ + searchTerms: SearchTerm[]; + + /** Operator to use when filtering (>, >=, EQ, IN, ...) */ + operator?: OperatorType | OperatorString; + + /** + * Useful when you want to display a certain field to the UI, but you want to use another field to query when Filtering/Sorting. + * Please note that it has higher precendence over the "field" property. + */ + queryField?: string; + + /** Last search input character when it is identified as "*" representing startsWith */ + searchInputLastChar?: string; + + /** What is the Field Type that can be used by the Filter (as precedence over the "type" set the column definition) */ + type: typeof FieldType[keyof typeof FieldType]; +} 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 e753c1d7a..a0f71dca7 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 @@ -19,6 +19,7 @@ import { import { Filters } from '../../filters'; import { FilterService } from '../filter.service'; import { FilterFactory } from '../../filters/filterFactory'; +import { getParsedSearchTermsByFieldType } from '../../filter-conditions'; import { SharedService } from '../shared.service'; import { CollectionService, SlickgridConfig } from '../../index'; import * as utilities from '../../services/backend-utilities'; @@ -171,7 +172,7 @@ describe('FilterService', () => { it('should call the same filter twice but expect the filter to be rendered only once', () => { const mockColumn = { - id: 'isActive', field: 'isActive', filterable: true, + id: 'isActive', field: 'isActive', filterable: true, type: FieldType.boolean, filter: { model: Filters.singleSelect, searchTerms: [true], collection: [{ value: true, label: 'True' }, { value: false, label: 'False' }], } @@ -187,7 +188,7 @@ describe('FilterService', () => { expect(service.isFilterFirstRender).toBe(false); expect(columnFilters).toEqual({ - isActive: { columnDef: mockColumn, columnId: 'isActive', operator: 'EQ', searchTerms: [true], }, + isActive: { columnDef: mockColumn, columnId: 'isActive', operator: 'EQ', searchTerms: [true], parsedSearchTerms: true, type: FieldType.boolean }, }); expect(filterMetadataArray.length).toBe(1); expect(filterMetadataArray[0]).toContainEntry(['$filterElm', expect.anything()]); @@ -313,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'] }; + 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); @@ -330,12 +331,13 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['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'] }; + 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); @@ -355,6 +357,7 @@ describe('FilterService', () => { columnFilters: { firstName: expectationColumnFilter }, operator: 'EQ', searchTerms: ['John'], + parsedSearchTerms: 'John', grid: gridStub }, expect.anything()); }); @@ -425,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'] }; + 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'); @@ -444,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'] }; - const filterLastExpectation = { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'] }; + 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'); @@ -573,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'] } }); + expect(service.getColumnFilters()).toEqual({ lastName: { columnDef: mockColumn2, columnId: 'lastName', operator: 'NE', searchTerms: ['Doe'], parsedSearchTerms: 'Doe', type: FieldType.string } }); expect(spyEmitter).toHaveBeenCalledWith('local'); }); }); @@ -587,38 +590,54 @@ describe('FilterService', () => { mockItem1 = { firstName: 'John', lastName: 'Doe', fullName: 'John Doe', age: 26, address: { zip: 123456 } }; }); + it('should execute "getParsedSearchTermsByFieldType" once if the first "customLocalFilter" is executed without parsedSearchTerms at the beginning', () => { + const searchTerms = ['John']; + const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; + jest.spyOn(gridStub, 'getColumns').mockReturnValue([]); + + service.init(gridStub); + 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(output).toBe(true); + }); + it('should return True (nothing to filter, all rows will be returned) when there are no column definition found', () => { - const searchValue = 'John'; + const searchTerms = ['John']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, FieldType.text); + const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is the same as the searchTerms', () => { - const searchValue = 'John'; + const searchTerms = ['John']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, FieldType.text); + const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should work on a hidden column by using the sharedService "allColumns" and return True when input value the same as the searchTerms', () => { - const searchValue = 'John'; + const searchTerms = ['John']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; sharedService.allColumns = [mockColumn1]; jest.spyOn(gridStub, 'getColumns').mockReturnValue([]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, FieldType.text); + const columnFilters = { firstName: { columnDef: undefined, columnId: 'firstName', operator: 'EQ', searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -647,111 +666,136 @@ describe('FilterService', () => { }); it('should return False when input value from datacontext is not equal to the searchTerms', () => { - const searchValue = 'Johnny'; + const searchTerms = ['Johnny']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); }); it('should work on a hidden column by using the sharedService "allColumns" and return return False when input value is not equal to the searchTerms', () => { - const searchValue = 'Johnny'; + const searchTerms = ['Johnny']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; sharedService.allColumns = [mockColumn1]; jest.spyOn(gridStub, 'getColumns').mockReturnValue([]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(false); }); it('should return True when input value from datacontext is a number and searchTerms is also a number', () => { - const searchValue = 26; - const mockColumn1 = { id: 'age', field: 'age', filterable: true } as Column; + const searchTerms = [26]; + const mockColumn1 = { id: 'age', field: 'age', filterable: true, type: FieldType.number } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { age: { columnDef: mockColumn1, columnId: 'age', operator: 'EQ', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'age', type: FieldType.number }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.number); + const columnFilters = { age: { ...columnFilter, operator: 'EQ', searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is a number, "filterSearchType" is a FieldType number and finally searchTerms is also a number', () => { - const searchValue = 26; + const searchTerms = [26]; const mockColumn1 = { id: 'age', field: 'age', filterable: true, filterSearchType: FieldType.number } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { age: { columnDef: mockColumn1, columnId: 'age', operator: 'EQ', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'age', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.number); + const columnFilters = { age: { ...columnFilter, operator: 'EQ', searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is equal to startsWith substring', () => { - const searchValue = 'Jo*'; + const searchTerms = ['Jo*']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator: 'EQ', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is equal to startsWith substring when using Operator startsWith', () => { - const searchValue = 'Jo'; + const searchTerms = ['Jo']; const operator = 'a*'; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator, searchTerms: [searchValue] } }; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, FieldType.text); + const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator, searchTerms, parsedSearchTerms, type: FieldType.string } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is equal to startsWith substring when having (*) as the last character in the searchTerms', () => { - const searchValue = 'Jo*'; + const searchTerms = ['Jo*']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is equal to endsWith substring', () => { - const searchValue = '*hn'; + const searchTerms = ['*hn']; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, 'text'); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); }); it('should return True when input value from datacontext is equal to endsWith substring when using Operator endsWith', () => { - const searchValue = '*hn'; + const searchTerms = ['*hn']; const operator = '*z'; const mockColumn1 = { id: 'firstName', field: 'firstName', filterable: true } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); service.init(gridStub); - const columnFilters = { firstName: { columnDef: mockColumn1, columnId: 'firstName', operator, searchTerms: [searchValue] } }; + const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; + const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, 'text'); + const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -771,12 +815,14 @@ describe('FilterService', () => { }); it('should return True when input value is a complex object searchTerms value is found following the dot notation', () => { - const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip' } as Column; + const searchTerms = [123456]; + const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip', type: FieldType.number } as Column; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms: [123456] } }; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, FieldType.number); + const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms, parsedSearchTerms, type: FieldType.number } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -784,13 +830,13 @@ describe('FilterService', () => { it('should return True when using row detail and the item is found in its parent', () => { gridOptionMock.enableRowDetailView = true; - const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip' } as Column; + const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip', type: FieldType.number } as Column; const mockItem2 = { __isPadding: true, __parent: mockItem1 }; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms: [123456] } }; + const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms: [123456], parsedSearchTerms: [123456], type: FieldType.number } }; const output = service.customLocalFilter(mockItem2, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -800,13 +846,13 @@ describe('FilterService', () => { // @ts-ignore gridOptionMock.rowDetailView = { keyPrefix: 'prefix_' }; gridOptionMock.enableRowDetailView = true; - const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip' } as Column; + const mockColumn1 = { id: 'zip', field: 'zip', filterable: true, queryFieldFilter: 'address.zip', type: FieldType.number } as Column; const mockItem2 = { prefix_isPadding: true, prefix_parent: mockItem1 }; jest.spyOn(gridStub, 'getColumns').mockReturnValue([mockColumn1]); jest.spyOn(dataViewStub, 'getIdxById').mockReturnValue(0); service.init(gridStub); - const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms: [123456] } }; + const columnFilters = { zip: { columnDef: mockColumn1, columnId: 'zip', operator: 'EQ', searchTerms: [123456], parsedSearchTerms: [123456], type: FieldType.number } }; const output = service.customLocalFilter(mockItem2, { dataView: dataViewStub, grid: gridStub, columnFilters }); expect(output).toBe(true); @@ -818,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'] } }; + 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); @@ -830,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'] } }; + 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); @@ -842,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'] } }; + 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); @@ -1024,7 +1070,7 @@ describe('FilterService', () => { gridOptionMock.enableFiltering = true; gridOptionMock.backendServiceApi = undefined; mockColumn1 = { id: 'firstName', name: 'firstName', field: 'firstName', filterable: true, filter: { model: Filters.compoundInputText } }; - mockColumn2 = { id: 'isActive', name: 'isActive', field: 'isActive', filterable: true, filter: { model: Filters.singleSelect, collection: [{ value: true, label: 'True' }, { value: false, label: 'False' }], } }; + mockColumn2 = { id: 'isActive', name: 'isActive', field: 'isActive', type: FieldType.boolean, filterable: true, filter: { model: Filters.select, collection: [{ value: true, label: 'True' }, { value: false, label: 'False' }], } }; mockArgs1 = { grid: gridStub, column: mockColumn1, node: document.getElementById(DOM_ELEMENT_ID) }; mockArgs2 = { grid: gridStub, column: mockColumn2, node: document.getElementById(DOM_ELEMENT_ID) }; mockNewFilters = [ @@ -1058,8 +1104,8 @@ describe('FilterService', () => { expect(emitSpy).toHaveBeenCalledWith('local'); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith' }, - isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ' } + 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 } }); }); @@ -1076,8 +1122,8 @@ describe('FilterService', () => { expect(emitSpy).not.toHaveBeenCalled(); expect(clearSpy).toHaveBeenCalledWith(false); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith' }, - isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ' } + 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 } }); }); @@ -1102,8 +1148,8 @@ describe('FilterService', () => { expect(backendProcessSpy).not.toHaveBeenCalled(); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith' }, - isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ' } + 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); expect(mockRefreshBackendDataset).toHaveBeenCalledWith(gridOptionMock); @@ -1131,12 +1177,13 @@ describe('FilterService', () => { expect(mockRefreshBackendDataset).not.toHaveBeenCalled(); expect(backendUpdateSpy).toHaveBeenCalledWith(mockNewFilters, true); expect(service.getColumnFilters()).toEqual({ - firstName: { columnId: 'firstName', columnDef: mockColumn1, searchTerms: ['Jane'], operator: 'StartsWith' }, - isActive: { columnId: 'isActive', columnDef: mockColumn2, searchTerms: [false], operator: 'EQ' } + 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); }); }); + describe('disableFilterFunctionality method', () => { beforeEach(() => { gridOptionMock.enableFiltering = true; @@ -1362,7 +1409,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'] } }; + 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 }); @@ -1375,7 +1422,7 @@ describe('FilterService', () => { it('should return False when item is found BUT its parent is collapsed', () => { const spyRxjs = jest.spyOn(service.onFilterChanged, 'next'); const preFilterSpy = jest.spyOn(service, 'preFilterTreeData'); - jest.spyOn(dataViewStub, 'getItemById').mockReturnValueOnce({ ...dataset[4], __collapsed: true }) + jest.spyOn(dataViewStub, 'getItemById').mockReturnValueOnce({ ...dataset[4] as any, __collapsed: true }) .mockReturnValueOnce(dataset[5]) .mockReturnValueOnce(dataset[6]); @@ -1383,14 +1430,14 @@ describe('FilterService', () => { service.init(gridStub); service.bindLocalOnFilter(gridStub); - gridStub.onHeaderRowCellRendered.notify(mockArgs1, new Slick.EventData(), gridStub); - gridStub.onHeaderRowCellRendered.notify(mockArgs2, new Slick.EventData(), gridStub); + 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'] } }; + 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 }); - expect(spyRxjs).toHaveBeenCalledWith([{ columnId: 'file', operator: 'Contains', searchTerms: ['map',] }]); + expect(spyRxjs).toHaveBeenCalledWith([{ columnId: 'file', operator: 'Contains', searchTerms: ['map'] }]); expect(output).toBe(false); expect(preFilterSpy).toHaveBeenCalledWith(dataset, columnFilters); expect(preFilterSpy).toHaveReturnedWith([21, 4, 5]); @@ -1399,7 +1446,7 @@ describe('FilterService', () => { it('should return False when item is not found in the dataset', () => { const spyRxjs = jest.spyOn(service.onFilterChanged, 'next'); const preFilterSpy = jest.spyOn(service, 'preFilterTreeData'); - jest.spyOn(dataViewStub, 'getItemById').mockReturnValueOnce({ ...dataset[4] }) + jest.spyOn(dataViewStub, 'getItemById').mockReturnValueOnce({ ...dataset[4] as any }) .mockReturnValueOnce(dataset[5]) .mockReturnValueOnce(dataset[6]); @@ -1407,16 +1454,16 @@ describe('FilterService', () => { service.init(gridStub); service.bindLocalOnFilter(gridStub); - gridStub.onHeaderRowCellRendered.notify(mockArgs1, new Slick.EventData(), gridStub); - gridStub.onHeaderRowCellRendered.notify(mockArgs2, new Slick.EventData(), gridStub); + 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', searchTerms: ['unknown'] } } as ColumnFilters; + const columnFilters = { file: { columnDef: mockColumn1, columnId: 'file', searchTerms: ['unknown'], type: FieldType.string } } as ColumnFilters; service.updateFilters([{ columnId: 'file', operator: '', searchTerms: ['unknown'] }], true, true, true); const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); - expect(spyRxjs).toHaveBeenCalledWith([{ columnId: 'file', operator: 'Contains', searchTerms: ['unknown',] }]); + expect(spyRxjs).toHaveBeenCalledWith([{ columnId: 'file', operator: 'Contains', searchTerms: ['unknown'] }]); expect(output).toBe(false); - expect(preFilterSpy).toHaveBeenCalledWith(dataset, { ...columnFilters, file: { ...columnFilters.file, operator: 'Contains' } }); // 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/src/app/modules/angular-slickgrid/services/__tests__/utilities.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/utilities.spec.ts index 2b2d192d2..d26070434 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/utilities.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/utilities.spec.ts @@ -20,6 +20,7 @@ import { htmlEncode, htmlEntityDecode, htmlEntityEncode, + isNumber, mapMomentDateFormatWithFieldType, mapFlatpickrDateFormatWithFieldType, mapOperatorByFieldType, @@ -147,6 +148,33 @@ describe('Service/Utilies', () => { }); }); + describe('isNumber method', () => { + it('should return True when comparing a number from a number/string variable when strict mode is disable', () => { + const result1 = isNumber(22); + const result2 = isNumber('33'); + + expect(result1).toBeTrue(); + expect(result2).toBeTrue(); + }); + + it('should return False when comparing string that has a number but also other text within and the strict mode is disable', () => { + const result = isNumber('33test'); + expect(result).toBeFalse(); + }); + + it('should return False when comparing a number from a string variable with strict mode enabled', () => { + const result1 = isNumber(null, true); + const result2 = isNumber(undefined, true); + const result3 = isNumber('33', true); + const result4 = isNumber('33test', true); + + expect(result1).toBeFalse(); + expect(result2).toBeFalse(); + expect(result3).toBeFalse(); + expect(result4).toBeFalse(); + }); + }); + describe('convertParentChildArrayToHierarchicalView method', () => { it('should take a parent/child array and return a hierarchical array structure', () => { const input = [ diff --git a/src/app/modules/angular-slickgrid/services/filter.service.ts b/src/app/modules/angular-slickgrid/services/filter.service.ts index 8003c10aa..a836df12e 100644 --- a/src/app/modules/angular-slickgrid/services/filter.service.ts +++ b/src/app/modules/angular-slickgrid/services/filter.service.ts @@ -4,7 +4,6 @@ import { isObservable, Subject } from 'rxjs'; import { Column, - ColumnFilter, ColumnFilters, CurrentFilter, EmitterType, @@ -18,13 +17,14 @@ import { KeyCode, OperatorString, OperatorType, + SearchColumnFilter, SearchTerm, SlickEvent, SlickEventHandler, } from './../models/index'; import { executeBackendCallback, refreshBackendDataset } from './backend-utilities'; -import { getDescendantProperty, mapOperatorByFieldType } from './utilities'; -import { FilterConditions } from './../filter-conditions'; +import { deepCopy, getDescendantProperty, mapOperatorByFieldType } from './utilities'; +import { FilterConditions, getParsedSearchTermsByFieldType } from './../filter-conditions'; import { FilterFactory } from '../filters/filterFactory'; import { SharedService } from './shared.service'; @@ -38,19 +38,19 @@ const DEFAULT_FILTER_TYPING_DEBOUNCE = 500; @Injectable() export class FilterService { - private _eventHandler: SlickEventHandler; - private _isFilterFirstRender = true; - private _firstColumnIdRendered = ''; + protected _eventHandler: SlickEventHandler; + protected _isFilterFirstRender = true; + protected _firstColumnIdRendered = ''; protected _filtersMetadata: Array = []; - private _columnFilters: ColumnFilters = {}; - private _grid: any; - private _onSearchChange: SlickEvent | null; - private _tmpPreFilteredData: number[]; - private httpCancelRequests$: Subject = new Subject(); // this will be used to cancel any pending http request + protected _columnFilters: ColumnFilters = {}; + protected _grid: any; + protected _onSearchChange: SlickEvent | null; + protected _tmpPreFilteredData: number[]; + protected httpCancelRequests$: Subject = new Subject(); // this will be used to cancel any pending http request onFilterChanged = new Subject(); onFilterCleared = new Subject(); - constructor(private filterFactory: FilterFactory, private sharedService: SharedService) { + constructor(protected filterFactory: FilterFactory, protected sharedService: SharedService) { this._eventHandler = new Slick.EventHandler(); this._onSearchChange = new Slick.Event(); } @@ -71,20 +71,24 @@ export class FilterService { } /** Getter for the Grid Options pulled through the Grid Object */ - private get _gridOptions(): GridOption { + protected get _gridOptions(): GridOption { return (this._grid && this._grid.getOptions) ? this._grid.getOptions() : {}; } /** Getter for the Column Definitions pulled through the Grid Object */ - private get _columnDefinitions(): Column[] { + protected get _columnDefinitions(): Column[] { return (this._grid && this._grid.getColumns) ? this._grid.getColumns() : []; } /** Getter of SlickGrid DataView object */ - private get _dataView(): any { + protected get _dataView(): any { return (this._grid && this._grid.getData) ? this._grid.getData() : {}; } + /** + * Initialize the Service + * @param grid + */ init(grid: any): void { this._grid = grid; @@ -219,10 +223,10 @@ export class FilterService { clearFilterByColumnId(event: Event, columnId: number | string) { // get current column filter before clearing, this allow us to know if the filter was empty prior to calling the clear filter - const currentColumnFilters = Object.keys(this._columnFilters) as ColumnFilter[]; - let currentColFilter: ColumnFilter; - if (Array.isArray(currentColumnFilters)) { - currentColFilter = currentColumnFilters.find((name) => name === columnId); + const currentFilterColumnIds = Object.keys(this._columnFilters); + let currentColFilter: string | undefined; + if (Array.isArray(currentFilterColumnIds)) { + currentColFilter = currentFilterColumnIds.find(name => name === `${columnId}`); } // find the filter object and call its clear method with true (the argument tells the method it was called by a clear filter) @@ -237,7 +241,7 @@ export class FilterService { // when using a backend service, we need to manually trigger a filter change but only if the filter was previously filled if (isBackendApi) { emitter = EmitterType.remote; - if (currentColFilter) { + if (currentColFilter !== undefined) { this.onBackendFilterChange(event as KeyboardEvent, { grid: this._grid, columnFilters: this._columnFilters }); } } @@ -324,13 +328,26 @@ export class FilterService { } else { if (typeof columnFilters === 'object') { for (const columnId of Object.keys(columnFilters)) { - const columnFilter = columnFilters[columnId] as ColumnFilter; - const conditionOptions = this.getFilterConditionOptionsOrBoolean(item, columnFilter, columnId, grid, dataView); + const columnFilter = columnFilters[columnId] as SearchColumnFilter; + const conditionOptions = this.preProcessFilterConditionOnDataContext(item, columnFilter, grid); + if (typeof conditionOptions === 'boolean') { return conditionOptions; } - if (!FilterConditions.executeMappedCondition(conditionOptions as FilterConditionOption)) { + let parsedSearchTerms = columnFilter && columnFilter.parsedSearchTerms; // parsed term could a single value or an array of values + + // in the rare case that it's empty (it can happen when creating an external grid global search) + // then get the parsed terms, once it's filled it typically won't ask for it anymore + if (parsedSearchTerms === undefined) { + parsedSearchTerms = getParsedSearchTermsByFieldType(columnFilter.searchTerms, columnFilter.columnDef.type || FieldType.string); // parsed term could a single value or an array of values + if (parsedSearchTerms !== undefined) { + columnFilter.parsedSearchTerms = parsedSearchTerms; + } + } + + // execute the filtering conditions check (all cell values vs search term(s)) + if (!FilterConditions.executeFilterConditionTest(conditionOptions as FilterConditionOption, parsedSearchTerms)) { return false; } } @@ -341,15 +358,75 @@ export class FilterService { return true; } - getFilterConditionOptionsOrBoolean(item: any, columnFilter: ColumnFilter, columnId: string | number, grid: any, dataView: any): FilterConditionOption | boolean { + /** + * Loop through each form input search filter and parse their searchTerms, + * for example a CompoundDate Filter will be parsed as a Moment object. + * Also if we are dealing with a text filter input, + * an operator can optionally be part of the filter itself and we need to extract it from there, + * for example a filter of "John*" will be analyzed as { operator: StartsWith, searchTerms: ['John'] } + * @param inputSearchTerms - filter search terms + * @param columnFilter - column filter object (the object properties represent each column id and the value is the filter metadata) + * @returns FilterConditionOption + */ + parseFormInputFilterConditions(inputSearchTerms: SearchTerm[] | undefined, columnFilter: Omit): Omit { + const searchValues: SearchTerm[] = deepCopy(inputSearchTerms) || []; + let fieldSearchValue = (Array.isArray(searchValues) && searchValues.length === 1) ? searchValues[0] : ''; + const columnDef = columnFilter.columnDef; + const fieldType = columnDef && columnDef.filter && columnDef.filter.type || columnDef && columnDef.type || FieldType.string; + + let matches = null; + if (fieldType !== FieldType.object) { + fieldSearchValue = (fieldSearchValue === undefined || fieldSearchValue === null) ? '' : `${fieldSearchValue}`; // make sure it's a string + matches = fieldSearchValue.match(/^([<>!=\*]{0,2})(.*[^<>!=\*])?([\*]?)$/); // group 1: Operator, 2: searchValue, 3: last char is '*' (meaning starts with, ex.: abc*) + } + + let operator = (!!(matches) ? matches[1] : columnFilter.operator); + const searchTerm = (!!matches) ? matches[2] : ''; + const inputLastChar = (!!matches) ? matches[3] : (operator === '*z' ? '*' : ''); + + if (typeof fieldSearchValue === 'string') { + fieldSearchValue = fieldSearchValue.replace(`'`, `''`); // escape any single quotes by doubling them + if (operator === '*' || operator === '*z') { + operator = OperatorType.endsWith; + } else if (operator === 'a*' || inputLastChar === '*') { + operator = OperatorType.startsWith; + } + } + + // if search value has a regex match we will only keep the value without the operator + // in this case we need to overwrite the returned search values to truncate operator from the string search + if (Array.isArray(matches) && matches.length >= 1 && (Array.isArray(searchValues) && searchValues.length === 1)) { + searchValues[0] = searchTerm; + } + + return { + dataKey: columnDef.dataKey, + fieldType, + searchTerms: searchValues || [], + operator: operator as OperatorString, + searchInputLastChar: inputLastChar, + filterSearchType: columnDef.filterSearchType + } as FilterConditionOption; + } + + /** + * PreProcess the filter(s) condition(s) on each item data context, the result might be a boolean or FilterConditionOption object. + * It will be a boolean when the searchTerms are invalid or the column is not found (it so it will return True and the item won't be filtered out from the grid) + * or else a FilterConditionOption object with the necessary info for the test condition needs to be processed in a further stage. + * @param item - item data context + * @param columnFilter - column filter object (the object properties represent each column id and the value is the filter metadata) + * @param grid - SlickGrid object + * @returns FilterConditionOption or boolean + */ + preProcessFilterConditionOnDataContext(item: any, columnFilter: SearchColumnFilter, grid: any): FilterConditionOption | boolean { + const columnDef = columnFilter.columnDef; + const columnId = columnFilter.columnId; let columnIndex = grid.getColumnIndex(columnId) as number; - let columnDef = grid.getColumns()[columnIndex] as Column; - // it might be a hidden column, if so it won't be part of the getColumns (because it we hide a column via setColumns) + // it might be a hidden column, if so it won't be part of the getColumns (because it could be hidden via setColumns()) // when that happens we can try to get the column definition from all defined columns if (!columnDef && this.sharedService && Array.isArray(this.sharedService.allColumns)) { - columnIndex = this.sharedService.allColumns.findIndex((col) => col.field === columnId); - columnDef = this.sharedService.allColumns[columnIndex]; + columnIndex = this.sharedService.allColumns.findIndex(col => col.field === columnId); } // if we still don't have a column definition then we should return then row anyway (true) @@ -365,13 +442,11 @@ export class FilterService { } } - const dataKey = columnDef.dataKey; let queryFieldName = (columnDef.filter && columnDef.filter.queryField) || columnDef.queryFieldFilter || columnDef.queryField || columnDef.field || ''; if (typeof columnDef.queryFieldNameGetterFn === 'function') { queryFieldName = columnDef.queryFieldNameGetterFn(item); } const fieldType = (columnDef.filter && columnDef.filter.type) || columnDef.type || FieldType.string; - const filterSearchType = (columnDef.filterSearchType) ? columnDef.filterSearchType : null; let cellValue = item[queryFieldName]; // when item is a complex object (dot "." notation), we need to filter the value contained in the object tree @@ -379,43 +454,14 @@ export class FilterService { cellValue = getDescendantProperty(item, queryFieldName); } - // if we find searchTerms use them but make a deep copy so that we don't affect original array - // we might have to overwrite the value(s) locally that are returned - // e.g: we don't want to operator within the search value, since it will fail filter condition check trigger afterward - const searchValues: SearchTerm[] = (columnFilter && columnFilter.searchTerms) ? $.extend(true, [], columnFilter.searchTerms) : []; - let fieldSearchValue = (Array.isArray(searchValues) && searchValues.length === 1) ? searchValues[0] : ''; - - let matches = null; - if (fieldType !== FieldType.object) { - fieldSearchValue = '' + fieldSearchValue; // make sure it's a string - matches = fieldSearchValue.match(/^([<>!=\*]{0,2})(.*[^<>!=\*])([\*]?)$/); // group 1: Operator, 2: searchValue, 3: last char is '*' (meaning starts with, ex.: abc*) - } - - let operator = columnFilter.operator || ((matches) ? matches[1] : ''); - const searchTerm = (!!matches) ? matches[2] : ''; - const lastValueChar = (!!matches) ? matches[3] : (operator === '*z' ? '*' : ''); - - if (searchValues && searchValues.length > 1) { - fieldSearchValue = searchValues.join(','); - } else if (typeof fieldSearchValue === 'string') { - // escaping the search value - fieldSearchValue = fieldSearchValue.replace(`'`, `''`); // escape single quotes by doubling them - if (operator === '*' || operator === 'a*' || operator === '*z' || lastValueChar === '*') { - operator = (operator === '*' || operator === '*z') ? OperatorType.endsWith : OperatorType.startsWith; - } - } + const operator = columnFilter.operator; + const searchValues = columnFilter.searchTerms || []; // no need to query if search value is empty or if the search value is in fact equal to the operator - if (searchTerm === '' && (!searchValues || (Array.isArray(searchValues) && (searchValues.length === 0 || searchValues.length === 1 && operator === searchValues[0])))) { + if (!searchValues || (Array.isArray(searchValues) && (searchValues.length === 0 || searchValues.length === 1 && operator === searchValues[0]))) { return true; } - // if search value has a regex match we will only keep the value without the operator - // in this case we need to overwrite the returned search values to truncate operator from the string search - if (Array.isArray(matches) && matches.length >= 1 && (Array.isArray(searchValues) && searchValues.length === 1)) { - searchValues[0] = searchTerm; - } - // filter search terms should always be string type (even though we permit the end user to input numbers) // so make sure each term are strings, if user has some default search terms, we will cast them to string if (searchValues && Array.isArray(searchValues) && fieldType !== FieldType.object) { @@ -427,9 +473,10 @@ export class FilterService { // when using localization (i18n), we should use the formatter output to search as the new cell value 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, columnIndex, cellValue, columnDef, item, this._grid) : ''; + cellValue = (columnDef && typeof columnDef.formatter === 'function') ? columnDef.formatter(rowIndex || 0, columnIndex, cellValue, columnDef, item, this._grid) : ''; } // make sure cell value is always a string @@ -437,15 +484,14 @@ export class FilterService { cellValue = cellValue.toString(); } - const currentCellValue = cellValue; return { - dataKey, + dataKey: columnDef.dataKey, fieldType, searchTerms: searchValues, - cellValue: currentCellValue, + cellValue, operator: operator as OperatorString, - cellValueLastChar: lastValueChar, - filterSearchType + searchInputLastChar: columnFilter.searchInputLastChar, + filterSearchType: columnDef.filterSearchType, } as FilterConditionOption; } @@ -471,17 +517,36 @@ export class FilterService { delete treeObj[inputArray[i][dataViewIdIdentifier]].__used; } + // Step 1. prepare search filter by getting their parsed value(s), for example if it's a date filter then parse it to a Moment object + // loop through all column filters once and get parsed filter search value then save a reference in the columnFilter itself + // it is much more effective to do it outside and prior to Step 2 so that we don't re-parse search filter for no reason while checking every row + for (const columnId of Object.keys(columnFilters)) { + const columnFilter = columnFilters[columnId] as SearchColumnFilter; + const searchValues: SearchTerm[] = (columnFilter && columnFilter.searchTerms) ? deepCopy(columnFilter.searchTerms) : []; + + const inputSearchConditions = this.parseFormInputFilterConditions(searchValues, columnFilter); + + const columnDef = columnFilter.columnDef; + const fieldType = columnDef && columnDef.filter && columnDef.filter.type || columnDef && columnDef.type || FieldType.string; + const parsedSearchTerms = getParsedSearchTermsByFieldType(inputSearchConditions.searchTerms, fieldType); // parsed term could a single value or an array of values + if (parsedSearchTerms !== undefined) { + columnFilter.parsedSearchTerms = parsedSearchTerms; + } + } + + // Step 2. loop through every item data context to execute filter condition check for (let i = 0; i < inputArray.length; i++) { const item = inputArray[i]; let matchFilter = true; // valid until proven otherwise // loop through all column filters and execute filter condition(s) for (const columnId of Object.keys(columnFilters)) { - const columnFilter = columnFilters[columnId] as ColumnFilter; - const conditionOptionResult = this.getFilterConditionOptionsOrBoolean(item, columnFilter, columnId, this._grid, this._dataView); + const columnFilter = columnFilters[columnId] as SearchColumnFilter; + const conditionOptionResult = this.preProcessFilterConditionOnDataContext(item, columnFilter, this._grid); if (conditionOptionResult) { - const conditionResult = (typeof conditionOptionResult === 'boolean') ? conditionOptionResult : FilterConditions.executeMappedCondition(conditionOptionResult as FilterConditionOption); + const parsedSearchTerms = columnFilter && columnFilter.parsedSearchTerms; // parsed term could a single value or an array of values + const conditionResult = (typeof conditionOptionResult === 'boolean') ? conditionOptionResult : FilterConditions.executeFilterConditionTest(conditionOptionResult as FilterConditionOption, parsedSearchTerms); if (conditionResult) { // don't return true since we still need to check other keys in columnFilters continue; @@ -542,7 +607,7 @@ export class FilterService { } /** - * A simple function that is binded to the subscriber and emit a change when the filter is called. + * A simple function that will be called to emit a change when a filter changes. * Other services, like Pagination, can then subscribe to it. * @param caller */ @@ -564,7 +629,6 @@ export class FilterService { throw new Error('Something went wrong when trying to bind the "onBackendFilterChange(event, args)" function, it seems that "args" is not populated correctly'); } - // const gridOptions: GridOption = (args.grid && args.grid.getOptions) ? args.grid.getOptions() : {}; const backendApi = this._gridOptions.backendServiceApi; if (!backendApi || !backendApi.process || !backendApi.service) { @@ -717,6 +781,7 @@ export class FilterService { * @param filters array * @param triggerEvent defaults to True, do we want to emit a filter changed event? * @param triggerBackendQuery defaults to True, which will query the backend. + * @param triggerOnSearchChangeEvent defaults to False, can be useful with Tree Data structure where the onSearchEvent has to run to execute a prefiltering step */ updateFilters(filters: CurrentFilter[], emitChangedEvent = true, triggerBackendQuery = true, triggerOnSearchChangeEvent = false) { if (!this._filtersMetadata || this._filtersMetadata.length === 0 || !this._gridOptions || !this._gridOptions.enableFiltering) { @@ -750,7 +815,7 @@ export class FilterService { if (backendApi) { const backendApiService = backendApi && backendApi.service; - if (backendApiService) { + if (backendApiService && backendApiService.updateFilters) { backendApiService.updateFilters(filters, true); if (triggerBackendQuery) { refreshBackendDataset(this._gridOptions); @@ -766,19 +831,19 @@ export class FilterService { } // -- - // private functions + // protected functions // ------------------- /** Add all created filters (from their template) to the header row section area */ - private addFilterTemplateToHeaderRow(_event: Event, args: { column: Column; grid: any; node: HTMLElement }, isFilterFirstRender = true) { + protected addFilterTemplateToHeaderRow(_event: Event, args: { column: Column; grid: any; node: HTMLElement }, isFilterFirstRender = true) { const columnDef = args.column; const columnId = columnDef && columnDef.id || ''; if (columnDef && columnId !== 'selector' && columnDef.filterable) { let searchTerms: SearchTerm[] | undefined; - let operator: OperatorType | OperatorString; - const newFilter: Filter | undefined = this.filterFactory.createFilter(args.column.filter); - operator = (columnDef && columnDef.filter && columnDef.filter.operator) || (newFilter && newFilter.operator) || undefined; + let operator: OperatorString | OperatorType | undefined; + const newFilter: Filter | undefined = this.filterFactory.createFilter(columnDef.filter); + operator = (columnDef && columnDef.filter && columnDef.filter.operator) || (newFilter && newFilter.operator); if (this._columnFilters[columnDef.id]) { searchTerms = this._columnFilters[columnDef.id].searchTerms || undefined; @@ -820,9 +885,9 @@ export class FilterService { /** * Callback method that is called and executed by the individual Filter (DOM element), - * for example when user type in a word to search (which uses InputFilter), this Filter will execute the callback from an input change event. + * for example when user starts typing chars on a search input (which uses InputFilter), this Filter will execute the callback from an input change event. */ - private callbackSearchEvent(event: any, args: FilterCallbackArg) { + protected callbackSearchEvent(event: any | undefined, args: FilterCallbackArg) { if (args) { const searchTerm = ((event && event.target) ? (event.target as HTMLInputElement).value : undefined); const searchTerms = (args.searchTerms && Array.isArray(args.searchTerms)) ? args.searchTerms : (searchTerm ? [searchTerm] : undefined); @@ -833,6 +898,7 @@ export class FilterService { const hasSearchTerms = searchTerms && Array.isArray(searchTerms); const termsCount = hasSearchTerms && searchTerms && searchTerms.length; const oldColumnFilters = { ...this._columnFilters }; + let parsedSearchTerms: SearchTerm | SearchTerm[] | undefined; if (columnDef && columnId) { if (!hasSearchTerms || termsCount === 0 || (termsCount === 1 && Array.isArray(searchTerms) && searchTerms[0] === '')) { @@ -840,14 +906,25 @@ export class FilterService { // without doing this, it would leave an incorrect state of the previous column filters when filtering on another column delete this._columnFilters[columnId]; } else { - const colId = '' + columnId as string; - const colFilter: ColumnFilter = { + const colId = `${columnId}`; + const colFilter: Omit = { columnId: colId, columnDef, - searchTerms, + parsedSearchTerms: [], + type: fieldType }; - colFilter.operator = operator || mapOperatorByFieldType(fieldType); - this._columnFilters[colId] = colFilter; + const inputSearchConditions = this.parseFormInputFilterConditions(searchTerms, colFilter); + colFilter.operator = operator || inputSearchConditions.operator || mapOperatorByFieldType(fieldType); + parsedSearchTerms = getParsedSearchTermsByFieldType(inputSearchConditions.searchTerms, fieldType); + if (parsedSearchTerms !== undefined) { + colFilter.parsedSearchTerms = parsedSearchTerms; + } + + // use searchTerms only coming from the input search result because original terms might include extra operator symbols within their string + // and the input search result would be correctly stripped them from input result and assigned to the appropriate operator + // for example we might have: { searchTerms: ['*doe'] } and that should be reassigned to: { operator: EndsWith, searchTerms: 'doe' } + (colFilter as SearchColumnFilter).searchTerms = inputSearchConditions.searchTerms || []; + this._columnFilters[colId] = colFilter as SearchColumnFilter; } } @@ -867,6 +944,7 @@ export class FilterService { columnFilters: this._columnFilters, operator: operator || mapOperatorByFieldType(fieldType), searchTerms, + parsedSearchTerms, grid: this._grid }, eventData); } @@ -880,7 +958,7 @@ export class FilterService { * (if we previously deleted these properties we wouldn't be able to change them back since these properties wouldn't exist anymore, hence why we just hide the commands) * @param {boolean} isDisabling - are we disabling the filter functionality? Defaults to true */ - private disableAllFilteringCommands(isDisabling = true): Column[] { + protected disableAllFilteringCommands(isDisabling = true): Column[] { const columnDefinitions = this._grid.getColumns(); // loop through column definition to hide/show header menu commands @@ -912,13 +990,18 @@ export class FilterService { return columnDefinitions; } - private updateColumnFilters(searchTerms: SearchTerm[] | undefined, columnDef: any, operator?: OperatorType | OperatorString) { + protected updateColumnFilters(searchTerms: SearchTerm[] | undefined, columnDef: any, operator?: OperatorType | OperatorString) { + const fieldType = columnDef && columnDef.filter && columnDef.filter.type || columnDef && columnDef.type || FieldType.string; + const parsedSearchTerms = getParsedSearchTermsByFieldType(searchTerms, fieldType); // parsed term could a single value or an array of values + if (searchTerms && columnDef) { this._columnFilters[columnDef.id] = { columnId: columnDef.id, columnDef, searchTerms, - operator + operator, + parsedSearchTerms, + type: fieldType, }; } } diff --git a/src/app/modules/angular-slickgrid/services/graphql.service.ts b/src/app/modules/angular-slickgrid/services/graphql.service.ts index c885291e1..938f2ef66 100644 --- a/src/app/modules/angular-slickgrid/services/graphql.service.ts +++ b/src/app/modules/angular-slickgrid/services/graphql.service.ts @@ -393,7 +393,7 @@ export class GraphqlService implements BackendService { throw new Error(`GraphQL filter could not find the field name to query the search, your column definition must include a valid "field" or "name" (optionally you can also use the "queryfield").`); } - fieldSearchValue = `${fieldSearchValue}`; // make sure it's a string + fieldSearchValue = (fieldSearchValue === undefined || fieldSearchValue === null) ? '' : `${fieldSearchValue}`; // make sure it's a string const matches = fieldSearchValue.match(/^([<>!=\*]{0,2})(.*[^<>!=\*])([\*]?)$/); // group 1: Operator, 2: searchValue, 3: last char is '*' (meaning starts with, ex.: abc*) let operator = columnFilter.operator || ((matches) ? matches[1] : ''); searchValue = (!!matches) ? matches[2] : ''; diff --git a/src/app/modules/angular-slickgrid/services/grid-odata.service.ts b/src/app/modules/angular-slickgrid/services/grid-odata.service.ts index 61e1e3548..eb42262e0 100644 --- a/src/app/modules/angular-slickgrid/services/grid-odata.service.ts +++ b/src/app/modules/angular-slickgrid/services/grid-odata.service.ts @@ -269,7 +269,7 @@ export class GridOdataService implements BackendService { throw new Error(`GridOData filter could not find the field name to query the search, your column definition must include a valid "field" or "name" (optionally you can also use the "queryfield").`); } - fieldSearchValue = '' + fieldSearchValue; // make sure it's a string + fieldSearchValue = (fieldSearchValue === undefined || fieldSearchValue === null) ? '' : `${fieldSearchValue}`; // make sure it's a string const matches = fieldSearchValue.match(/^([<>!=\*]{0,2})(.*[^<>!=\*])([\*]?)$/); // group 1: Operator, 2: searchValue, 3: last char is '*' (meaning starts with, ex.: abc*) let operator = columnFilter.operator || ((matches) ? matches[1] : ''); let searchValue = (!!matches) ? matches[2] : ''; diff --git a/src/app/modules/angular-slickgrid/services/utilities.ts b/src/app/modules/angular-slickgrid/services/utilities.ts index 365b970d8..af6b53406 100644 --- a/src/app/modules/angular-slickgrid/services/utilities.ts +++ b/src/app/modules/angular-slickgrid/services/utilities.ts @@ -334,6 +334,19 @@ export function htmlEncodedStringWithPadding(inputStr: string, paddingLength: nu return outputStr; } +/** + * Check if input value is a number, by default it won't be a strict checking + * but optionally we could check for strict equality, for example in strict "3" will return False but without strict it will return True + * @param value - input value of any type + * @param strict - when using strict it also check for strict equality, for example in strict "3" will return but without strict it will return true + */ +export function isNumber(value: any, strict = false) { + if (strict) { + return (value === null || value === undefined || typeof value === 'string') ? false : !isNaN(value); + } + return (value === null || value === undefined || value === '') ? false : !isNaN(+value); +} + /** * Take a number (or a string) and display it as a formatted decimal string with defined minimum and maximum decimals * @param input diff --git a/test/cypress/integration/example03.spec.js b/test/cypress/integration/example03.spec.js index acd573060..d44e91266 100644 --- a/test/cypress/integration/example03.spec.js +++ b/test/cypress/integration/example03.spec.js @@ -213,4 +213,30 @@ describe('Example 3 - Grid with Editors', () => { cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(11)`).click(); cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(11) > input.editor-checkbox.editor-effort-driven`).uncheck(); }); + + it('should open the "Prerequisites" Filter then choose "Task 3", "Task 4" and "Task 8" from the list and expect to see 2 rows of data in the grid', () => { + cy.get('div.ms-filter.filter-prerequisites') + .trigger('click'); + + cy.get('.ms-drop') + .contains(/^Task 3$/) // use regexp to avoid finding first Task 3 which is in fact Task 399 + .click(); + + cy.get('.ms-drop') + .contains(/^Task 4$/) + .click(); + + cy.get('.ms-drop') + .contains(/^Task 8$/) + .click(); + + cy.get('.ms-ok-button') + .click(); + + cy.get('.slick-row') + .should('have.length', 2); + + cy.get(`[style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'Task 4'); + cy.get(`[style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', 'Task 8'); + }); }); diff --git a/test/cypress/package.json b/test/cypress/package.json index 6b843ba52..1237d17a4 100644 --- a/test/cypress/package.json +++ b/test/cypress/package.json @@ -11,7 +11,7 @@ "license": "MIT", "devDependencies": { "cypress": "^6.4.0", - "mocha": "^8.2.1", + "mocha": "^8.3.0", "mochawesome": "^6.2.1" } } From 012c1a5b0af08432ed13bebe0987494a6d26aad3 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Fri, 12 Feb 2021 15:01:46 -0500 Subject: [PATCH 2/2] refactor: add missing TS Types --- .../services/__tests__/filter.service.spec.ts | 4 +- .../__tests__/graphql.service.spec.ts | 86 +++++----- .../__tests__/grid-odata.service.spec.ts | 154 +++++++++--------- 3 files changed, 122 insertions(+), 122 deletions(-) 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 a0f71dca7..83a6f0440 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 @@ -778,7 +778,7 @@ describe('FilterService', () => { service.init(gridStub); const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); - const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, 'text'); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); @@ -794,7 +794,7 @@ describe('FilterService', () => { service.init(gridStub); const columnFilter = { columnDef: mockColumn1, columnId: 'firstName', type: FieldType.string }; const filterCondition = service.parseFormInputFilterConditions(searchTerms, columnFilter); - const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, 'text'); + const parsedSearchTerms = getParsedSearchTermsByFieldType(filterCondition.searchTerms, FieldType.text); const columnFilters = { firstName: { ...columnFilter, operator: filterCondition.operator, searchTerms: filterCondition.searchTerms, parsedSearchTerms } }; const output = service.customLocalFilter(mockItem1, { dataView: dataViewStub, grid: gridStub, columnFilters }); diff --git a/src/app/modules/angular-slickgrid/services/__tests__/graphql.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/graphql.service.spec.ts index 569ef2f12..ac4b6ee38 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/graphql.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/graphql.service.spec.ts @@ -646,7 +646,7 @@ describe('GraphqlService', () => { { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -660,8 +660,8 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, operator: 'EQ' }, - } as ColumnFilters; + gender: { columnId: 'gender', columnDef: mockColumn, operator: 'EQ', type: FieldType.string }, + } as unknown as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); service.updateFilters(mockColumnFilters, false); @@ -675,8 +675,8 @@ describe('GraphqlService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: OperatorType.notContains }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: OperatorType.notContains, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -691,8 +691,8 @@ describe('GraphqlService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -720,7 +720,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:StartsWith, value:"fem"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['fem*'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['fem*'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -734,7 +734,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['*le'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['*le'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -748,7 +748,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: '*z' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: '*z', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -762,7 +762,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:StartsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: 'a*' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: 'a*', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -776,7 +776,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender', filter: { operator: '*z' } } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -790,7 +790,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EndsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender', filter: { operator: 'EndsWith' } } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -804,7 +804,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:StartsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'a*' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'a*', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -818,7 +818,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:StartsWith, value:"le"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'StartsWith' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'StartsWith', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -832,7 +832,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:"2"}, {field:duration, operator:LE, value:"33"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'duration', field: 'duration' } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: ['2..33'] }, + duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: ['2..33'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -846,7 +846,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:"5"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeInclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -860,7 +860,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:LE, value:"5"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeInclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -874,7 +874,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GT, value:"5"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeExclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -888,7 +888,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:LT, value:"5"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeExclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -902,7 +902,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:duration, operator:GE, value:2}, {field:duration, operator:LE, value:33}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'duration', field: 'duration' } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: [2, 33], operator: 'RangeInclusive' }, + duration: { columnId: 'duration', columnDef: mockColumn, searchTerms: [2, 33], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -916,7 +916,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:startDate, operator:GE, value:"2001-01-01"}, {field:startDate, operator:LE, value:"2001-01-31"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'startDate', field: 'startDate' } as Column; const mockColumnFilters = { - startDate: { columnId: 'startDate', columnDef: mockColumn, searchTerms: ['2001-01-01..2001-01-31'] }, + startDate: { columnId: 'startDate', columnDef: mockColumn, searchTerms: ['2001-01-01..2001-01-31'], type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -930,7 +930,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:startDate, operator:GE, value:"2001-01-01"}, {field:startDate, operator:LE, value:"2001-01-31"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'startDate', field: 'startDate' } as Column; const mockColumnFilters = { - startDate: { columnId: 'startDate', columnDef: mockColumn, searchTerms: ['2001-01-01', '2001-01-31'], operator: 'RangeInclusive' }, + startDate: { columnId: 'startDate', columnDef: mockColumn, searchTerms: ['2001-01-01', '2001-01-31'], operator: 'RangeInclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -945,8 +945,8 @@ describe('GraphqlService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20'], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20'], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -961,8 +961,8 @@ describe('GraphqlService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: [], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: [], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -976,7 +976,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:IN, value:"female,male"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'IN' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'IN', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -990,7 +990,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:NOT_IN, value:"female,male"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: OperatorType.notIn }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: OperatorType.notIn, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1004,7 +1004,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:NOT_IN, value:"female,male"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender', filter: { operator: OperatorType.notIn } } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1019,8 +1019,8 @@ describe('GraphqlService', () => { const mockColumnGender = { id: 'gender', field: 'gender', type: FieldType.string } as Column; const mockColumnAge = { id: 'age', field: 'age', type: FieldType.number } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'] }, - age: { columnId: 'age', columnDef: mockColumnAge, searchTerms: [28] }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: FieldType.string }, + age: { columnId: 'age', columnDef: mockColumnAge, searchTerms: [28], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1035,8 +1035,8 @@ describe('GraphqlService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCity = { id: 'city', field: 'city' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'] }, - city: { columnId: 'city', columnDef: mockColumnCity, searchTerms: ['Bali'] }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: FieldType.string }, + city: { columnId: 'city', columnDef: mockColumnCity, searchTerms: ['Bali'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1050,7 +1050,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EQ, value:""}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [undefined], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [undefined], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1064,7 +1064,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:isMale, operator:EQ, value:"true"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender', queryField: 'isMale' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [true], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [true], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1078,7 +1078,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:hasPriority, operator:EQ, value:"female"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', field: 'gender', queryField: 'isAfter', queryFieldFilter: 'hasPriority' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1092,7 +1092,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10, offset:0, filterBy:[{field:gender, operator:EQ, value:"female"}]) { totalCount,nodes{ id,company,gender,name } }}`; const mockColumn = { id: 'gender', name: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1106,7 +1106,7 @@ describe('GraphqlService', () => { const expectation = `query{ users(first:10,offset:0) { totalCount, nodes {id, company, gender,name} }}`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1235,7 +1235,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"0.22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1249,7 +1249,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"-22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.float } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1263,7 +1263,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"22"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.integer } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1277,7 +1277,7 @@ describe('GraphqlService', () => { const expectation = `query{users(first:10,offset:0,filterBy:[{field:duration,operator:EQ,value:"0"}]){totalCount,nodes{id,company,gender,duration,startDate}}}`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); diff --git a/src/app/modules/angular-slickgrid/services/__tests__/grid-odata.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/grid-odata.service.spec.ts index 27fad64ab..737c26d5f 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/grid-odata.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/grid-odata.service.spec.ts @@ -536,7 +536,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender eq 'female')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -550,8 +550,8 @@ describe('GridOdataService', () => { const expectation = `$top=10`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, operator: 'EQ' }, - } as ColumnFilters; + gender: { columnId: 'gender', columnDef: mockColumn, operator: 'EQ', type: FieldType.string }, + } as unknown as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); service.updateFilters(mockColumnFilters, false); @@ -565,8 +565,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: OperatorType.notContains }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: OperatorType.notContains, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -581,8 +581,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: [`abc's`], operator: OperatorType.notContains }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: [`abc's`], operator: OperatorType.notContains, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -597,8 +597,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ' }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -626,7 +626,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(startswith(Gender, 'fem'))`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['fem*'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['fem*'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -640,7 +640,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(endswith(Gender, 'le'))`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['*le'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['*le'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -654,7 +654,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(endswith(Gender, 'le'))`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: '*z' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: '*z', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -668,7 +668,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(startswith(Gender, 'le'))`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: 'a*' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le*'], operator: 'a*', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -682,7 +682,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(endswith(Gender, 'le'))`; const mockColumn = { id: 'gender', field: 'gender', filter: { operator: '*z' } } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -696,7 +696,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(startswith(Gender, 'le'))`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'a*' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['le'], operator: 'a*', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -710,7 +710,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender eq 'female' or Gender eq 'male')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'IN' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: 'IN', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -724,7 +724,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender eq 'female' or Gender eq 'ma%2Fle')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'ma/le'], operator: 'IN' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'ma/le'], operator: 'IN', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -738,7 +738,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Id eq 100 or Id eq 101)`; const mockColumn = { id: 'id', field: 'id', type: FieldType.number } as Column; const mockColumnFilters = { - gender: { columnId: 'id', columnDef: mockColumn, searchTerms: [100, 101], operator: 'IN' }, + gender: { columnId: 'id', columnDef: mockColumn, searchTerms: [100, 101], operator: 'IN', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -752,7 +752,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender ne 'female' and Gender ne 'male')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: OperatorType.notIn }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], operator: OperatorType.notIn, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -766,7 +766,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender ne 'female' and Gender ne 'ma%2Fle')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'ma/le'], operator: OperatorType.notIn }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'ma/le'], operator: OperatorType.notIn, type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -780,7 +780,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender ne 'female' and Gender ne 'male')`; const mockColumn = { id: 'gender', field: 'gender', filter: { operator: OperatorType.notIn } } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'] }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female', 'male'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -795,8 +795,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender', type: FieldType.string } as Column; const mockColumnAge = { id: 'age', field: 'age', type: FieldType.number } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'] }, - age: { columnId: 'age', columnDef: mockColumnAge, searchTerms: [28] }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: FieldType.string }, + age: { columnId: 'age', columnDef: mockColumnAge, searchTerms: [28], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -811,8 +811,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCity = { id: 'city', field: 'city' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'] }, - city: { columnId: 'city', columnDef: mockColumnCity, searchTerms: ['Bali'] }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['le'], type: FieldType.string }, + city: { columnId: 'city', columnDef: mockColumnCity, searchTerms: ['Bali'], type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -826,7 +826,7 @@ describe('GridOdataService', () => { const expectation = `$top=10`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [undefined], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [undefined], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -840,7 +840,7 @@ describe('GridOdataService', () => { const expectation = `$top=10`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [''], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [''], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -854,7 +854,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(IsMale eq true)`; const mockColumn = { id: 'gender', field: 'gender', type: FieldType.boolean, queryField: 'isMale' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [true], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: [true], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -868,7 +868,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(HasPriority eq 'female')`; const mockColumn = { id: 'gender', field: 'gender', queryField: 'isAfter', queryFieldFilter: 'hasPriority' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -882,7 +882,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Gender eq 'female')`; const mockColumn = { id: 'gender', name: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -897,8 +897,8 @@ describe('GridOdataService', () => { const mockColumnGender = { id: 'gender', field: 'gender' } as Column; const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', bypassBackendQuery: true }, - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, + gender: { columnId: 'gender', columnDef: mockColumnGender, searchTerms: ['female'], operator: 'EQ', bypassBackendQuery: true, type: FieldType.string }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -913,8 +913,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -928,7 +928,7 @@ describe('GridOdataService', () => { const expectation = `$top=10`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -946,8 +946,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -961,7 +961,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Duration ge 5)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeInclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -975,7 +975,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Duration le 5)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeInclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -989,7 +989,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Duration gt 5)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeExclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..'], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1003,7 +1003,7 @@ describe('GridOdataService', () => { const expectation = `$top=10&$filter=(Duration lt 5)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeExclusive' }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['..5'], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1018,8 +1018,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1034,8 +1034,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20..2001-02-28'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20..2001-02-28'], operator: 'RangeInclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1050,8 +1050,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20', '2001-02-28'], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20', '2001-02-28'], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1070,7 +1070,7 @@ describe('GridOdataService', () => { const expectation = `$filter=(Gender eq 'female')`; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1084,7 +1084,7 @@ describe('GridOdataService', () => { const expectation = ''; const mockColumn = { id: 'gender', field: 'gender' } as Column; const mockColumnFilters = { - gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ' }, + gender: { columnId: 'gender', columnDef: mockColumn, searchTerms: ['female'], operator: 'EQ', type: FieldType.string }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1102,8 +1102,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1126,8 +1126,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1142,8 +1142,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1158,8 +1158,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1174,8 +1174,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration' } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [5, 22], operator: 'RangeExclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1190,8 +1190,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20..2001-02-28'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20..2001-02-28'], operator: 'RangeInclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1206,8 +1206,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20', '2001-02-28'], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20', '2001-02-28'], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1222,8 +1222,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20'], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-01-20'], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1238,8 +1238,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: [], operator: 'RangeExclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: [], operator: 'RangeExclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1254,8 +1254,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: [], operator: 'RangeInclusive', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1275,8 +1275,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnUpdated = { id: 'updatedDate', field: 'updatedDate', type: FieldType.date } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + updatedDate: { columnId: 'updatedDate', columnDef: mockColumnUpdated, searchTerms: ['2001-02-28'], operator: 'EQ', type: FieldType.dateIso }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1291,8 +1291,8 @@ describe('GridOdataService', () => { const mockColumnCompany = { id: 'company', field: 'company' } as Column; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains' }, - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive' }, + company: { columnId: 'company', columnDef: mockColumnCompany, searchTerms: ['abc'], operator: 'Contains', type: FieldType.string }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['5..22'], operator: 'RangeInclusive', type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1600,7 +1600,7 @@ describe('GridOdataService', () => { const expectation = `$filter=(Duration eq 0.22)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['.22'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1614,7 +1614,7 @@ describe('GridOdataService', () => { const expectation = `$filter=(Duration eq -22)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.number } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-2a2'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1628,7 +1628,7 @@ describe('GridOdataService', () => { const expectation = `$filter=(Duration eq 22)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.integer } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['22;'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub); @@ -1642,7 +1642,7 @@ describe('GridOdataService', () => { const expectation = `$filter=(Duration eq 0)`; const mockColumnDuration = { id: 'duration', field: 'duration', type: FieldType.float } as Column; const mockColumnFilters = { - duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'] }, + duration: { columnId: 'duration', columnDef: mockColumnDuration, searchTerms: ['-'], type: FieldType.number }, } as ColumnFilters; service.init(serviceOptions, paginationOptions, gridStub);