Skip to content

Commit

Permalink
feat: πŸ”₯ Add generic comparison function
Browse files Browse the repository at this point in the history
Add comparison function and start change over to using it

βœ… Closes: #220
  • Loading branch information
Ryan Smee committed Apr 1, 2022
1 parent af09284 commit 6833592
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 22 deletions.
4 changes: 2 additions & 2 deletions packages/falso/src/lib/between-date.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { checkUniqueDate, fake, FakeOptions } from './core/core';
import { dateIsUnique, fake, FakeOptions } from './core/core';
import { randNumber } from './number';

interface BetweenOptions extends FakeOptions {
Expand Down Expand Up @@ -39,5 +39,5 @@ export function randBetweenDate<Options extends BetweenOptions = never>(
);
};

return fake(generator, options, checkUniqueDate);
return fake(generator, options, dateIsUnique);
}
45 changes: 37 additions & 8 deletions packages/falso/src/lib/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,29 @@ type Return<T, O extends FakeOptions> = [O] extends [never]
export function fake<T, Options extends FakeOptions>(
data: T[] | (() => T),
options?: Options,
comparisonFunction: (item: T, items: T[]) => boolean = checkUniquePrimitive
comparisonFunction: (item: T, items: T[]) => boolean = primitiveValueIsUnique,
comparisonKeys?: string[]
): Return<T, Options> {
if (Array.isArray(data)) {
return fakeFromArray(data, options) as any;
}

return fakeFromFunction(data, comparisonFunction, options) as any;
return fakeFromFunction(
data,
comparisonFunction,
comparisonKeys,
options
) as any;
}

