Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests and enhancements for helper functions #32

Merged
merged 1 commit into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
});
});
Loading