diff --git a/docs-generator.js b/docs-generator.js index f3e82ac9..885b71e6 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 7e4a4d43..5ee81982 100644 --- a/packages/falso/src/lib/collection.ts +++ b/packages/falso/src/lib/collection.ts @@ -1,32 +1,72 @@ -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. + * + * 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. + * + * If minLength was equals to 0 and random return to 0, this function will return an empty array. + * + * @category general * * @example * + * toCollection(() => { return { data: randNumber() }}) // {data: 2} + * + * @example + * + * * toCollection(() => { * return { data: randNumber(); } - * }) + * }, { length: 10 }) // default is no length. + * // [{data: 2}, {data: 5}, ..., {data: 3}] * * @example * * toCollection(() => { * return { data: randNumber(); } - * }, { length: 10 }) // default is no length. + * }, { 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) => Collection, + 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( - () => 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 10d65084..07ae170f 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 { @@ -56,7 +57,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', () => { @@ -83,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(); + }); + }); });