export function fakeFromFunction<T, Options extends FakeOptions>(
data: () => T,
comparisonFunction: (item: T, items: T[]) => boolean,
isItemADuplicateFunction: (
item: T,
items: T[],
comparisonKeys?: string[]
) => boolean,
comparisonKeys?: string[],
options?: Options
) {
if (!options?.length) {
Expand All @@ -46,7 +57,7 @@ export function fakeFromFunction<T, Options extends FakeOptions>(
while (items.length < options.length && attempts < maxAttempts) {
const item = data();

if (!comparisonFunction(item, items)) {
if (!isItemADuplicateFunction(item, items, comparisonKeys)) {
items.push(item);
}

Expand Down Expand Up @@ -84,21 +95,39 @@ export function fakeFromArray<T, Options extends FakeOptions>(
return newArray;
}

export const checkUniquePrimitive: <T>(item: T, items: T[]) => boolean = (
export const primitiveValueIsUnique: <T>(item: T, items: T[]) => boolean = (
item,
items
) => items.includes(item);
) => !items.includes(item);

export const checkUniqueDate: (date: Date, dates: Date[]) => boolean = (
export const dateIsUnique: (date: Date, dates: Date[]) => boolean = (
date,
dates
) => dates.some((d) => d.valueOf() === date.valueOf());
) => !dates.some((d) => d.valueOf() === date.valueOf());

export const checkUniqueObjectWithId: <T extends { id: string }>(
item: T,
items: T[]
) => boolean = (item, items) => items.some((i) => i.id === item.id);

export const objectIsUnique: (
item: any,
items: any[],
keys: string[]
) => boolean = (item: any, items: any[], keys: string[]) => {
for (const key of keys) {
if (!item[key]) {
throw `${key} does not exist in this array value type`;
}

if (items.some((arrayItem) => arrayItem[key] === item[key])) {
return true;
}
}

return false;
};

export function randElement<T>(arr: T[]): T {
return arr[Math.floor(random() * arr.length)];
}
Expand Down
4 changes: 2 additions & 2 deletions packages/falso/src/lib/future-date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { randBetweenDate } from './between-date';
import { checkUniqueDate, fake, FakeOptions } from './core/core';
import { dateIsUnique, fake, FakeOptions } from './core/core';

interface FutureOptions extends FakeOptions {
years?: number;
Expand Down Expand Up @@ -37,5 +37,5 @@ export function randFutureDate<Options extends FutureOptions = never>(
const to = new Date(from.getTime() + yearsInMilliseconds);
const factory: () => Date = () => randBetweenDate({ from, to });

return fake(factory, options, checkUniqueDate);
return fake(factory, options, dateIsUnique);
}
16 changes: 12 additions & 4 deletions packages/falso/src/lib/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,21 @@ const generateRandomValue = (): any => {
* @example
* randJSON()
*
* @example If a fixed number of keys are required
* @example
*
* randJSON({ totalKeys: 10 }) // If a fixed number of keys are required
*
* @example
*
* randJSON({ totalKeys: 10 })
* randJSON({ minKeys: 1, maxKeys: 10 }) // If a random number of keys are required
*
* @example If a random number of keys are required
* @example
*
* randJSON({ length: 10 })
*
* @example
*
* randJSON({ minKeys: 1, maxKeys: 10 })
* randJSON({ length: 10, priority: 'length' })
*
*/
export function randJSON<Options extends RandomJSONOptions = never>(
Expand Down
4 changes: 2 additions & 2 deletions packages/falso/src/lib/past-date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { randBetweenDate } from './between-date';
import { checkUniqueDate, fake, FakeOptions } from './core/core';
import { dateIsUnique, fake, FakeOptions } from './core/core';

interface PastOptions extends FakeOptions {
years?: number;
Expand Down Expand Up @@ -36,5 +36,5 @@ export function randPastDate<Options extends PastOptions = never>(
const to = new Date();
const from = new Date(to.getTime() - yearsInMilliseconds);

return fake(() => randBetweenDate({ from, to }), options, checkUniqueDate);
return fake(() => randBetweenDate({ from, to }), options, dateIsUnique);
}
4 changes: 2 additions & 2 deletions packages/falso/src/lib/recent-date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { randBetweenDate } from './between-date';
import { checkUniqueDate, fake, FakeOptions } from './core/core';
import { dateIsUnique, fake, FakeOptions } from './core/core';

interface RecentOptions extends FakeOptions {
days?: number;
Expand Down Expand Up @@ -36,5 +36,5 @@ export function randRecentDate<Options extends RecentOptions = never>(
const to = new Date();
const from = new Date(to.getTime() - daysInMilliseconds);

return fake(() => randBetweenDate({ from, to }), options, checkUniqueDate);
return fake(() => randBetweenDate({ from, to }), options, dateIsUnique);
}
4 changes: 2 additions & 2 deletions packages/falso/src/lib/soon-date.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { randBetweenDate } from './between-date';
import { checkUniqueDate, fake, FakeOptions } from './core/core';
import { dateIsUnique, fake, FakeOptions } from './core/core';

interface SoonOptions extends FakeOptions {
days?: number;
Expand Down Expand Up @@ -35,5 +35,5 @@ export function randSoonDate<Options extends SoonOptions = never>(
const daysInMilliseconds = days * 24 * 60 * 60 * 1000;
const from = new Date();
const to = new Date(from.getTime() + daysInMilliseconds);
return fake(() => randBetweenDate({ from, to }), options, checkUniqueDate);
return fake(() => randBetweenDate({ from, to }), options, dateIsUnique);
}
114 changes: 114 additions & 0 deletions packages/falso/src/tests/core/core.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { objectIsUnique } from '../../lib/core/core';
import { randUser, User } from '../../lib/user';
import { randUuid } from '../../lib/uuid';
import { randFirstName } from '../../lib/first-name';

describe('valueExistsInObjectArray', () => {
describe("keys contains a key that doesn't exist", () => {
let array: User[];
let newItem: User;
let keys: string[];

beforeEach(() => {
array = randUser({ length: 3 });
newItem = randUser();
keys = ['id', 'noExistentKey', 'firstName'];
});

it('should throw error', () => {
expect(() => objectIsUnique(newItem, array, keys)).toThrow(
'noExistentKey does not exist in this array value type'
);
});
});

describe('1 key is passed', () => {
let array: User[];
let newItem: User;
let keys: string[];

beforeEach(() => {
array = randUser({ length: 3 });
newItem = randUser();
keys = ['id'];
});

describe('passed item matches and array item have 1 matching key value', () => {
let sharedId: string;

beforeEach(() => {
sharedId = randUuid();
newItem.id = sharedId;
array[1].id = sharedId;
});

it('should return true', () => {
const result = objectIsUnique(newItem, array, keys);

expect(result).toEqual(true);
});
});

describe('passed item matches and array item have 0 matching key values', () => {
beforeEach(() => {
array[0].id = randUuid() + '0';
array[1].id = randUuid() + '1';
array[2].id = randUuid() + '2';
newItem.id = randUuid() + '3';
});

it('should return true', () => {
const result = objectIsUnique(newItem, array, keys);

expect(result).toEqual(false);
});
});
});

describe('multiple keys are passed', () => {
let array: User[];
let newItem: User;
let keys: string[];

beforeEach(() => {
array = randUser({ length: 3 });
newItem = randUser();
keys = ['id', 'firstName'];
});

describe('passed item matches and array item have 1 matching key value', () => {
let sharedFirstName: string;

beforeEach(() => {
sharedFirstName = randFirstName();
newItem.firstName = sharedFirstName;
array[1].firstName = sharedFirstName;
});

it('should return true', () => {
const result = objectIsUnique(newItem, array, keys);

expect(result).toEqual(true);
});
});

describe('passed item matches and array item have 0 matching key values', () => {
beforeEach(() => {
array[0].id = randUuid() + '0';
array[0].firstName = randFirstName() + '0';
array[1].id = randUuid() + '1';
array[1].id = randFirstName() + '1';
array[2].id = randUuid() + '2';
array[2].id = randFirstName() + '2';
newItem.id = randUuid() + '3';
newItem.id = randFirstName() + '3';
});

it('should return true', () => {
const result = objectIsUnique(newItem, array, keys);

expect(result).toEqual(false);
});
});
});
});

0 comments on commit 6833592

Please sign in to comment.