From 6833592fd94df26cfda6339c817fa075f936aa92 Mon Sep 17 00:00:00 2001 From: Ryan Smee Date: Fri, 1 Apr 2022 18:47:13 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=94=A5=20Add=20generic=20comparis?= =?UTF-8?q?on=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comparison function and start change over to using it ✅ Closes: #220 --- packages/falso/src/lib/between-date.ts | 4 +- packages/falso/src/lib/core/core.ts | 45 ++++++-- packages/falso/src/lib/future-date.ts | 4 +- packages/falso/src/lib/json.ts | 16 ++- packages/falso/src/lib/past-date.ts | 4 +- packages/falso/src/lib/recent-date.ts | 4 +- packages/falso/src/lib/soon-date.ts | 4 +- packages/falso/src/tests/core/core.spec.ts | 114 +++++++++++++++++++++ 8 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 packages/falso/src/tests/core/core.spec.ts diff --git a/packages/falso/src/lib/between-date.ts b/packages/falso/src/lib/between-date.ts index 54af5cd16..baa46a352 100644 --- a/packages/falso/src/lib/between-date.ts +++ b/packages/falso/src/lib/between-date.ts @@ -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 { @@ -39,5 +39,5 @@ export function randBetweenDate( ); }; - return fake(generator, options, checkUniqueDate); + return fake(generator, options, dateIsUnique); } diff --git a/packages/falso/src/lib/core/core.ts b/packages/falso/src/lib/core/core.ts index 8dfe72519..f728c9c53 100644 --- a/packages/falso/src/lib/core/core.ts +++ b/packages/falso/src/lib/core/core.ts @@ -14,18 +14,29 @@ type Return = [O] extends [never] export function fake( data: T[] | (() => T), options?: Options, - comparisonFunction: (item: T, items: T[]) => boolean = checkUniquePrimitive + comparisonFunction: (item: T, items: T[]) => boolean = primitiveValueIsUnique, + comparisonKeys?: string[] ): Return { 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( data: () => T, - comparisonFunction: (item: T, items: T[]) => boolean, + isItemADuplicateFunction: ( + item: T, + items: T[], + comparisonKeys?: string[] + ) => boolean, + comparisonKeys?: string[], options?: Options ) { if (!options?.length) { @@ -46,7 +57,7 @@ export function fakeFromFunction( while (items.length < options.length && attempts < maxAttempts) { const item = data(); - if (!comparisonFunction(item, items)) { + if (!isItemADuplicateFunction(item, items, comparisonKeys)) { items.push(item); } @@ -84,21 +95,39 @@ export function fakeFromArray( return newArray; } -export const checkUniquePrimitive: (item: T, items: T[]) => boolean = ( +export const primitiveValueIsUnique: (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: ( 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(arr: T[]): T { return arr[Math.floor(random() * arr.length)]; } diff --git a/packages/falso/src/lib/future-date.ts b/packages/falso/src/lib/future-date.ts index 12ed2259f..83218892b 100644 --- a/packages/falso/src/lib/future-date.ts +++ b/packages/falso/src/lib/future-date.ts @@ -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; @@ -37,5 +37,5 @@ export function randFutureDate( const to = new Date(from.getTime() + yearsInMilliseconds); const factory: () => Date = () => randBetweenDate({ from, to }); - return fake(factory, options, checkUniqueDate); + return fake(factory, options, dateIsUnique); } diff --git a/packages/falso/src/lib/json.ts b/packages/falso/src/lib/json.ts index 2aa7e5eca..8b9bbc75c 100644 --- a/packages/falso/src/lib/json.ts +++ b/packages/falso/src/lib/json.ts @@ -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( diff --git a/packages/falso/src/lib/past-date.ts b/packages/falso/src/lib/past-date.ts index b36cb79f7..ae168b160 100644 --- a/packages/falso/src/lib/past-date.ts +++ b/packages/falso/src/lib/past-date.ts @@ -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; @@ -36,5 +36,5 @@ export function randPastDate( 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); } diff --git a/packages/falso/src/lib/recent-date.ts b/packages/falso/src/lib/recent-date.ts index f9db31f78..2c87dd62b 100644 --- a/packages/falso/src/lib/recent-date.ts +++ b/packages/falso/src/lib/recent-date.ts @@ -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; @@ -36,5 +36,5 @@ export function randRecentDate( 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); } diff --git a/packages/falso/src/lib/soon-date.ts b/packages/falso/src/lib/soon-date.ts index 00bd32313..7eea8e55a 100644 --- a/packages/falso/src/lib/soon-date.ts +++ b/packages/falso/src/lib/soon-date.ts @@ -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; @@ -35,5 +35,5 @@ export function randSoonDate( 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); } diff --git a/packages/falso/src/tests/core/core.spec.ts b/packages/falso/src/tests/core/core.spec.ts new file mode 100644 index 000000000..254d1570e --- /dev/null +++ b/packages/falso/src/tests/core/core.spec.ts @@ -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); + }); + }); + }); +});