From d16572a9b02573c23187529e44b9c66a30f4718b Mon Sep 17 00:00:00 2001 From: Emmanuel De Saint Steban Date: Wed, 17 Aug 2022 11:56:24 +0000 Subject: [PATCH 1/2] feat(toCollection): add index into the generateCollection function --- packages/falso/src/lib/collection.ts | 12 ++++++++++-- packages/falso/src/tests/collection.spec.ts | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/falso/src/lib/collection.ts b/packages/falso/src/lib/collection.ts index 7e4a4d430..c44ba3cc3 100644 --- a/packages/falso/src/lib/collection.ts +++ b/packages/falso/src/lib/collection.ts @@ -11,22 +11,30 @@ import { FakeOptions, fake } from './core/core'; * return { data: randNumber(); } * }) * + * example : {data: 2} + * * @example * + * * toCollection(() => { * return { data: randNumber(); } * }, { length: 10 }) // default is no length. * + * example : [{data: 2}, {data: 5}, ..., {data: 3}] + * + * The generator function, could receive two parameters : + * - options : the options object. You can pass to the generator some other options, if needed + * - index : The index of item currently generated. if you would like to use it in your generator function. */ export function toCollection< Collection = never, Options extends FakeOptions = never >( - generateCollection: (options?: Options) => Collection, + generateCollection: (options?: Options, index?: number) => Collection, options?: Options ): Collection | Collection[] { return fake( - () => generateCollection(options), + (index: number) => generateCollection(options, index), options ) as Collection | Collection[]; } diff --git a/packages/falso/src/tests/collection.spec.ts b/packages/falso/src/tests/collection.spec.ts index 10d65084a..f4d940ac1 100644 --- a/packages/falso/src/tests/collection.spec.ts +++ b/packages/falso/src/tests/collection.spec.ts @@ -56,7 +56,8 @@ describe('randCollection', () => { }) ).toEqual([{ data: 1 }, { data: 1 }]); expect(generatorFunction).toHaveBeenCalledTimes(2); - expect(generatorFunction).toHaveBeenCalledWith({ length: 2 }); + expect(generatorFunction).toHaveBeenNthCalledWith(1, { length: 2 }, 0); + expect(generatorFunction).toHaveBeenNthCalledWith(2, { length: 2 }, 1); }); }); describe('with call external random function', () => { From a10276e49f44f239e6f7e8fd3807a6d461a3fbf5 Mon Sep 17 00:00:00 2001 From: Emmanuel De Saint Steban Date: Wed, 17 Aug 2022 13:50:47 +0000 Subject: [PATCH 2/2] feat(toCollection): add random length collection generator You can specify minLength or maxLength (or both), to generate a random length collection. If one of minLength or maxLength is specified, a random length will be calculated before, and will return this random number of items. fix #303 --- docs-generator.js | 1 + packages/falso/src/lib/collection.ts | 62 ++++++-- packages/falso/src/tests/collection.spec.ts | 167 ++++++++++++++++++++ 3 files changed, 215 insertions(+), 15 deletions(-) diff --git a/docs-generator.js b/docs-generator.js index f3e82ac94..885b71e64 100644 --- a/docs-generator.js +++ b/docs-generator.js @@ -13,6 +13,7 @@ const functionModifiers = { randRecentDate: 'randRecentDate().toString()', randSoonDate: 'randSoonDate().toString()', randTextRange: 'randTextRange({ min: 10, max: 100 })', + toCollection: 'toCollection(() => { return { data: randNumber() }}, {length: 10})', } const skipLivePreview = ['seed']; diff --git a/packages/falso/src/lib/collection.ts b/packages/falso/src/lib/collection.ts index c44ba3cc3..5ee819822 100644 --- a/packages/falso/src/lib/collection.ts +++ b/packages/falso/src/lib/collection.ts @@ -1,17 +1,31 @@ -import { FakeOptions, fake } from './core/core'; +import { FakeOptions, fake, Return, getRandomInRange } from './core/core'; + +export interface ToCollectionOptions extends FakeOptions { + minLength?: number; // default 0 + maxLength?: number; // default minLength + 10 = 10 +} /** * Generate a collection from a custom generators functions * - * @category util + * The generator function, could receive two parameters : + * - options : the options object. You can pass to the generator some other options, if needed + * - index : The index of item currently generated. if you would like to use it in your generator function. + * + * ### Randomly length generator. * - * @example + * You can specify minLength or maxLength (or both), to generate a random length collection. + * If one of minLength or maxLength is specified, a random length will be calculated before, + * and will return this random number of items. + * By default, minLength was equal to 0 and maxLength equal to min + 10. * - * toCollection(() => { - * return { data: randNumber(); } - * }) + * If minLength was equals to 0 and random return to 0, this function will return an empty array. * - * example : {data: 2} + * @category general + * + * @example + * + * toCollection(() => { return { data: randNumber() }}) // {data: 2} * * @example * @@ -19,22 +33,40 @@ import { FakeOptions, fake } from './core/core'; * toCollection(() => { * return { data: randNumber(); } * }, { length: 10 }) // default is no length. + * // [{data: 2}, {data: 5}, ..., {data: 3}] * - * example : [{data: 2}, {data: 5}, ..., {data: 3}] + * @example * - * The generator function, could receive two parameters : - * - options : the options object. You can pass to the generator some other options, if needed - * - index : The index of item currently generated. if you would like to use it in your generator function. - */ + * toCollection(() => { + * return { data: randNumber(); } + * }, { minLength: 1, maxLength: 10 }) // default is minLength = 0 and maxLength = min + 10. + * // [{data: 2}, {data: 5}, ..., {data: 3}] + * + * */ export function toCollection< Collection = never, - Options extends FakeOptions = never + Options extends ToCollectionOptions = never >( generateCollection: (options?: Options, index?: number) => Collection, options?: Options -): Collection | Collection[] { +): Return | Collection[] { + if ( + options && + !options?.length && + (options?.minLength !== null || options?.maxLength !== null) + ) { + const min = options.minLength || 0; + options.length = getRandomInRange({ + min, + max: options.maxLength || min + 10, + fraction: 0, + }); + if (options.length === 0) { + return []; + } + } return fake( (index: number) => generateCollection(options, index), options - ) as Collection | Collection[]; + ); } diff --git a/packages/falso/src/tests/collection.spec.ts b/packages/falso/src/tests/collection.spec.ts index f4d940ac1..07ae170f3 100644 --- a/packages/falso/src/tests/collection.spec.ts +++ b/packages/falso/src/tests/collection.spec.ts @@ -1,6 +1,7 @@ import { toCollection } from '../lib/collection'; import { randNumber, random, seed } from '@ngneat/falso'; import * as numberFunctions from '../lib/number'; +import * as coreFunctions from '../lib/core/core'; import Mock = jest.Mock; interface fakeData { @@ -84,4 +85,170 @@ describe('randCollection', () => { ).toEqual([{ data: 1 }, { data: 2 }]); }); }); + + describe('Generate a random lenth of collection', () => { + let randNumberSpy: jest.SpyInstance; + + beforeAll(() => { + randNumberSpy = jest.spyOn(coreFunctions, 'getRandomInRange'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should return an empty array if getRandomInRange equal to zero', () => { + randNumberSpy.mockReturnValueOnce(0); + + expect( + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 0 } + ) + ).toEqual([]); + }); + + it('should return an array with one element if getRandomInRange equal to 1', () => { + randNumberSpy.mockReturnValueOnce(1); + + expect( + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 0 } + ) + ).toEqual([{ data: 0 }]); + }); + + it('should return an array with one element if minLength and maxLength equal to 1', () => { + expect( + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 1, maxLength: 1 } + ) + ).toEqual([{ data: 0 }]); + }); + + it('should return an array with 8 element if getRandomInRange return a 8 value', () => { + randNumberSpy.mockReturnValueOnce(8); + + expect( + toCollection( + (__option, index) => { + return { data: index }; + }, + { maxLength: 10 } + ) + ).toEqual([ + { data: 0 }, + { data: 1 }, + { data: 2 }, + { data: 3 }, + { data: 4 }, + { data: 5 }, + { data: 6 }, + { data: 7 }, + ]); + }); + + it('should getRandomInRange with correct value', () => { + randNumberSpy.mockReturnValue(5); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { maxLength: 8 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(1, { + min: 0, + max: 8, + fraction: 0, + }); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 1, maxLength: 7 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(2, { + min: 1, + max: 7, + fraction: 0, + }); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 8 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(3, { + min: 8, + max: 18, + fraction: 0, + }); + }); + + it('should getRandomInRange with default value if default value is given', () => { + randNumberSpy.mockReturnValue(5); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { maxLength: 10 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(1, { + min: 0, + max: 10, + fraction: 0, + }); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 0, maxLength: 10 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(2, { + min: 0, + max: 10, + fraction: 0, + }); + + toCollection( + (__option, index) => { + return { data: index }; + }, + { minLength: 0 } + ); + expect(randNumberSpy).toHaveBeenNthCalledWith(3, { + min: 0, + max: 10, + fraction: 0, + }); + }); + + it('should not call getRandomInRange if minLength or maxLength was not called', () => { + toCollection( + (__option, index) => { + return { data: index }; + }, + { length: 10 } + ); + expect(randNumberSpy).not.toBeCalled(); + + toCollection((__option, index) => { + return { data: index }; + }); + expect(randNumberSpy).not.toBeCalled(); + }); + }); });