Skip to content

Commit

Permalink
feat(tests): add Sorters full test suite (#164)
Browse files Browse the repository at this point in the history
* feat(tests): add Sorters full test suite

* feat(test): add CollectionService unit tests

* feat(test): add CollectionService unit tests

* refactor(build): add missing TSlint types

* feat(tests): add extra Collection Service option and tests

* refactor(circleci): try orb to upload codecov

* refactor(circleci): rollback last commit

* refactor(tests): remove unnecessary tests code and move to test folder
  • Loading branch information
ghiscoding committed May 15, 2019
1 parent 5c6d028 commit d375939
Show file tree
Hide file tree
Showing 19 changed files with 587 additions and 46 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"@types/text-encoding-utf-8": "^1.0.1",
"@types/webpack": "^3.0.4",
"aurelia-animator-css": "^1.0.2",
"aurelia-bootstrapper": "^2.1.1",
"aurelia-bootstrapper": "^2.3.3",
"aurelia-cli": "^0.31.3",
"aurelia-fetch-client": "^1.1.3",
"aurelia-http-client": "^1.2.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const arrayObjectToCsvFormatter: Formatter = (row: number, cell: number,
// we will 1st get the object from the dataContext, then
if (Array.isArray(dataContext[parentObjectKeyName]) && dataContext[parentObjectKeyName].length > 0) {
const outputStrings: string[] = [];
dataContext[parentObjectKeyName].forEach((data) => {
dataContext[parentObjectKeyName].forEach((data: any) => {
const strings: string[] = [];

// 2nd from that data loop through all propertyNames we want to use (e.g.: ['firstName', 'lastName'])
Expand Down
2 changes: 1 addition & 1 deletion src/aurelia-slickgrid/formatters/maskFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const maskFormatter: Formatter = (row: number, cell: number, value: any,
if (value && mask) {
let i = 0;
const v = value.toString();
return mask.replace(/[09A]/gi, (match) => {
return mask.replace(/[09A]/gi, (match: string) => {
// only replace the char when the mask is a 0 or 9 for a digit OR the mask is "A" and the char is a non-digit meaning a string char
if (
((match === '0' || match === '9') && /\d*/g.test(v[i])) // mask is 0 or 9 and value is a digit
Expand Down
4 changes: 2 additions & 2 deletions src/aurelia-slickgrid/models/collectionOption.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export interface CollectionOption {
/**
* Defaults to "chain", when using multiple "collectionFilterBy", do we want to "merge" or "chain" the result after each pass?
* For example if we have 2 filters to pass by, and we start with pass 1 returning 7 items and last pass returning 5 items
* "chain" is the default and will return 5 items, since the result of each pass is sent used by the next pass
* "merge" would return the merge of the 7 items & 5 items (without duplicates), since some item might be the same the result is anywhere between 5 to 13 items
* "chain" is the default and will return 5 items, since the result of each pass will be used by the next pass
* "merge" would return the merge of the 7 items and the 5 items (without duplicates), since some items might be the same, the result is anywhere between 5 to 13 items
*/
filterResultAfterEachPass?: FilterMultiplePassType | FilterMultiplePassTypeString;

Expand Down
2 changes: 1 addition & 1 deletion src/aurelia-slickgrid/services/backend-utilities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

import { GraphqlResult } from '../models/graphqlResult.interface';
import { BackendServiceApi } from '../models/backendServiceApi.interface';
import { GridOption } from '../models';
import { GridOption } from '../models/index';

/** Execute the Backend Processes Callback, that could come from an Observable or a Promise callback */
export function executeBackendProcessesCallback(startTime: Date, processResult: GraphqlResult | any, backendApi: BackendServiceApi, gridOptions: GridOption): GraphqlResult | any {
Expand Down
272 changes: 272 additions & 0 deletions src/aurelia-slickgrid/services/collection.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
import { I18N } from 'aurelia-i18n';
import { EventAggregator } from 'aurelia-event-aggregator';
import { BindingSignaler } from 'aurelia-templating-resources';
import { CollectionService } from './collection.service';
import {
CollectionFilterBy,
CollectionSortBy,
FilterMultiplePassType,
OperatorType,
Column,
FieldType,
} from './../models/index';

describe('CollectionService', () => {
let collection = [];
let i18n: I18N;
let service: CollectionService;

// stub some methods of the SlickGrid Grid instance
const gridStub = {
getOptions: jest.fn()
};

beforeEach(() => {
i18n = new I18N(new EventAggregator(), new BindingSignaler());
i18n.setup({
resources: {
en: {
translation: {
SALES_REP: 'Sales Rep.',
FINANCE_MANAGER: 'Finance Manager',
HUMAN_RESOURCES: 'Human Resources',
IT_ADMIN: 'IT Admin',
DEVELOPER: 'Developer',
}
},
fr: {
translation:
{
SALES_REP: 'Représentant des ventes',
FINANCE_MANAGER: 'Responsable des finances',
HUMAN_RESOURCES: 'Ressources humaines',
IT_ADMIN: 'Administrateur IT',
DEVELOPER: 'Développeur',
}
}
},
lng: '0',
fallbackLng: 'en',
debug: false
});
service = new CollectionService(i18n);

collection = [
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
];
});

afterEach(() => {
collection = undefined;
});

it('should create the service', () => {
expect(service).toBeTruthy();
});

it('should return an array without certain filtered values', () => {
const filterBy = { property: 'firstName', operator: 'NE', value: 'John' } as CollectionFilterBy;

const result = service.filterCollection(collection, filterBy);

expect(result).toEqual([
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
]);
});

it('should return an array without certain filtered valuess in a "chain" way', () => {
const filterBy = [
{ property: 'firstName', operator: 'NE', value: 'John' },
{ property: 'lastName', operator: 'NE', value: 'Doe' }
] as CollectionFilterBy[];

const result1 = service.filterCollection(collection, filterBy);
const result2 = service.filterCollection(collection, filterBy, 'chain'); // chain is default

expect(result1).toEqual([
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
]);
expect(result1).toEqual(result2);
});

it('should return an array with merged output of filtered values', () => {
const filterBy = [
{ property: 'firstName', operator: OperatorType.equal, value: 'John' },
{ property: 'lastName', value: 'Doe' } // ommitted operator are Equal by default
] as CollectionFilterBy[];

const result = service.filterCollection(collection, filterBy, FilterMultiplePassType.merge);

expect(result).toEqual([
// the array will have all "John" 1st, then all "Doe"
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
]);
});

it('should return an array by using the "contains" filter type', () => {
const filterBy = { property: 'firstName', operator: OperatorType.contains, value: 'Foo' } as CollectionFilterBy;

const result = service.singleFilterCollection(collection, filterBy);

expect(result).toEqual([{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 }]);
});

it('should return an array by using the "notContains" filter type', () => {
const filterBy = { property: 'firstName', operator: OperatorType.notContains, value: 'John' } as CollectionFilterBy;

const result = service.singleFilterCollection(collection, filterBy);

expect(result).toEqual([
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
]);
});

it('should return a collection sorted by a "dataKey"', () => {
const columnDef = { id: 'users', field: 'users', dataKey: 'lastName' } as Column;

const result = service.sortCollection(columnDef, collection, { property: 'lastName', sortDesc: true, fieldType: FieldType.string });

expect(result).toEqual([
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
]);
});

it('should return a collection sorted by multiple sortBy entities', () => {
const columnDef = { id: 'users', field: 'users', dataKey: 'lastName' } as Column;
const sortBy = [
{ property: 'firstName', sortDesc: false, fieldType: FieldType.string },
{ property: 'lastName', sortDesc: true, fieldType: FieldType.string },
] as CollectionSortBy[];

const result = service.sortCollection(columnDef, collection, sortBy);

expect(result).toEqual([
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
]);
});

it('should return a collection sorted by a sortyBy entity being a number', () => {
const columnDef = { id: 'users', field: 'users', dataKey: 'lastName' } as Column;
const sortBy = [
{ property: 'order', sortDesc: true, fieldType: FieldType.number },
] as CollectionSortBy[];

const result = service.sortCollection(columnDef, collection, sortBy);

expect(result).toEqual([
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
]);
});

it('should return a collection sorted by multiple sortBy entities and their translated value', () => {
i18n.setLocale('fr');
const columnDef = { id: 'users', field: 'users', dataKey: 'lastName' } as Column;
const sortBy = [
{ property: 'firstName', sortDesc: false, fieldType: FieldType.string },
{ property: 'position', sortDesc: true }, // fieldType is string by default
] as CollectionSortBy[];

const result = service.sortCollection(columnDef, collection, sortBy, true);

expect(result).toEqual([
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
]);
});

it('should return a collection sorted by a single sortBy entity and their translated value', () => {
i18n.setLocale('en');
const columnDef = { id: 'users', field: 'users' } as Column;
const sortBy = { property: 'position', sortDesc: false } as CollectionSortBy; // fieldType is string by default

const result = service.sortCollection(columnDef, collection, sortBy, true);

expect(result).toEqual([
{ firstName: 'John', lastName: 'Doe', position: null, order: 5 },
{ firstName: 'John', lastName: 'Doe', position: 'DEVELOPER', order: 4 },
{ firstName: 'Jane', lastName: 'Doe', position: 'FINANCE_MANAGER', order: 1 },
{ firstName: 'Ava Luna', lastName: null, position: 'HUMAN_RESOURCES', order: 13 },
{ firstName: 'John', lastName: 'Z', position: 'SALES_REP', order: 10 },
{ firstName: '', lastName: 'Cash', position: 'SALES_REP', order: 3 },
{ firstName: 'Bob', lastName: 'Cash', position: 'SALES_REP', order: 0 },
{ firstName: 'John', lastName: 'Zachary', position: 'SALES_REP', order: 2 },
{ firstName: 'John Foo', lastName: 'Bar', position: 'SALES_REP', order: 8 },
]);
});

it('should return a collection of numbers sorted', () => {
i18n.setLocale('en');
const columnDef = { id: 'count', field: 'count', fieldType: FieldType.number } as Column;

const result1 = service.sortCollection(columnDef, [0, -11, 3, 99999, -200], { property: '', sortDesc: false } as CollectionSortBy);
const result2 = service.sortCollection(columnDef, [0, -11, 3, 99999, -200], { property: '', sortDesc: true } as CollectionSortBy);

expect(result1).toEqual([-200, -11, 0, 3, 99999]);
expect(result2).toEqual([99999, 3, 0, -11, -200]);
});

it('should return a collection of translation values sorted', () => {
i18n.setLocale('en');
const roleCollection = ['SALES_REP', 'DEVELOPER', 'SALES_REP', null, 'HUMAN_RESOURCES', 'FINANCE_MANAGER', 'UNKNOWN'];
const columnDef = { id: 'count', field: 'count', fieldType: FieldType.string } as Column;

const result1 = service.sortCollection(columnDef, [...roleCollection], { property: '', sortDesc: false } as CollectionSortBy, true);
const result2 = service.sortCollection(columnDef, [...roleCollection], { property: '', sortDesc: true } as CollectionSortBy, true);

expect(result1).toEqual([null, 'DEVELOPER', 'FINANCE_MANAGER', 'HUMAN_RESOURCES', 'SALES_REP', 'SALES_REP', 'UNKNOWN']);
expect(result2).toEqual(['UNKNOWN', 'SALES_REP', 'SALES_REP', 'HUMAN_RESOURCES', 'FINANCE_MANAGER', 'DEVELOPER', null]);
});
});
27 changes: 19 additions & 8 deletions src/aurelia-slickgrid/services/collection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export class CollectionService {
singleFilterCollection(collection: any[], filterBy: CollectionFilterBy): any[] {
let filteredCollection: any[] = [];

if (filterBy) {
const property = filterBy.property || '';
if (filterBy && filterBy.property) {
const property = filterBy.property;
const operator = filterBy.operator || OperatorType.equal;
// just check for undefined since the filter value could be null, 0, '', false etc
const value = typeof filterBy.value === 'undefined' ? '' : filterBy.value;
Expand All @@ -74,7 +74,6 @@ export class CollectionService {
filteredCollection = collection.filter((item) => item[property] !== value);
}
}

return filteredCollection;
}

Expand All @@ -95,9 +94,9 @@ export class CollectionService {
for (let i = 0, l = sortByOptions.length; i < l; i++) {
const sortBy = sortByOptions[i];

if (sortBy) {
if (sortBy && sortBy.property) {
const sortDirection = sortBy.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc;
const propertyName = sortBy.property || '';
const propertyName = sortBy.property;
const fieldType = sortBy.fieldType || FieldType.string;
const value1 = (enableTranslateLabel) ? this.i18n.tr(dataRow1[propertyName] || ' ') : dataRow1[propertyName];
const value2 = (enableTranslateLabel) ? this.i18n.tr(dataRow2[propertyName] || ' ') : dataRow2[propertyName];
Expand All @@ -110,9 +109,9 @@ export class CollectionService {
}
return SortDirectionNumber.neutral;
});
} else {
} else if (sortByOptions && sortByOptions.property) {
// single sort
const propertyName = sortByOptions.property || '';
const propertyName = sortByOptions.property;
const sortDirection = sortByOptions.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc;
const fieldType = sortByOptions.fieldType || FieldType.string;

Expand All @@ -125,9 +124,21 @@ export class CollectionService {
}
return SortDirectionNumber.neutral;
});
} else if (sortByOptions && !sortByOptions.property) {
const sortDirection = sortByOptions.sortDesc ? SortDirectionNumber.desc : SortDirectionNumber.asc;
const fieldType = sortByOptions.fieldType || FieldType.string;

sortedCollection = collection.sort((dataRow1: any, dataRow2: any) => {
const value1 = (enableTranslateLabel) ? this.i18n.tr(dataRow1 || ' ') : dataRow1;
const value2 = (enableTranslateLabel) ? this.i18n.tr(dataRow2 || ' ') : dataRow2;
const sortResult = sortByFieldType(value1, value2, fieldType, sortDirection, columnDef);
if (sortResult !== SortDirectionNumber.neutral) {
return sortResult;
}
return SortDirectionNumber.neutral;
});
}
}

return sortedCollection;
}
}
2 changes: 1 addition & 1 deletion src/aurelia-slickgrid/services/resizer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class ResizerService {
});
}

resizeGridCallback(newSizes: GridDimension) {
resizeGridCallback(newSizes: GridDimension | undefined) {
const lastDimensions = this.resizeGridWithDimensions(newSizes);
this.ea.publish(`${this.aureliaEventPrefix}:onAfterResize`, lastDimensions);
return lastDimensions;
Expand Down
Loading

0 comments on commit d375939

Please sign in to comment.