Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit c3abe0f

Browse files
authored
Merge pull request #275 from ghiscoding/feat/filters-tests
feat(filter): add unit test suite for multiple Filters
2 parents 1fd11ab + 46787af commit c3abe0f

29 files changed

+1530
-161
lines changed

.circleci/config.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ jobs:
4747
command: |
4848
cd test/cypress
4949
yarn cypress:ci:mochawesome
50-
- store_test_results:
51-
path: reports/junit
52-
- store_artifacts:
53-
path: reports/junit
50+
# - run:
51+
# name: Build Demo Site
52+
# command: NODE_ENV=production npx ng build --prod
53+
# - store_test_results:
54+
# path: reports/junit
55+
# - store_artifacts:
56+
# path: reports/junit

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Thumbs.db
4444

4545
# Distribution folder
4646
dist
47+
dist-demo
4748

4849
# Tests Report & Coverage
4950
**/node_modules

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
### [2.10.4](https://github.com/ghiscoding/angular-slickgrid/compare/v2.10.3...v2.10.4) (2019-08-29)
6+
7+
### [2.10.3](https://github.com/ghiscoding/angular-slickgrid/compare/v2.10.2...v2.10.3) (2019-08-29)
8+
9+
10+
### Bug Fixes
11+
12+
* **core:** dowgrade to previous SlickGrid version to fix issues ([1fd11ab](https://github.com/ghiscoding/angular-slickgrid/commit/1fd11ab))
13+
14+
15+
### Features
16+
17+
* **filter:** add CompoundDate Filter unit tests ([45348a0](https://github.com/ghiscoding/angular-slickgrid/commit/45348a0))
18+
* **filter:** add CompoundSlider Filter unit tests ([06abb3d](https://github.com/ghiscoding/angular-slickgrid/commit/06abb3d))
19+
* **filter:** add NativeSelect Filter unit tests & tweak some Filters ([4b952c9](https://github.com/ghiscoding/angular-slickgrid/commit/4b952c9))
20+
* **filter:** add slider unit tests ([5ae1a85](https://github.com/ghiscoding/angular-slickgrid/commit/5ae1a85))
21+
522
### [2.10.2](https://github.com/ghiscoding/angular-slickgrid/compare/v2.10.1...v2.10.2) (2019-08-26)
623

724

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular-slickgrid",
3-
"version": "2.10.2",
3+
"version": "2.10.4",
44
"description": "Slickgrid components made available in Angular",
55
"keywords": [
66
"angular",

src/app/examples/custom-angularComponentFilter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class CustomAngularComponentFilter implements Filter {
6262
this.grid = args.grid;
6363
this.callback = args.callback;
6464
this.columnDef = args.columnDef;
65-
this.searchTerms = args.searchTerms || [];
65+
this.searchTerms = (args.hasOwnProperty('searchTerms') ? args.searchTerms : []) || [];
6666

6767
if (!this.columnFilter || !this.columnFilter.params.component || !(this.angularUtilService instanceof AngularUtilService)) {
6868
throw new Error(`[Angular-Slickgrid] For Filter with Angular Component to work properly, you need to provide your component to the "component" property and make sure to add it to your "entryComponents" array.

src/app/examples/custom-inputFilter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ export class CustomInputFilter implements Filter {
4242
this.grid = args.grid;
4343
this.callback = args.callback;
4444
this.columnDef = args.columnDef;
45-
this.searchTerms = args.searchTerms || [];
45+
this.searchTerms = (args.hasOwnProperty('searchTerms') ? args.searchTerms : []) || [];
4646

4747
// filter input can only have 1 search term, so we will use the 1st array index if it exist
48-
const searchTerm = (Array.isArray(this.searchTerms) && this.searchTerms[0]) || '';
48+
const searchTerm = (Array.isArray(this.searchTerms) && this.searchTerms.length >= 0) ? this.searchTerms[0] : '';
4949

5050
// step 1, create HTML string template
5151
const filterTemplate = this.buildTemplateHtmlString();

src/app/examples/grid-menu.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class GridMenuComponent implements OnInit {
4040
{ id: 'title', name: 'Title', field: 'title', headerKey: 'TITLE', filterable: true, type: FieldType.string },
4141
{ id: 'duration', name: 'Duration', field: 'duration', headerKey: 'DURATION', sortable: true, filterable: true, type: FieldType.string },
4242
{
43-
id: '%', name: '% Complete', field: 'percentComplete', headerKey: 'PERCENT_COMPLETE', sortable: true, filterable: true,
43+
id: 'percentComplete', name: '% Complete', field: 'percentComplete', headerKey: 'PERCENT_COMPLETE', sortable: true, filterable: true,
4444
type: FieldType.number,
4545
formatter: Formatters.percentCompleteBar,
4646
filter: { model: Filters.compoundSlider, params: { hideSliderNumber: false } }
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
2+
import { TestBed } from '@angular/core/testing';
3+
import { TranslateService, TranslateModule } from '@ngx-translate/core';
4+
import { Column, FilterArguments, GridOption, FieldType } from '../../models';
5+
import { Filters } from '..';
6+
import { CompoundDateFilter } from '../compoundDateFilter';
7+
8+
const containerId = 'demo-container';
9+
10+
// define a <div> container to simulate the grid container
11+
const template = `<div id="${containerId}"></div>`;
12+
13+
const gridOptionMock = {
14+
enableFiltering: true,
15+
enableFilterTrimWhiteSpace: true,
16+
} as GridOption;
17+
18+
const gridStub = {
19+
getOptions: () => gridOptionMock,
20+
getColumns: jest.fn(),
21+
getHeaderRowColumn: jest.fn(),
22+
render: jest.fn(),
23+
};
24+
25+
describe('CompoundDateFilter', () => {
26+
let divContainer: HTMLDivElement;
27+
let filter: CompoundDateFilter;
28+
let filterArguments: FilterArguments;
29+
let spyGetHeaderRow;
30+
let mockColumn: Column;
31+
let translate: TranslateService;
32+
33+
beforeEach(() => {
34+
divContainer = document.createElement('div');
35+
divContainer.innerHTML = template;
36+
document.body.appendChild(divContainer);
37+
spyGetHeaderRow = jest.spyOn(gridStub, 'getHeaderRowColumn').mockReturnValue(divContainer);
38+
39+
mockColumn = { id: 'finish', field: 'finish', filterable: true, outputType: FieldType.dateIso, filter: { model: Filters.compoundDate, operator: '>' } };
40+
filterArguments = {
41+
grid: gridStub,
42+
columnDef: mockColumn,
43+
callback: jest.fn()
44+
};
45+
46+
TestBed.configureTestingModule({
47+
providers: [],
48+
imports: [TranslateModule.forRoot()]
49+
});
50+
translate = TestBed.get(TranslateService);
51+
52+
translate.setTranslation('en', {
53+
CONTAINS: 'Contains',
54+
EQUALS: 'Equals',
55+
ENDS_WITH: 'Ends With',
56+
STARTS_WITH: 'Starts With',
57+
});
58+
translate.setTranslation('fr', {
59+
CONTAINS: 'Contient',
60+
EQUALS: 'Égale',
61+
ENDS_WITH: 'Se termine par',
62+
STARTS_WITH: 'Commence par',
63+
});
64+
translate.setDefaultLang('en');
65+
66+
filter = new CompoundDateFilter(translate);
67+
});
68+
69+
afterEach(() => {
70+
filter.destroy();
71+
});
72+
73+
it('should throw an error when trying to call init without any arguments', () => {
74+
expect(() => filter.init(null)).toThrowError('[Angular-SlickGrid] A filter must always have an "init()" with valid arguments.');
75+
});
76+
77+
it('should initialize the filter', () => {
78+
filter.init(filterArguments);
79+
const filterCount = divContainer.querySelectorAll('.form-group.search-filter.filter-finish').length;
80+
81+
expect(spyGetHeaderRow).toHaveBeenCalled();
82+
expect(filterCount).toBe(1);
83+
});
84+
85+
it('should have a placeholder when defined in its column definition', () => {
86+
const testValue = 'test placeholder';
87+
mockColumn.filter.placeholder = testValue;
88+
89+
filter.init(filterArguments);
90+
const filterElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
91+
92+
expect(filterElm.placeholder).toBe(testValue);
93+
});
94+
95+
it('should be able to retrieve default flatpickr options through the Getter', () => {
96+
filter.init(filterArguments);
97+
98+
expect(filter.flatInstance).toBeTruthy();
99+
expect(filter.flatpickrOptions).toEqual({
100+
altFormat: 'Y-m-d',
101+
altInput: true,
102+
closeOnSelect: true,
103+
dateFormat: 'Y-m-d',
104+
defaultDate: '',
105+
locale: 'en',
106+
onChange: expect.anything(),
107+
wrap: true,
108+
});
109+
});
110+
111+
it('should be able to call "setValues" and have that value set in the picker', () => {
112+
const mockDate = '2001-01-02T16:02:02.239Z';
113+
filter.init(filterArguments);
114+
filter.setValues(mockDate);
115+
expect(filter.currentDate).toEqual(mockDate);
116+
});
117+
118+
it('should be able to call "setValues" as an array and have that value set in the picker', () => {
119+
const mockDate = '2001-01-02T16:02:02.239Z';
120+
filter.init(filterArguments);
121+
filter.setValues([mockDate]);
122+
expect(filter.currentDate).toEqual(mockDate);
123+
});
124+
125+
it('should trigger input change event and expect the callback to be called with the date provided in the input', () => {
126+
mockColumn.filter.filterOptions = { allowInput: true }; // change to allow input value only for testing purposes
127+
mockColumn.filter.operator = '>';
128+
const spyCallback = jest.spyOn(filterArguments, 'callback');
129+
130+
filter.init(filterArguments);
131+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
132+
filterInputElm.value = '2001-01-02T16:02:02.239Z';
133+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));
134+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
135+
136+
expect(filterFilledElms.length).toBe(1);
137+
expect(spyCallback).toHaveBeenCalledWith(undefined, {
138+
columnDef: mockColumn, operator: '>', searchTerms: ['2001-01-02'], shouldTriggerQuery: true
139+
});
140+
});
141+
142+
it('should pass a different operator then trigger an input change event and expect the callback to be called with the date provided in the input', () => {
143+
mockColumn.filter.filterOptions = { allowInput: true }; // change to allow input value only for testing purposes
144+
mockColumn.filter.operator = '>';
145+
const spyCallback = jest.spyOn(filterArguments, 'callback');
146+
147+
filter.init(filterArguments);
148+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
149+
filterInputElm.value = '2001-01-02T16:02:02.239Z';
150+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));
151+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
152+
153+
expect(filterFilledElms.length).toBe(1);
154+
expect(spyCallback).toHaveBeenCalledWith(undefined, { columnDef: mockColumn, operator: '>', searchTerms: ['2001-01-02'], shouldTriggerQuery: true });
155+
});
156+
157+
it('should create the input filter with a default search term when passed as a filter argument', () => {
158+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
159+
mockColumn.filter.operator = '<=';
160+
const spyCallback = jest.spyOn(filterArguments, 'callback');
161+
162+
filter.init(filterArguments);
163+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
164+
165+
filterInputElm.focus();
166+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keyup', { keyCode: 97, bubbles: true, cancelable: true }));
167+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
168+
169+
expect(filterFilledElms.length).toBe(1);
170+
expect(filter.currentDate).toBe('2000-01-01T05:00:00.000Z');
171+
expect(filterInputElm.value).toBe('2000-01-01');
172+
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['2000-01-01T05:00:00.000Z'], shouldTriggerQuery: true });
173+
});
174+
175+
it('should trigger an operator change event and expect the callback to be called with the searchTerms and operator defined', () => {
176+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
177+
mockColumn.filter.operator = '>';
178+
const spyCallback = jest.spyOn(filterArguments, 'callback');
179+
180+
filter.init(filterArguments);
181+
const filterSelectElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish select');
182+
183+
filterSelectElm.value = '<=';
184+
filterSelectElm.dispatchEvent(new Event('change'));
185+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
186+
187+
expect(filterFilledElms.length).toBe(1);
188+
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['2000-01-01T05:00:00.000Z'], shouldTriggerQuery: true });
189+
});
190+
191+
it('should work with different locale when locale is changed', () => {
192+
translate.use('fr-CA');
193+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
194+
mockColumn.filter.operator = '<=';
195+
const spyCallback = jest.spyOn(filterArguments, 'callback');
196+
197+
filter.init(filterArguments);
198+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
199+
200+
filterInputElm.focus();
201+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keyup', { keyCode: 97, bubbles: true, cancelable: true }));
202+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
203+
204+
expect(filterFilledElms.length).toBe(1);
205+
expect(filter.currentDate).toBe('2000-01-01T05:00:00.000Z');
206+
expect(filterInputElm.value).toBe('2000-01-01');
207+
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['2000-01-01T05:00:00.000Z'], shouldTriggerQuery: true });
208+
});
209+
210+
it('should trigger a callback with the clear filter set when calling the "clear" method', () => {
211+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
212+
const spyCallback = jest.spyOn(filterArguments, 'callback');
213+
214+
filter.init(filterArguments);
215+
filter.clear();
216+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
217+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
218+
219+
expect(filterInputElm.value).toBe('');
220+
expect(filterFilledElms.length).toBe(0);
221+
expect(spyCallback).toHaveBeenCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, shouldTriggerQuery: true });
222+
});
223+
224+
it('should trigger a callback with the clear filter but without querying when when calling the "clear" method with False as argument', () => {
225+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
226+
const spyCallback = jest.spyOn(filterArguments, 'callback');
227+
228+
filter.init(filterArguments);
229+
filter.clear(false);
230+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
231+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
232+
233+
expect(filterInputElm.value).toBe('');
234+
expect(filterFilledElms.length).toBe(0);
235+
expect(spyCallback).toHaveBeenCalledWith(undefined, { columnDef: mockColumn, clearFilterTriggered: true, shouldTriggerQuery: false });
236+
});
237+
238+
it('should have a value with date & time in the picker when "enableTime" option is set and we trigger a change', () => {
239+
mockColumn.filter.filterOptions = { enableTime: true, allowInput: true }; // change to allow input value only for testing purposes
240+
mockColumn.outputType = FieldType.dateTimeIsoAmPm;
241+
mockColumn.filter.operator = '>';
242+
const spyCallback = jest.spyOn(filterArguments, 'callback');
243+
244+
filter.init(filterArguments);
245+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
246+
filterInputElm.value = '2001-01-02T16:02:02.000+05:00';
247+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keydown', { keyCode: 13, bubbles: true, cancelable: true }));
248+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
249+
250+
expect(filterFilledElms.length).toBe(1);
251+
// expect(filter.currentDate.toISOString()).toBe('2001-01-02T21:02:02.000Z');
252+
expect(filterInputElm.value).toBe('2001-01-02 4:02:02 PM');
253+
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), {
254+
columnDef: mockColumn, operator: '>', searchTerms: ['2001-01-02'], shouldTriggerQuery: true
255+
});
256+
});
257+
258+
it('should have a value with date & time in the picker when using no "outputType" which will default to UTC date', () => {
259+
mockColumn.outputType = null;
260+
filterArguments.searchTerms = ['2000-01-01T05:00:00.000Z'];
261+
mockColumn.filter.operator = '<=';
262+
const spyCallback = jest.spyOn(filterArguments, 'callback');
263+
264+
filter.init(filterArguments);
265+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-finish .flatpickr input.flatpickr');
266+
267+
filterInputElm.focus();
268+
filterInputElm.dispatchEvent(new (window.window as any).KeyboardEvent('keyup', { keyCode: 97, bubbles: true, cancelable: true }));
269+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.form-group.search-filter.filter-finish.filled');
270+
271+
expect(filterFilledElms.length).toBe(1);
272+
expect(filter.currentDate).toBe('2000-01-01T05:00:00.000Z');
273+
expect(filterInputElm.value).toBe('2000-01-01T05:00:00.000Z');
274+
expect(spyCallback).toHaveBeenCalledWith(expect.anything(), { columnDef: mockColumn, operator: '<=', searchTerms: ['2000-01-01T05:00:00.000Z'], shouldTriggerQuery: true });
275+
});
276+
});

src/app/modules/angular-slickgrid/filters/__tests__/compoundInputFilter.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,17 @@ describe('CompoundInputFilter', () => {
190190
expect(filterInputElm.value).toBe('xyz');
191191
});
192192

193+
it('should expect the input not to have the "filled" css class when the search term provided is an empty string', () => {
194+
filterArguments.searchTerms = [''];
195+
196+
filter.init(filterArguments);
197+
const filterInputElm = divContainer.querySelector<HTMLInputElement>('.search-filter.filter-duration input');
198+
const filterFilledElms = divContainer.querySelectorAll<HTMLInputElement>('.search-filter.filter-duration.filled');
199+
200+
expect(filterInputElm.value).toBe('');
201+
expect(filterFilledElms.length).toBe(0);
202+
});
203+
193204
it('should create the input filter with operator dropdown options related to numbers when column definition type is FieldType.number', () => {
194205
mockColumn.type = FieldType.number;
195206
filterArguments.searchTerms = ['9'];

0 commit comments

Comments
 (0)