Skip to content

Commit

Permalink
Merge pull request #32 from SquirrelCorporation/chores-add-tests
Browse files Browse the repository at this point in the history
Add tests and enhancements for helper functions
  • Loading branch information
SquirrelDeveloper authored Jun 18, 2024
2 parents 89b7eb8 + a923255 commit e853eb0
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 13 deletions.
1 change: 1 addition & 0 deletions server/src/helpers/PaginationHelper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Pagination logic
export function paginate<T>(data: T[], current = 1, pageSize = 10): T[] {
if (!data) return [];
return [...data].slice(
((current as number) - 1) * (pageSize as number),
(current as number) * (pageSize as number),
Expand Down
37 changes: 24 additions & 13 deletions server/src/helpers/SorterHelper.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import logger from '../logger';

export function sortByFields<T>(data: T[], params: any) {
if (params.sorter) {
const sorter = JSON.parse(params.sorter);
let sorter: Record<keyof T, string> | null = null;
try {
sorter = params.sorter ? JSON.parse(params.sorter) : null;
} catch (error) {
logger.error('Could not parse sorter parameter');
return data;
}

if (sorter) {
return data.sort((prev, next) => {
let sortNumber = 0;
(Object.keys(sorter) as Array<keyof T>).forEach((key) => {
const nextSort = next[key] as string;
const preSort = prev[key] as string;
if (sorter[key] === 'descend') {
if (typeof prev[key] === 'string' && typeof next[key] === 'string') {
const nextSort = next[key] as string;
const preSort = prev[key] as string;
if (sorter[key] === 'descend') {
if (preSort.localeCompare(nextSort) > 0) {
sortNumber += -1;
} else {
sortNumber += 1;
}
return;
}
if (preSort.localeCompare(nextSort) > 0) {
sortNumber += -1;
} else {
sortNumber += 1;
} else {
sortNumber += -1;
}
return;
}
if (preSort.localeCompare(nextSort) > 0) {
sortNumber += 1;
} else {
sortNumber += -1;
}
});
return sortNumber;
Expand Down
87 changes: 87 additions & 0 deletions server/src/tests/helpers/FilterHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { filterByFields, filterByQueryParams } from '../../helpers/FilterHelper';
import { describe, test, expect } from 'vitest';

const data = [
{ name: 'John Doe', age: 30, registered: true },
{ name: 'Jane Doe', age: 25, registered: false },
{ name: 'Tom Jerry', age: 32, registered: true },
{ name: 'Mickey Mouse', age: 22, registered: false },
{ name: 'Donald Duck', age: 18, registered: true },
{ name: 'Bugs Bunny', age: 27, registered: false },
{ name: 'Daffy Duck', age: 28, registered: true },
{ name: 'Porky Pig', age: 25, registered: false },
{ name: 'Elmer Fudd', age: 35, registered: true },
{ name: 'Wile E. Coyote', age: 25, registered: false },
];

describe('filterByFields', () => {
test('filters by the given fields', () => {
const params = {
filter: JSON.stringify({
registered: ['true'],
}),
};
const result = filterByFields(data, params);
expect(result.length).toBe(5);
result.forEach((r) => expect(r.registered).toBe(true));
});

test('returns all data if no filter is provided', () => {
const result = filterByFields(data, {});
expect(result.length).toBe(data.length);
});

test('throws an error if provided filter format is invalid', () => {
expect(() => filterByFields(data, { filter: '{invalid' })).toThrow();
});

test('returns empty array if no data given', () => {
const result = filterByFields([], { filter: JSON.stringify({ name: ['Doe'] }) });
expect(result).toEqual([]);
});
});

const authorizedParams = ['name', 'age', 'registered'];

const params2 = {
name: 'doe', // This should match John Doe and Jane Doe
age: '25', // This should match Jane Doe and Porky Pig
};

describe('filterByQueryParams', () => {
test('filters by the given query param', () => {
let params = { name: 'doe' };
let result = filterByQueryParams(data, params, authorizedParams);
expect(result.length).toBe(2);
result.forEach((r) => expect(r.name.toLowerCase()).toContain(params.name));
});

test('returns all data if no params are provided', () => {
const result = filterByQueryParams(data, {}, authorizedParams);
expect(result.length).toBe(data.length);
});

test('ignores unauthorized params', () => {
const result = filterByQueryParams(data, { franchise: 'wb' }, authorizedParams);
expect(result.length).toBe(data.length);
});

test('returns empty array if no data given', () => {
const result = filterByQueryParams([], params2, authorizedParams);
expect(result).toEqual([]);
});

test('filters by multiple given query params', () => {
let result = filterByQueryParams(data, params2, authorizedParams);
expect(result.length).toBe(1);
expect(result[0].name.toLowerCase()).toContain(params2.name);
expect(result[0].age).toBe(parseInt(params2.age));
});

test('processes numeric params correctly', () => {
const params = { age: '18' }; // This should match Donald Duck
let result = filterByQueryParams(data, params, authorizedParams);
expect(result.length).toBe(1);
expect(result[0].name).toBe('Donald Duck');
});
});
62 changes: 62 additions & 0 deletions server/src/tests/helpers/Pagination.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { describe, test, expect } from 'vitest';
import { paginate } from '../../helpers/PaginationHelper';

describe('paginate', () => {
// Generate an array of 100 items for test
const data: number[] = Array.from({ length: 100 }, (_, i) => i + 1); // [1, 2, ... , 100]

test('returns the correct items for the first page', () => {
const result = paginate(data, 1, 10);
expect(result[0]).toBe(1);
expect(result.length).toBe(10);
expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});

test('returns the correct items for a middle page', () => {
const result = paginate(data, 5, 10);
expect(result[0]).toBe(41);
expect(result.length).toBe(10);
expect(result).toEqual([41, 42, 43, 44, 45, 46, 47, 48, 49, 50]);
});

test('returns the correct items for the last page', () => {
const result = paginate(data, 10, 10);
expect(result[0]).toBe(91);
expect(result.length).toBe(10);
expect(result).toEqual([91, 92, 93, 94, 95, 96, 97, 98, 99, 100]);
});

test('returns an empty array if there are no items', () => {
const result = paginate([], 1, 10);
expect(result.length).toBe(0);
expect(result).toEqual([]);
});

test('adjusts the number of returned items if there are less items than pageSize', () => {
const result = paginate(data, 11, 10);
expect(result.length).toBe(0);
expect(result).toEqual([]);
});

// Generate an array of 100 items for test
const data2: Array<number | null | undefined> = Array.from({ length: 100 }, (_, i) =>
i % 3 === 0 ? null : i % 5 === 0 ? undefined : i + 1,
);

test('includes null and undefined values in the output', () => {
const result = paginate(data2, 1, 10);
expect(result).toEqual([null, 2, 3, null, 5, undefined, null, 8, 9, null]);
});

test('returns an empty array if the given data is undefined', () => {
const nullData = null as unknown as Array<number | null | undefined>;
let result = paginate(nullData, 1, 10);
expect(result).toEqual([]);
});

test('returns an empty array if the given data is null', () => {
const undefinedData = undefined as unknown as Array<number | null | undefined>;
const result = paginate(undefinedData, 1, 10);
expect(result).toEqual([]);
});
});
74 changes: 74 additions & 0 deletions server/src/tests/helpers/SorterHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { test, expect, describe } from 'vitest';
import { sortByFields } from '../../helpers/SorterHelper';

describe('SorterHelper', () => {
test('Sorts array of basic objects by given fields in ascending order', () => {
const data = [
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Tom', age: 35 },
];
const params = { sorter: JSON.stringify({ name: 'ascend' }) };
const result = sortByFields(data, params);
expect(result).toEqual([
{ name: 'Alice', age: 25 },
{ name: 'Josh', age: 30 },
{ name: 'Tom', age: 35 },
]);
});

test('Sorts array of basic objects by given fields in descending order', () => {
const data = [
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Tom', age: 35 },
];
const params = { sorter: JSON.stringify({ name: 'descend' }) };
const result = sortByFields(data, params);
expect(result).toEqual([
{ name: 'Tom', age: 35 },
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
]);
});

test('Returns empty array if input data is empty regardless of sorter', () => {
const data: any[] = [];
const params = { sorter: JSON.stringify({ name: 'descend' }) };
const result = sortByFields(data, params);
expect(result).toEqual([]);
});

test('Returns provided data unaltered if sorter is not provided in params', () => {
const data = [
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Tom', age: 35 },
];
const params = {};
const result = sortByFields(data, params);
expect(result).toEqual(data);
});

test('Returns provided data unaltered if sorter is not valid JSON', () => {
const data = [
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Tom', age: 35 },
];
const params = { sorter: "{name:'descend'}" };
const result = sortByFields(data, params);
expect(result).toEqual(data); // the SorterHelper now handles invalid JSON without throwing errors
});

test('Ignores fields not found on data items when sorting', () => {
const data = [
{ name: 'Josh', age: 30 },
{ name: 'Alice', age: 25 },
{ name: 'Tom', age: 35 },
];
const params = { sorter: JSON.stringify({ foobar: 'ascend' }) }; // the field "foobar" does not exist on any data item
const result = sortByFields(data, params);
expect(result).toEqual(data); // the original data is returned, unaltered
});
});

0 comments on commit e853eb0

Please sign in to comment.