diff --git a/README.md b/README.md index 2bf22eb4d2f..d21fc5f5cfd 100644 --- a/README.md +++ b/README.md @@ -132,12 +132,12 @@ If you want consistent results, you can set your own seed: ```ts faker.seed(123); -const firstRandom = faker.datatype.number(); +const firstRandom = faker.number.int(); // Setting the seed again resets the sequence. faker.seed(123); -const secondRandom = faker.datatype.number(); +const secondRandom = faker.number.int(); console.log(firstRandom === secondRandom); ``` diff --git a/docs/.vitepress/api-pages.ts b/docs/.vitepress/api-pages.ts index 69bb49644c7..188e24d371d 100644 --- a/docs/.vitepress/api-pages.ts +++ b/docs/.vitepress/api-pages.ts @@ -18,6 +18,7 @@ export const apiPages = [ { text: 'Location', link: '/api/location.html' }, { text: 'Lorem', link: '/api/lorem.html' }, { text: 'Music', link: '/api/music.html' }, + { text: 'Number', link: '/api/number.html' }, { text: 'Person', link: '/api/person.html' }, { text: 'Phone', link: '/api/phone.html' }, { text: 'Random', link: '/api/random.html' }, diff --git a/src/faker.ts b/src/faker.ts index fd7ed5223a2..7870d9c5618 100644 --- a/src/faker.ts +++ b/src/faker.ts @@ -21,6 +21,7 @@ import type { LocationModule as AddressModule } from './modules/location'; import { LocationModule } from './modules/location'; import { LoremModule } from './modules/lorem'; import { MusicModule } from './modules/music'; +import { NumberModule } from './modules/number'; import type { PersonModule as NameModule } from './modules/person'; import { PersonModule } from './modules/person'; import { PhoneModule } from './modules/phone'; @@ -103,6 +104,7 @@ export class Faker { readonly lorem: LoremModule = new LoremModule(this); readonly music: MusicModule = new MusicModule(this); readonly person: PersonModule = new PersonModule(this); + readonly number: NumberModule = new NumberModule(this); readonly phone: PhoneModule = new PhoneModule(this); readonly science: ScienceModule = new ScienceModule(this); readonly string: StringModule = new StringModule(this); @@ -239,12 +241,12 @@ export class Faker { * @example * // Consistent values for tests: * faker.seed(42) - * faker.datatype.number(10); // 4 - * faker.datatype.number(10); // 8 + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 * * faker.seed(42) - * faker.datatype.number(10); // 4 - * faker.datatype.number(10); // 8 + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 * * @example * // Random but reproducible tests: @@ -271,12 +273,12 @@ export class Faker { * @example * // Consistent values for tests: * faker.seed([42, 13, 17]) - * faker.datatype.number(10); // 4 - * faker.datatype.number(10); // 8 + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 * * faker.seed([42, 13, 17]) - * faker.datatype.number(10); // 4 - * faker.datatype.number(10); // 8 + * faker.number.int(10); // 4 + * faker.number.int(10); // 8 * * @example * // Random but reproducible tests: diff --git a/src/index.ts b/src/index.ts index 9589c25750b..5d5c280c86c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -63,6 +63,7 @@ export type { } from './modules/location'; export type { LoremModule } from './modules/lorem'; export type { MusicModule } from './modules/music'; +export type { NumberModule } from './modules/number'; export { Sex } from './modules/person'; export type { /** @deprecated Use PersonModule instead */ diff --git a/src/modules/color/index.ts b/src/modules/color/index.ts index 4fd79a063e9..b232f6490d5 100644 --- a/src/modules/color/index.ts +++ b/src/modules/color/index.ts @@ -320,13 +320,9 @@ export class ColorModule { color = formatHexColor(color, options); return color; } - color = Array.from({ length: 3 }).map(() => - this.faker.datatype.number({ min: 0, max: 255 }) - ); + color = Array.from({ length: 3 }).map(() => this.faker.number.int(255)); if (includeAlpha) { - color.push( - this.faker.datatype.float({ min: 0, max: 1, precision: 0.01 }) - ); + color.push(this.faker.number.float({ max: 1, precision: 0.01 })); cssFunction = 'rgba'; } return toColorFormat(color, format, cssFunction); @@ -385,7 +381,7 @@ export class ColorModule { cmyk(options?: { format?: ColorFormat }): string | number[]; cmyk(options?: { format?: ColorFormat }): string | number[] { const color: string | number[] = Array.from({ length: 4 }).map(() => - this.faker.datatype.float({ min: 0, max: 1, precision: 0.01 }) + this.faker.number.float({ max: 1, precision: 0.01 }) ); return toColorFormat(color, options?.format || 'decimal', 'cmyk'); } @@ -460,9 +456,9 @@ export class ColorModule { format?: ColorFormat; includeAlpha?: boolean; }): string | number[] { - const hsl: number[] = [this.faker.datatype.number({ min: 0, max: 360 })]; + const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < (options?.includeAlpha ? 3 : 2); i++) { - hsl.push(this.faker.datatype.float({ min: 0, max: 1, precision: 0.01 })); + hsl.push(this.faker.number.float({ max: 1, precision: 0.01 })); } return toColorFormat( hsl, @@ -537,9 +533,9 @@ export class ColorModule { * @since 7.0.0 */ hwb(options?: { format?: ColorFormat }): string | number[] { - const hsl: number[] = [this.faker.datatype.number({ min: 0, max: 360 })]; + const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < 2; i++) { - hsl.push(this.faker.datatype.float({ min: 0, max: 1, precision: 0.01 })); + hsl.push(this.faker.number.float({ max: 1, precision: 0.01 })); } return toColorFormat(hsl, options?.format || 'decimal', 'hwb'); } @@ -596,12 +592,10 @@ export class ColorModule { */ lab(options?: { format?: ColorFormat }): string | number[]; lab(options?: { format?: ColorFormat }): string | number[] { - const lab = [ - this.faker.datatype.float({ min: 0, max: 1, precision: 0.000001 }), - ]; + const lab = [this.faker.number.float({ max: 1, precision: 0.000001 })]; for (let i = 0; i < 2; i++) { lab.push( - this.faker.datatype.float({ min: -100, max: 100, precision: 0.0001 }) + this.faker.number.float({ min: -100, max: 100, precision: 0.0001 }) ); } return toColorFormat(lab, options?.format || 'decimal', 'lab'); @@ -671,13 +665,9 @@ export class ColorModule { */ lch(options?: { format?: ColorFormat }): string | number[]; lch(options?: { format?: ColorFormat }): string | number[] { - const lch = [ - this.faker.datatype.float({ min: 0, max: 1, precision: 0.000001 }), - ]; + const lch = [this.faker.number.float({ max: 1, precision: 0.000001 })]; for (let i = 0; i < 2; i++) { - lch.push( - this.faker.datatype.number({ min: 0, max: 230, precision: 0.1 }) - ); + lch.push(this.faker.number.float({ max: 230, precision: 0.1 })); } return toColorFormat(lch, options?.format || 'decimal', 'lch'); } @@ -753,7 +743,7 @@ export class ColorModule { options = { ...options, space: 'sRGB' }; } const color = Array.from({ length: 3 }).map(() => - this.faker.datatype.float({ min: 0, max: 1, precision: 0.0001 }) + this.faker.number.float({ max: 1, precision: 0.0001 }) ); return toColorFormat( color, diff --git a/src/modules/commerce/index.ts b/src/modules/commerce/index.ts index 088112c1577..99515463bc7 100644 --- a/src/modules/commerce/index.ts +++ b/src/modules/commerce/index.ts @@ -67,14 +67,10 @@ export class CommerceModule { return `${symbol}${0.0}`; } - const randValue = this.faker.datatype.number({ max: max, min: min }); + // TODO @Shinigami92 2022-11-24: https://github.com/faker-js/faker/issues/350 + const randValue = this.faker.number.int({ min, max }); - return ( - symbol + - (Math.round(randValue * Math.pow(10, dec)) / Math.pow(10, dec)).toFixed( - dec - ) - ); + return symbol + randValue.toFixed(dec); } /** diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts index b40f2f432fc..22ee1bfe66c 100644 --- a/src/modules/datatype/index.ts +++ b/src/modules/datatype/index.ts @@ -1,7 +1,5 @@ import type { Faker } from '../..'; -import { FakerError } from '../../errors/faker-error'; import { deprecated } from '../../internal/deprecated'; -import type { Mersenne } from '../../internal/mersenne/mersenne'; /** * Module to generate various primitive values and data types. @@ -37,36 +35,26 @@ export class DatatypeModule { * faker.datatype.number({ min: 10, max: 100, precision: 0.01 }) // 36.94 * * @since 5.5.0 + * + * @deprecated Use `faker.number.int()` instead. */ number( options: number | { min?: number; max?: number; precision?: number } = 99999 ): number { + deprecated({ + deprecated: 'faker.datatype.number()', + proposed: 'faker.number.int()', + since: '8.0', + until: '9.0', + }); + if (typeof options === 'number') { options = { max: options }; } - const { min = 0, precision = 1 } = options; - const max = options.max ?? min + 99999; - - if (max === min) { - return min; - } - - if (max < min) { - throw new FakerError(`Max ${max} should be greater than min ${min}.`); - } + const { min = 0, max = min + 99999, precision = 1 } = options; - const mersenne: Mersenne = - // @ts-expect-error: access private member field - this.faker._mersenne; - - const randomNumber = mersenne.next({ - min: min / precision, - max: max / precision + 1, - }); - - // Workaround problem in float point arithmetics for e.g. 6681493 / 0.01 - return randomNumber / (1 / precision); + return this.faker.number.float({ min, max, precision }); } /** @@ -86,24 +74,19 @@ export class DatatypeModule { * faker.datatype.float({ min: 10, max: 100, precision: 0.001 }) // 57.315 * * @since 5.5.0 + * + * @deprecated Use `faker.number.float()` instead. */ float( options?: number | { min?: number; max?: number; precision?: number } ): number { - if (typeof options === 'number') { - options = { - precision: options, - }; - } - options = options || {}; - const opts: { precision?: number } = {}; - for (const p in options) { - opts[p] = options[p]; - } - if (opts.precision == null) { - opts.precision = 0.01; - } - return this.number(opts); + deprecated({ + deprecated: 'faker.datatype.float()', + proposed: 'faker.number.float()', + since: '8.0', + until: '9.0', + }); + return this.faker.number.float(options); } /** @@ -139,7 +122,7 @@ export class DatatypeModule { max = Date.UTC(2100, 0); } - return new Date(this.number({ min, max })); + return new Date(this.faker.number.int({ min, max })); } /** @@ -177,7 +160,7 @@ export class DatatypeModule { * * @since 5.5.0 * - * @deprecated Use faker.string.uuid() instead. + * @deprecated Use `faker.string.uuid()` instead. */ uuid(): string { deprecated({ @@ -222,7 +205,7 @@ export class DatatypeModule { // This check is required to avoid returning false when float() returns 1 return true; } - return this.float({ min: 0, max: 1 }) < probability; + return this.faker.number.float({ max: 1 }) < probability; } /** @@ -247,7 +230,7 @@ export class DatatypeModule { * * @since 6.1.2 * - * @deprecated Use `faker.string.hexadecimal()` instead. + * @deprecated Use `faker.string.hexadecimal()` or `faker.number.hex()` instead. */ hexadecimal( options: { @@ -258,7 +241,7 @@ export class DatatypeModule { ): string { deprecated({ deprecated: 'faker.datatype.hexadecimal()', - proposed: 'faker.string.hexadecimal()', + proposed: 'faker.string.hexadecimal() or faker.number.hex()', since: '8.0', until: '9.0', }); @@ -280,7 +263,7 @@ export class DatatypeModule { properties.forEach((prop) => { returnObject[prop] = this.boolean() ? this.faker.string.sample() - : this.number(); + : this.faker.number.int(); }); return JSON.stringify(returnObject); @@ -299,7 +282,7 @@ export class DatatypeModule { */ array(length = 10): Array { return Array.from({ length }).map(() => - this.boolean() ? this.faker.string.sample() : this.number() + this.boolean() ? this.faker.string.sample() : this.faker.number.int() ); } @@ -320,6 +303,8 @@ export class DatatypeModule { * faker.datatype.bigInt({ min: 10n, max: 100n }) // 36n * * @since 6.0.0 + * + * @deprecated Use `faker.number.bigInt()` instead. */ bigInt( options?: @@ -332,36 +317,12 @@ export class DatatypeModule { max?: bigint | boolean | number | string; } ): bigint { - let min: bigint; - let max: bigint; - - if (typeof options === 'object') { - min = BigInt(options.min ?? 0); - max = BigInt(options.max ?? min + BigInt(999999999999999)); - } else { - min = BigInt(0); - max = BigInt(options ?? 999999999999999); - } - - if (max === min) { - return min; - } - - if (max < min) { - throw new FakerError(`Max ${max} should be larger then min ${min}.`); - } - - const delta = max - min; - - const offset = - BigInt( - this.faker.string.numeric({ - length: delta.toString(10).length, - allowLeadingZeros: true, - }) - ) % - (delta + BigInt(1)); - - return min + offset; + deprecated({ + deprecated: 'faker.datatype.bigInt()', + proposed: 'faker.number.bigInt()', + since: '8.0', + until: '9.0', + }); + return this.faker.number.bigInt(options); } } diff --git a/src/modules/date/index.ts b/src/modules/date/index.ts index d5470dabc4a..673f0e337ac 100644 --- a/src/modules/date/index.ts +++ b/src/modules/date/index.ts @@ -58,7 +58,7 @@ export class DateModule { }; let past = date.getTime(); - past -= this.faker.datatype.number(range); // some time from now to N years ago, in milliseconds + past -= this.faker.number.int(range); // some time from now to N years ago, in milliseconds date.setTime(past); return date; @@ -91,7 +91,7 @@ export class DateModule { }; let future = date.getTime(); - future += this.faker.datatype.number(range); // some time from now to N years later, in milliseconds + future += this.faker.number.int(range); // some time from now to N years later, in milliseconds date.setTime(future); return date; @@ -111,7 +111,7 @@ export class DateModule { between(from: string | Date | number, to: string | Date | number): Date { const fromMs = toDate(from).getTime(); const toMs = toDate(to).getTime(); - const dateOffset = this.faker.datatype.number(toMs - fromMs); + const dateOffset = this.faker.number.int(toMs - fromMs); return new Date(fromMs + dateOffset); } @@ -176,7 +176,7 @@ export class DateModule { }; let future = date.getTime(); - future -= this.faker.datatype.number(range); // some time from now to N days ago, in milliseconds + future -= this.faker.number.int(range); // some time from now to N days ago, in milliseconds date.setTime(future); return date; @@ -209,7 +209,7 @@ export class DateModule { }; let future = date.getTime(); - future += this.faker.datatype.number(range); // some time from now to N days later, in milliseconds + future += this.faker.number.int(range); // some time from now to N days later, in milliseconds date.setTime(future); return date; @@ -343,6 +343,6 @@ export class DateModule { throw new FakerError(`Max ${max} should be larger then min ${min}.`); } - return new Date(this.faker.datatype.number({ min, max })); + return new Date(this.faker.number.int({ min, max })); } } diff --git a/src/modules/finance/index.ts b/src/modules/finance/index.ts index e739d9207f6..e6533e50538 100644 --- a/src/modules/finance/index.ts +++ b/src/modules/finance/index.ts @@ -143,10 +143,10 @@ export class FinanceModule { symbol: string = '', autoFormat?: boolean ): string { - const randValue = this.faker.datatype.number({ + const randValue = this.faker.number.float({ max, min, - precision: Math.pow(10, -dec), + precision: 10 ** -dec, }); let formattedString: string; @@ -231,7 +231,7 @@ export class FinanceModule { * @since 3.1.0 */ bitcoinAddress(): string { - const addressLength = this.faker.datatype.number({ min: 25, max: 39 }); + const addressLength = this.faker.number.int({ min: 25, max: 39 }); let address = this.faker.helpers.arrayElement(['1', '3']); @@ -253,7 +253,7 @@ export class FinanceModule { * @since 5.0.0 */ litecoinAddress(): string { - const addressLength = this.faker.datatype.number({ min: 26, max: 33 }); + const addressLength = this.faker.number.int({ min: 26, max: 33 }); let address = this.faker.helpers.arrayElement(['L', 'M', '3']); @@ -305,11 +305,7 @@ export class FinanceModule { * @since 5.0.0 */ creditCardCVV(): string { - let cvv = ''; - for (let i = 0; i < 3; i++) { - cvv += this.faker.datatype.number({ max: 9 }).toString(); - } - return cvv; + return this.faker.string.numeric({ length: 3, allowLeadingZeros: true }); } /** @@ -342,7 +338,7 @@ export class FinanceModule { if (length < 1) { throw new FakerError('minimum length is 1'); } - return Array.from({ length }, () => this.faker.datatype.number(9)).join(''); + return this.faker.string.numeric({ length, allowLeadingZeros: true }); } /** @@ -394,7 +390,7 @@ export class FinanceModule { s += this.faker.helpers.arrayElement(iban.alpha); } else if (bban.type === 'c') { if (this.faker.datatype.boolean(0.8)) { - s += this.faker.datatype.number(9); + s += this.faker.number.int(9); } else { s += this.faker.helpers.arrayElement(iban.alpha); } @@ -408,7 +404,7 @@ export class FinanceModule { c--; } } else { - s += this.faker.datatype.number(9); + s += this.faker.number.int(9); } } c--; diff --git a/src/modules/git/index.ts b/src/modules/git/index.ts index 336da8203dc..2ea65063da9 100644 --- a/src/modules/git/index.ts +++ b/src/modules/git/index.ts @@ -145,7 +145,7 @@ export class GitModule { // Timezone offset dateParts.push( GIT_TIMEZONE_FORMAT.format( - this.faker.datatype.number({ min: -11, max: 12 }) * 100 + this.faker.number.int({ min: -11, max: 12 }) * 100 ) ); diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index 406f7f19c94..6caa8effcb3 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -56,9 +56,9 @@ export class HelpersModule { let str = ''; for (let i = 0; i < string.length; i++) { if (string.charAt(i) === symbol) { - str += this.faker.datatype.number(9); + str += this.faker.number.int(9); } else if (string.charAt(i) === '!') { - str += this.faker.datatype.number({ min: 2, max: 9 }); + str += this.faker.number.int({ min: 2, max: 9 }); } else { str += string.charAt(i); } @@ -117,13 +117,13 @@ export class HelpersModule { for (let i = 0; i < string.length; i++) { if (string.charAt(i) === '#') { - str += this.faker.datatype.number(9); + str += this.faker.number.int(9); } else if (string.charAt(i) === '?') { str += this.arrayElement(alpha); } else if (string.charAt(i) === '*') { str += this.faker.datatype.boolean() ? this.arrayElement(alpha) - : this.faker.datatype.number(9); + : this.faker.number.int(9); } else { str += string.charAt(i); } @@ -197,7 +197,7 @@ export class HelpersModule { max = min; min = tmp; } - repetitions = this.faker.datatype.number({ min: min, max: max }); + repetitions = this.faker.number.int({ min, max }); string = string.slice(0, token.index) + token[1].repeat(repetitions) + @@ -229,7 +229,7 @@ export class HelpersModule { } string = string.slice(0, token.index) + - this.faker.datatype.number({ min: min, max: max }).toString() + + this.faker.number.int({ min, max }).toString() + string.slice(token.index + token[0].length); token = string.match(RANGE_REG); } @@ -289,7 +289,7 @@ export class HelpersModule { } for (let i = list.length - 1; i > 0; --i) { - const j = this.faker.datatype.number(i); + const j = this.faker.number.int(i); [list[i], list[j]] = [list[j], list[i]]; } @@ -341,7 +341,7 @@ export class HelpersModule { * * @example * faker.helpers.mustache('I found {{count}} instances of "{{word}}".', { - * count: () => `${faker.datatype.number()}`, + * count: () => `${faker.number.int()}`, * word: "this word", * }) // 'I found 57591 instances of "this word".' * @@ -438,9 +438,7 @@ export class HelpersModule { array: ReadonlyArray = ['a', 'b', 'c'] as unknown as ReadonlyArray ): T { const index = - array.length > 1 - ? this.faker.datatype.number({ max: array.length - 1 }) - : 0; + array.length > 1 ? this.faker.number.int({ max: array.length - 1 }) : 0; return array[index]; } @@ -470,7 +468,7 @@ export class HelpersModule { count = array.length === 0 ? 0 - : this.faker.datatype.number({ min: 1, max: array.length }); + : this.faker.number.int({ min: 1, max: array.length }); } else if (count > array.length) { count = array.length; } else if (count < 0) { @@ -485,7 +483,7 @@ export class HelpersModule { while (i-- > min) { index = Math.floor( - (i + 1) * this.faker.datatype.float({ min: 0, max: 0.99 }) + (i + 1) * this.faker.number.float({ min: 0, max: 0.99 }) ); temp = arrayCopy[index]; arrayCopy[index] = arrayCopy[i]; @@ -644,7 +642,7 @@ export class HelpersModule { if (typeof numberOrRange === 'number') { return numberOrRange; } - return this.faker.datatype.number(numberOrRange); + return this.faker.number.int(numberOrRange); } /** diff --git a/src/modules/image/index.ts b/src/modules/image/index.ts index 8113c1f6b98..e0dd9d2212b 100644 --- a/src/modules/image/index.ts +++ b/src/modules/image/index.ts @@ -111,7 +111,7 @@ export class ImageModule { } if (randomize) { - url += `?lock=${this.faker.datatype.number()}`; + url += `?lock=${this.faker.number.int()}`; } return url; diff --git a/src/modules/image/providers/lorempixel.ts b/src/modules/image/providers/lorempixel.ts index a172517b609..f8bb81e5da1 100644 --- a/src/modules/image/providers/lorempixel.ts +++ b/src/modules/image/providers/lorempixel.ts @@ -60,7 +60,7 @@ export class Lorempixel { } if (randomize) { - url += `?${this.faker.datatype.number()}`; + url += `?${this.faker.number.int()}`; } return url; diff --git a/src/modules/internet/index.ts b/src/modules/internet/index.ts index 60e3ac55a46..a32045d9753 100644 --- a/src/modules/internet/index.ts +++ b/src/modules/internet/index.ts @@ -46,7 +46,7 @@ export class InternetModule { * @since 2.0.1 */ avatar(): string { - return `https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/${this.faker.datatype.number( + return `https://cloudflare-ipfs.com/ipfs/Qmd3W5DuhgHirLHGVixi6V76LhCkZUz6pnFt5AJBiyvHye/avatar/${this.faker.number.int( 1249 )}.jpg`; } @@ -140,9 +140,9 @@ export class InternetModule { let result: string; firstName = firstName || this.faker.person.firstName(); lastName = lastName || this.faker.person.lastName(); - switch (this.faker.datatype.number(2)) { + switch (this.faker.number.int(2)) { case 0: - result = `${firstName}${this.faker.datatype.number(99)}`; + result = `${firstName}${this.faker.number.int(99)}`; break; case 1: result = @@ -152,7 +152,7 @@ export class InternetModule { result = `${firstName}${this.faker.helpers.arrayElement([ '.', '_', - ])}${lastName}${this.faker.datatype.number(99)}`; + ])}${lastName}${this.faker.number.int(99)}`; break; } result = result.toString().replace(/'/g, ''); @@ -319,7 +319,7 @@ export class InternetModule { */ ipv4(): string { const randNum = () => { - return this.faker.datatype.number(255).toFixed(0); + return this.faker.number.int(255).toFixed(0); }; const result: string[] = []; @@ -380,7 +380,7 @@ export class InternetModule { * @since 5.4.0 */ port(): number { - return this.faker.datatype.number({ min: 0, max: 65535 }); + return this.faker.number.int(65535); } /** @@ -418,7 +418,7 @@ export class InternetModule { blueBase: number = 0 ): string { const colorFromBase = (base: number): string => - Math.floor((this.faker.datatype.number(256) + base) / 2) + Math.floor((this.faker.number.int(256) + base) / 2) .toString(16) .padStart(2, '0'); @@ -451,7 +451,7 @@ export class InternetModule { } for (i = 0; i < 12; i++) { - mac += this.faker.datatype.number(15).toString(16); + mac += this.faker.number.hex(15); if (i % 2 === 1 && i !== 11) { mac += validSep; } @@ -506,7 +506,7 @@ export class InternetModule { pattern = consonant; } } - const n = this.faker.datatype.number(94) + 33; + const n = this.faker.number.int(94) + 33; let char = String.fromCharCode(n); if (memorable) { char = char.toLowerCase(); diff --git a/src/modules/internet/user-agent.ts b/src/modules/internet/user-agent.ts index 972b25741e8..005e31e62d2 100644 --- a/src/modules/internet/user-agent.ts +++ b/src/modules/internet/user-agent.ts @@ -58,7 +58,7 @@ export function generate(faker: Faker): string { obj: T ): keyof T => { //returns a random key from the passed object; keys are weighted by the decimal probability in their value - const rand = faker.datatype.number({ min: 0, max: 100 }) / 100; + const rand = faker.number.int(100) / 100; let min = 0; let max = 0; let return_val: string; @@ -217,7 +217,7 @@ export function generate(faker: Faker): string { //generate a random revision //dots = 2 returns .x.y where x & y are between 0 and 9 for (let x = 0; x < dots; x++) { - return_val += `.${faker.datatype.number({ min: 0, max: 9 })}`; + return_val += `.${faker.string.numeric({ allowLeadingZeros: true })}`; } return return_val; }; @@ -225,53 +225,51 @@ export function generate(faker: Faker): string { const version_string = { net() { return [ - faker.datatype.number({ min: 1, max: 4 }), - faker.datatype.number({ min: 0, max: 9 }), - faker.datatype.number({ min: 10000, max: 99999 }), - faker.datatype.number({ min: 0, max: 9 }), + faker.number.int({ min: 1, max: 4 }), + faker.number.int(9), + faker.number.int({ min: 10000, max: 99999 }), + faker.number.int(9), ].join('.'); }, nt() { - return [ - faker.datatype.number({ min: 5, max: 6 }), - faker.datatype.number({ min: 0, max: 3 }), - ].join('.'); + return [faker.number.int({ min: 5, max: 6 }), faker.number.int(3)].join( + '.' + ); }, ie() { - return faker.datatype.number({ min: 7, max: 11 }); + return faker.number.int({ min: 7, max: 11 }); }, trident() { - return [ - faker.datatype.number({ min: 3, max: 7 }), - faker.datatype.number({ min: 0, max: 1 }), - ].join('.'); + return [faker.number.int({ min: 3, max: 7 }), faker.number.int(1)].join( + '.' + ); }, osx(delim?: string) { return [ 10, - faker.datatype.number({ min: 5, max: 10 }), - faker.datatype.number({ min: 0, max: 9 }), + faker.number.int({ min: 5, max: 10 }), + faker.number.int(9), ].join(delim || '.'); }, chrome() { return [ - faker.datatype.number({ min: 13, max: 39 }), + faker.number.int({ min: 13, max: 39 }), 0, - faker.datatype.number({ min: 800, max: 899 }), + faker.number.int({ min: 800, max: 899 }), 0, ].join('.'); }, presto() { - return `2.9.${faker.datatype.number({ min: 160, max: 190 })}`; + return `2.9.${faker.number.int({ min: 160, max: 190 })}`; }, presto2() { - return `${faker.datatype.number({ min: 10, max: 12 })}.00`; + return `${faker.number.int({ min: 10, max: 12 })}.00`; }, safari() { return [ - faker.datatype.number({ min: 531, max: 538 }), - faker.datatype.number({ min: 0, max: 2 }), - faker.datatype.number({ min: 0, max: 2 }), + faker.number.int({ min: 531, max: 538 }), + faker.number.int(2), + faker.number.int(2), ].join('.'); }, }; @@ -279,7 +277,7 @@ export function generate(faker: Faker): string { const browserMap = { firefox(arch: OS): string { //https://developer.mozilla.org/en-US/docs/Gecko_user_agent_string_reference - const firefox_ver = `${faker.datatype.number({ + const firefox_ver = `${faker.number.int({ min: 5, max: 15, })}${randomRevision(2)}`, @@ -303,7 +301,7 @@ export function generate(faker: Faker): string { if (ver >= 11) { //http://msdn.microsoft.com/en-us/library/ie/hh869301(v=vs.85).aspx - return `Mozilla/5.0 (Windows NT 6.${faker.datatype.number({ + return `Mozilla/5.0 (Windows NT 6.${faker.number.int({ min: 1, max: 3, })}; Trident/7.0; ${ @@ -327,29 +325,23 @@ export function generate(faker: Faker): string { ? `(X11; Linux ${randomProc(arch)}; U; ${randomLang()}${presto_ver}` : `(Macintosh; Intel Mac OS X ${version_string.osx()} U; ${randomLang()} Presto/${version_string.presto()} Version/${version_string.presto2()})`; - return `Opera/${faker.datatype.number({ + return `Opera/${faker.number.int({ min: 9, max: 14, - })}.${faker.datatype.number({ - min: 0, - max: 99, - })} ${os_ver}`; + })}.${faker.number.int(99)} ${os_ver}`; }, safari(arch: OS): string { const safari = version_string.safari(), - ver = `${faker.datatype.number({ + ver = `${faker.number.int({ min: 4, max: 7, - })}.${faker.datatype.number({ - min: 0, - max: 1, - })}.${faker.datatype.number({ min: 0, max: 10 })}`, + })}.${faker.number.int(1)}.${faker.number.int(10)}`, os_ver = arch === 'mac' ? `(Macintosh; ${randomProc('mac')} Mac OS X ${version_string.osx( '_' - )} rv:${faker.datatype.number({ + )} rv:${faker.number.int({ min: 2, max: 6, })}.0; ${randomLang()}) ` diff --git a/src/modules/location/index.ts b/src/modules/location/index.ts index fcb8aabd2ae..a4e6ef9e958 100644 --- a/src/modules/location/index.ts +++ b/src/modules/location/index.ts @@ -60,7 +60,7 @@ export class LocationModule { zipCodeByState(state: string): string { const zipRange = this.faker.definitions.location.postcode_by_state?.[state]; if (zipRange) { - return String(this.faker.datatype.number(zipRange)); + return String(this.faker.number.int(zipRange)); } return this.zipCode(); } @@ -269,11 +269,7 @@ export class LocationModule { */ // TODO @xDivisionByZerox 2022-06-12 this signature should probably be an object for easier maintainability latitude(max: number = 90, min: number = -90, precision: number = 4): number { - return this.faker.datatype.number({ - min, - max, - precision: parseFloat(`${(0.0).toPrecision(precision)}1`), - }); + return this.faker.number.float({ min, max, precision: 10 ** -precision }); } /** @@ -295,11 +291,7 @@ export class LocationModule { min: number = -180, precision: number = 4 ): number { - return this.faker.datatype.number({ - max: max, - min: min, - precision: parseFloat(`${(0.0).toPrecision(precision)}1`), - }); + return this.faker.number.float({ max, min, precision: 10 ** -precision }); } /** @@ -399,8 +391,7 @@ export class LocationModule { return [this.latitude(), this.longitude()]; } - const angleRadians = this.faker.datatype.float({ - min: 0, + const angleRadians = this.faker.number.float({ max: 2 * Math.PI, precision: 0.00001, }); // in ° radians @@ -408,8 +399,7 @@ export class LocationModule { const radiusMetric = isMetric ? radius : radius * 1.60934; // in km const errorCorrection = 0.995; // avoid float issues const distanceInKm = - this.faker.datatype.float({ - min: 0, + this.faker.number.float({ max: radiusMetric, precision: 0.001, }) * errorCorrection; // in km diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts new file mode 100644 index 00000000000..c30d50153f4 --- /dev/null +++ b/src/modules/number/index.ts @@ -0,0 +1,197 @@ +import type { Faker } from '../..'; +import { FakerError } from '../../errors/faker-error'; +import type { Mersenne } from '../../internal/mersenne/mersenne'; + +/** + * Module to generate numbers of any kind. + */ +export class NumberModule { + constructor(private readonly faker: Faker) { + // Bind `this` so namespaced is working correctly + for (const name of Object.getOwnPropertyNames(NumberModule.prototype)) { + if (name === 'constructor' || typeof this[name] !== 'function') { + continue; + } + this[name] = this[name].bind(this); + } + } + + /** + * Returns a single random integer between zero and the given max value or the given range. + * The bounds are inclusive. + * + * @param options Maximum value or options object. Defaults to `{}`. + * @param options.min Lower bound for generated number. Defaults to `0`. + * @param options.max Upper bound for generated number. Defaults to `min + 99999`. + * + * @throws When options define `max < min`. + * + * @example + * faker.number.int() // 55422 + * faker.number.int(100) // 52 + * faker.number.int({ min: 1000000 }) // 1031433 + * faker.number.int({ max: 100 }) // 42 + * faker.number.int({ min: 10, max: 100 }) // 57 + * + * @since 8.0.0 + */ + int(options: number | { min?: number; max?: number } = {}): number { + if (typeof options === 'number') { + options = { max: options }; + } + + const { min = 0, max = min + 99999 } = options; + + if (max === min) { + return min; + } + + if (max < min) { + throw new FakerError(`Max ${max} should be greater than min ${min}.`); + } + + const mersenne: Mersenne = + // @ts-expect-error: access private member field + this.faker._mersenne; + + return mersenne.next({ min, max: max + 1 }); + } + + /** + * Returns a single random floating-point number for a given precision or range and precision. + * + * @param options Precision or options object. Defaults to `{}`. + * @param options.min Lower bound for generated number. Defaults to `0`. + * @param options.max Upper bound for generated number. Defaults to `99999`. + * @param options.precision Precision of the generated number. Defaults to `0.01`. + * + * @example + * faker.number.float() // 51696.36 + * faker.number.float(1) // 52023.2 + * faker.number.float({ min: 1000000 }) // 212859.76 + * faker.number.float({ max: 100 }) // 28.11 + * faker.number.float({ precision: 0.1 }) // 84055.3 + * faker.number.float({ min: 10, max: 100, precision: 0.001 }) // 57.315 + * + * @since 8.0.0 + */ + float( + options: number | { min?: number; max?: number; precision?: number } = {} + ): number { + if (typeof options === 'number') { + options = { + precision: options, + }; + } + + const { min = 0, max = min + 99999, precision = 0.01 } = options; + + if (max === min) { + return min; + } + + if (max < min) { + throw new FakerError(`Max ${max} should be greater than min ${min}.`); + } + + const factor = 1 / precision; + const int = this.int({ + min: min * factor, + max: max * factor, + }); + + return int / factor; + } + + /** + * Returns a lowercase [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) number. + * + * @param options Maximum value or options object. Defaults to `{}`. + * @param options.min Lower bound for generated number. Defaults to `0`. + * @param options.max Upper bound for generated number. Defaults to `min + 16`. + * + * @example + * faker.number.hex() // 'b' + * faker.number.hex(16) // '9' + * faker.number.hex({ min: 0, max: 65536 }) // 'af17' + * + * @since 8.0.0 + */ + hex(options: number | { min?: number; max?: number } = {}): string { + if (typeof options === 'number') { + options = { max: options }; + } + + const { min = 0, max = min + 16 } = options; + + return this.int({ + max, + min, + }).toString(16); + } + + /** + * Returns a [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type) number. + * + * @param options Maximum value or options object. Defaults to `{}`. + * @param options.min Lower bound for generated bigint. Defaults to `0n`. + * @param options.max Upper bound for generated bigint. Defaults to `min + 999999999999999n`. + * + * @throws When options define `max < min`. + * + * @example + * faker.number.bigInt() // 55422n + * faker.number.bigInt(100n) // 52n + * faker.number.bigInt({ min: 1000000n }) // 431433n + * faker.number.bigInt({ max: 100n }) // 42n + * faker.number.bigInt({ min: 10n, max: 100n }) // 36n + * + * @since 8.0.0 + */ + bigInt( + options: + | bigint + | number + | string + | boolean + | { + min?: bigint | number | string | boolean; + max?: bigint | number | string | boolean; + } = {} + ): bigint { + if ( + typeof options === 'bigint' || + typeof options === 'number' || + typeof options === 'string' || + typeof options === 'boolean' + ) { + options = { + max: options, + }; + } + + const min = BigInt(options.min ?? 0); + const max = BigInt(options.max ?? min + BigInt(999999999999999)); + + if (max === min) { + return min; + } + + if (max < min) { + throw new FakerError(`Max ${max} should be larger then min ${min}.`); + } + + const delta = max - min; + + const offset = + BigInt( + this.faker.string.numeric({ + length: delta.toString(10).length, + allowLeadingZeros: true, + }) + ) % + (delta + BigInt(1)); + + return min + offset; + } +} diff --git a/src/modules/random/index.ts b/src/modules/random/index.ts index cb81935b2d4..21e46509bc4 100644 --- a/src/modules/random/index.ts +++ b/src/modules/random/index.ts @@ -151,7 +151,7 @@ export class RandomModule { const words: string[] = []; if (count == null) { - count = this.faker.datatype.number({ min: 1, max: 3 }); + count = this.faker.number.int({ min: 1, max: 3 }); } for (let i = 0; i < count; i++) { diff --git a/src/modules/string/index.ts b/src/modules/string/index.ts index f1a13c1d073..de1ec446ed4 100644 --- a/src/modules/string/index.ts +++ b/src/modules/string/index.ts @@ -79,7 +79,7 @@ export type NumericChar = export type AlphaChar = LowerAlphaChar | UpperAlphaChar; export type AlphaNumericChar = AlphaChar | NumericChar; -const SAMPLE_MAX_LENGTH = Math.pow(2, 20); +const SAMPLE_MAX_LENGTH = 2 ** 20; /** * Module to generate string related entries. @@ -416,7 +416,7 @@ export class StringModule { while (returnString.length < length) { returnString += String.fromCharCode( - this.faker.datatype.number(charCodeOption) + this.faker.number.int(charCodeOption) ); } @@ -434,7 +434,7 @@ export class StringModule { uuid(): string { const RFC4122_TEMPLATE = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'; const replacePlaceholders = (placeholder: string) => { - const random = this.faker.datatype.number({ min: 0, max: 15 }); + const random = this.faker.number.int(15); const value = placeholder === 'x' ? random : (random & 0x3) | 0x8; return value.toString(16); }; diff --git a/src/modules/system/index.ts b/src/modules/system/index.ts index 12bb25ba6b7..4bf28affc58 100644 --- a/src/modules/system/index.ts +++ b/src/modules/system/index.ts @@ -228,9 +228,9 @@ export class SystemModule { */ semver(): string { return [ - this.faker.datatype.number(9), - this.faker.datatype.number(9), - this.faker.datatype.number(9), + this.faker.number.int(9), + this.faker.number.int(9), + this.faker.number.int(9), ].join('.'); } @@ -262,35 +262,24 @@ export class SystemModule { let suffix: string; let prefix = ''; + const digit = () => this.faker.string.numeric({ allowLeadingZeros: true }); switch (interfaceSchema) { case 'index': - suffix = this.faker.datatype.number(9).toString(); + suffix = digit(); break; case 'slot': - suffix = `${this.faker.datatype.number(9)}${ - this.faker.helpers.maybe(() => `f${this.faker.datatype.number(9)}`) ?? - '' - }${ - this.faker.helpers.maybe(() => `d${this.faker.datatype.number(9)}`) ?? - '' - }`; + suffix = `${digit()}${ + this.faker.helpers.maybe(() => `f${digit()}`) ?? '' + }${this.faker.helpers.maybe(() => `d${digit()}`) ?? ''}`; break; case 'mac': suffix = this.faker.internet.mac(''); break; case 'pci': - prefix = - this.faker.helpers.maybe(() => `P${this.faker.datatype.number(9)}`) ?? - ''; - suffix = `${this.faker.datatype.number(9)}s${this.faker.datatype.number( - 9 - )}${ - this.faker.helpers.maybe(() => `f${this.faker.datatype.number(9)}`) ?? - '' - }${ - this.faker.helpers.maybe(() => `d${this.faker.datatype.number(9)}`) ?? - '' - }`; + prefix = this.faker.helpers.maybe(() => `P${digit()}`) ?? ''; + suffix = `${digit()}s${digit()}${ + this.faker.helpers.maybe(() => `f${digit()}`) ?? '' + }${this.faker.helpers.maybe(() => `d${digit()}`) ?? ''}`; break; } @@ -322,17 +311,17 @@ export class SystemModule { const { includeYear = false, includeNonStandard = false } = options; // create the arrays to hold the available values for each component of the expression - const minutes = [this.faker.datatype.number({ min: 0, max: 59 }), '*']; - const hours = [this.faker.datatype.number({ min: 0, max: 23 }), '*']; - const days = [this.faker.datatype.number({ min: 1, max: 31 }), '*', '?']; - const months = [this.faker.datatype.number({ min: 1, max: 12 }), '*']; + const minutes = [this.faker.number.int(59), '*']; + const hours = [this.faker.number.int(23), '*']; + const days = [this.faker.number.int({ min: 1, max: 31 }), '*', '?']; + const months = [this.faker.number.int({ min: 1, max: 12 }), '*']; const daysOfWeek = [ - this.faker.datatype.number({ min: 0, max: 6 }), + this.faker.number.int(6), this.faker.helpers.arrayElement(CRON_DAY_OF_WEEK), '*', '?', ]; - const years = [this.faker.datatype.number({ min: 1970, max: 2099 }), '*']; + const years = [this.faker.number.int({ min: 1970, max: 2099 }), '*']; const minute = this.faker.helpers.arrayElement(minutes); const hour = this.faker.helpers.arrayElement(hours); diff --git a/src/modules/vehicle/index.ts b/src/modules/vehicle/index.ts index e6bdaeb3f97..dd08b75fb0c 100644 --- a/src/modules/vehicle/index.ts +++ b/src/modules/vehicle/index.ts @@ -100,8 +100,7 @@ export class VehicleModule { length: 1, casing: 'upper', exclude, - })}${this.faker.datatype.number({ min: 10000, max: 99999 })}` // return five digit # - .toUpperCase(); + })}${this.faker.number.int({ min: 10000, max: 99999 })}`; // return five digit # } /** @@ -128,12 +127,9 @@ export class VehicleModule { return `${this.faker.string.alpha({ length: 2, casing: 'upper', - })}${this.faker.datatype.number({ - min: 0, - max: 9, - })}${this.faker.datatype.number({ - min: 0, - max: 9, + })}${this.faker.string.numeric({ + length: 2, + allowLeadingZeros: true, })}${this.faker.string.alpha({ length: 3, casing: 'upper', diff --git a/test/__snapshots__/number.spec.ts.snap b/test/__snapshots__/number.spec.ts.snap new file mode 100644 index 00000000000..e89a2a0ed15 --- /dev/null +++ b/test/__snapshots__/number.spec.ts.snap @@ -0,0 +1,109 @@ +// Vitest Snapshot v1 + +exports[`number > 42 > bigInt > noArgs 1`] = `379177551410048n`; + +exports[`number > 42 > bigInt > with big options 1`] = `17723424219505212239212136n`; + +exports[`number > 42 > bigInt > with bigint value 1`] = `7n`; + +exports[`number > 42 > bigInt > with boolean value 1`] = `1n`; + +exports[`number > 42 > bigInt > with number value 1`] = `37n`; + +exports[`number > 42 > bigInt > with options 1`] = `1n`; + +exports[`number > 42 > bigInt > with string value 1`] = `37n`; + +exports[`number > 42 > float > with max 1`] = `25.84`; + +exports[`number > 42 > float > with min 1`] = `37411.64`; + +exports[`number > 42 > float > with min and max 1`] = `-0.43`; + +exports[`number > 42 > float > with min, max and precision 1`] = `-0.4261`; + +exports[`number > 42 > float > with plain number 1`] = `37453.636891`; + +exports[`number > 42 > hex > noArgs 1`] = `"6"`; + +exports[`number > 42 > hex > with options 1`] = `"4"`; + +exports[`number > 42 > hex > with value 1`] = `"0"`; + +exports[`number > 42 > int > noArgs 1`] = `37454`; + +exports[`number > 42 > int > with options 1`] = `4`; + +exports[`number > 42 > int > with value 1`] = `0`; + +exports[`number > 1211 > bigInt > noArgs 1`] = `948721906162743n`; + +exports[`number > 1211 > bigInt > with big options 1`] = `22017767508700414061739128n`; + +exports[`number > 1211 > bigInt > with bigint value 1`] = `80n`; + +exports[`number > 1211 > bigInt > with boolean value 1`] = `1n`; + +exports[`number > 1211 > bigInt > with number value 1`] = `8n`; + +exports[`number > 1211 > bigInt > with options 1`] = `10n`; + +exports[`number > 1211 > bigInt > with string value 1`] = `24n`; + +exports[`number > 1211 > float > with max 1`] = `64.07`; + +exports[`number > 1211 > float > with min 1`] = `92809.09`; + +exports[`number > 1211 > float > with min and max 1`] = `61.07`; + +exports[`number > 1211 > float > with min, max and precision 1`] = `61.0658`; + +exports[`number > 1211 > float > with plain number 1`] = `92851.086855`; + +exports[`number > 1211 > hex > noArgs 1`] = `"f"`; + +exports[`number > 1211 > hex > with options 1`] = `"a"`; + +exports[`number > 1211 > hex > with value 1`] = `"1"`; + +exports[`number > 1211 > int > noArgs 1`] = `92852`; + +exports[`number > 1211 > int > with options 1`] = `10`; + +exports[`number > 1211 > int > with value 1`] = `1`; + +exports[`number > 1337 > bigInt > noArgs 1`] = `251225403255239n`; + +exports[`number > 1337 > bigInt > with big options 1`] = `31258255497061442772623668n`; + +exports[`number > 1337 > bigInt > with bigint value 1`] = `3n`; + +exports[`number > 1337 > bigInt > with boolean value 1`] = `0n`; + +exports[`number > 1337 > bigInt > with number value 1`] = `25n`; + +exports[`number > 1337 > bigInt > with options 1`] = `-15n`; + +exports[`number > 1337 > bigInt > with string value 1`] = `25n`; + +exports[`number > 1337 > float > with max 1`] = `18.08`; + +exports[`number > 1337 > float > with min 1`] = `26160.2`; + +exports[`number > 1337 > float > with min and max 1`] = `-12.92`; + +exports[`number > 1337 > float > with min, max and precision 1`] = `-12.9153`; + +exports[`number > 1337 > float > with plain number 1`] = `26202.205595`; + +exports[`number > 1337 > hex > noArgs 1`] = `"4"`; + +exports[`number > 1337 > hex > with options 1`] = `"2"`; + +exports[`number > 1337 > hex > with value 1`] = `"0"`; + +exports[`number > 1337 > int > noArgs 1`] = `26202`; + +exports[`number > 1337 > int > with options 1`] = `2`; + +exports[`number > 1337 > int > with value 1`] = `0`; diff --git a/test/datatype.spec.ts b/test/datatype.spec.ts index bd675e9a6ec..5571f3b9b02 100644 --- a/test/datatype.spec.ts +++ b/test/datatype.spec.ts @@ -157,19 +157,37 @@ describe('datatype', () => { expect(foundNegative5).toBeTruthy(); }); - it('provides numbers with a given precision', () => { - const options = { min: 0, max: 1.5, precision: 0.5 }; + it('provides numbers with a given precision of 0.5 steps', () => { const results = Array.from( new Set( - Array.from({ length: 50 }, () => faker.datatype.number(options)) + Array.from({ length: 50 }, () => + faker.datatype.float({ + min: 0, + max: 1.5, + precision: 0.5, + }) + ) ) ).sort(); - expect(results).toContain(0.5); - expect(results).toContain(1.0); + expect(results).toEqual([0, 0.5, 1, 1.5]); + }); - expect(results[0]).toBe(0); - expect(results[results.length - 1]).toBe(1.5); + // TODO @Shinigami92 2022-11-24: https://github.com/faker-js/faker/issues/1595 + it.todo('provides numbers with a given precision of 0.4 steps', () => { + const results = Array.from( + new Set( + Array.from({ length: 50 }, () => + faker.datatype.float({ + min: 0, + max: 1.9, + precision: 0.4, + }) + ) + ) + ).sort(); + + expect(results).toEqual([0, 0.4, 0.8, 1.2, 1.6]); }); it('provides numbers with a with exact precision', () => { @@ -259,11 +277,7 @@ describe('datatype', () => { ) ).sort(); - expect(results).toContain(0.5); - expect(results).toContain(1.0); - - expect(results[0]).toBe(0); - expect(results[results.length - 1]).toBe(1.5); + expect(results).toEqual([0, 0.5, 1, 1.5]); }); it('provides numbers with a with exact precision', () => { diff --git a/test/faker.spec.ts b/test/faker.spec.ts index dac1be5a1e9..d418f19bdfa 100644 --- a/test/faker.spec.ts +++ b/test/faker.spec.ts @@ -115,15 +115,15 @@ describe('faker', () => { it('should reset the sequence when calling `seed`', () => { const seed = faker.seed(); - const num1 = faker.datatype.number(); + const num1 = faker.number.int(); const newSeed = faker.seed(seed); - const num2 = faker.datatype.number(); + const num2 = faker.number.int(); expect(num1).toBe(num2); expect(newSeed).toBe(seed); - const num3 = faker.datatype.number(); + const num3 = faker.number.int(); expect(num1).not.toBe(num3); }); diff --git a/test/finance.spec.ts b/test/finance.spec.ts index 730bfe788e8..7c90a62a1ac 100644 --- a/test/finance.spec.ts +++ b/test/finance.spec.ts @@ -130,7 +130,7 @@ describe('finance', () => { }); it('should set a specified length', () => { - let expected = faker.datatype.number(20); + let expected = faker.number.int(20); expected = expected || 4; diff --git a/test/helpers.spec.ts b/test/helpers.spec.ts index 412dff30982..c56d2589848 100644 --- a/test/helpers.spec.ts +++ b/test/helpers.spec.ts @@ -109,8 +109,8 @@ describe('helpers', () => { t.describe('unique', (t) => { t.it('with customMethod', customUniqueMethod) .it('with customMethod and args', customUniqueMethod, ['prefix-1-']) - .it('with () => number', faker.datatype.number) - .it('with () => number and args', faker.datatype.number, [50]); + .it('with () => number', faker.number.int) + .it('with () => number and args', faker.number.int, [50]); }); }); @@ -384,7 +384,7 @@ describe('helpers', () => { }); it('definition array returns unique array', () => { - const length = faker.datatype.number({ min: 1, max: 6 }); + const length = faker.number.int({ min: 1, max: 6 }); const unique = faker.helpers.uniqueArray( faker.definitions.hacker.noun, length @@ -394,7 +394,7 @@ describe('helpers', () => { }); it('function returns unique array', () => { - const length = faker.datatype.number({ min: 1, max: 6 }); + const length = faker.number.int({ min: 1, max: 6 }); const unique = faker.helpers.uniqueArray(faker.lorem.word, length); expect(unique).not.toContainDuplicates(); expect(unique).toHaveLength(length); @@ -402,7 +402,7 @@ describe('helpers', () => { it('empty array returns empty array', () => { const input = []; - const length = faker.datatype.number({ min: 1, max: 6 }); + const length = faker.number.int({ min: 1, max: 6 }); const unique = faker.helpers.uniqueArray(input, length); expect(unique).toHaveLength(0); }); diff --git a/test/number.spec.ts b/test/number.spec.ts new file mode 100644 index 00000000000..5ba1a0a4a3d --- /dev/null +++ b/test/number.spec.ts @@ -0,0 +1,354 @@ +import validator from 'validator'; +import { afterEach, describe, expect, it } from 'vitest'; +import { faker, FakerError } from '../src'; +import { seededTests } from './support/seededRuns'; + +describe('number', () => { + afterEach(() => { + faker.locale = 'en'; + }); + + seededTests(faker, 'number', (t) => { + t.describeEach( + 'int', + 'hex' + )((t) => { + t.it('noArgs') + .it('with value', 1) + .it('with options', { min: 0, max: 10 }); + }); + + t.describe('float', (t) => { + t.it('with plain number', 0.000001) + .it('with min', { min: -42 }) + .it('with max', { max: 69 }) + .it('with min and max', { min: -42, max: 69 }) + .it('with min, max and precision', { + min: -42, + max: 69, + precision: 0.0001, + }); + }); + + t.describe('bigInt', (t) => { + t.it('noArgs') + .it('with number value', 42) + .it('with string value', '69') + .it('with boolean value', true) + .it('with bigint value', 123n) + .it('with options', { min: -42, max: 69 }) + .it('with big options', { + min: 6135715171537515454317351n, + max: 32465761264574654845432354n, + }); + }); + }); + + describe(`random seeded tests for seed ${faker.seed()}`, () => { + describe('int', () => { + it('should return a random number given a maximum value as Number', () => { + const actual = faker.number.int(10); + + expect(actual).toBeGreaterThanOrEqual(0); + expect(actual).toBeLessThanOrEqual(10); + }); + + it('should return a random number given a maximum value as Object', () => { + const actual = faker.number.int({ max: 10 }); + + expect(actual).toBeGreaterThanOrEqual(0); + expect(actual).toBeLessThanOrEqual(10); + }); + + it('should return a random number given a maximum value of 0', () => { + const actual = faker.number.int({ max: 0 }); + + expect(actual).toBe(0); + }); + + it('should return a random number given a negative number minimum and maximum value of 0', () => { + const actual = faker.number.int({ min: -100, max: 0 }); + + expect(actual).toBeGreaterThanOrEqual(-100); + expect(actual).toBeLessThanOrEqual(0); + }); + + it('should return a random number between a range', () => { + for (let i = 0; i < 100; i++) { + const actual = faker.number.int({ min: 22, max: 33 }); + expect(actual).toBeGreaterThanOrEqual(22); + expect(actual).toBeLessThanOrEqual(33); + } + }); + + it('should return inclusive negative max value', () => { + let foundNegative4 = false; + let foundNegative5 = false; + + for (let iter = 0; iter < 1000; iter++) { + const actual = faker.number.int({ min: -5, max: -4 }); + + if (actual === -4) { + foundNegative4 = true; + } else if (actual === -5) { + foundNegative5 = true; + } + + expect(actual).toBeGreaterThanOrEqual(-5); + expect(actual).toBeLessThanOrEqual(-4); + + if (foundNegative4 && foundNegative5) { + break; + } + } + + expect(foundNegative4).toBeTruthy(); + expect(foundNegative5).toBeTruthy(); + }); + + it('should not mutate the input object', () => { + const input: { + min?: number; + max?: number; + precision?: number; + otherProperty: string; + } = Object.freeze({ + min: 1, + precision: 1, + otherProperty: 'hello darkness my old friend', + }); + + expect(() => faker.number.int(input)).not.toThrow(); + }); + + it('should throw when min > max', () => { + const min = 10; + const max = 9; + + expect(() => { + faker.number.int({ min, max }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); + + // TODO @Shinigami92 2022-11-24: https://github.com/faker-js/faker/issues/1595 + it.todo( + 'should throw when there is no integer between min and max', + () => { + expect(() => { + faker.number.int({ min: 2.1, max: 2.9 }); + }).toThrow(); + } + ); + }); + + describe('float', () => { + it('should return a random float with a default precision of 2 digits after floating point', () => { + const number = faker.number.float(); + expect(number).toBe(Number(number.toFixed(2))); + }); + + it('should return a random float given a precision value', () => { + const number = faker.number.float(0.001); + expect(number).toBe(Number(number.toFixed(3))); + }); + + it('should return a random number given a max value of 10', () => { + const float = faker.number.float({ max: 10 }); + expect(float).toBeGreaterThanOrEqual(0); + expect(float).toBeLessThanOrEqual(10); + }); + + it('should return 0 given a max value of 0', () => { + expect(faker.number.float({ max: 0 })).toBe(0); + }); + + it('should return a random number given a negative number min and max value of 0', () => { + const float = faker.number.float({ min: -100, max: 0 }); + expect(float).toBeGreaterThanOrEqual(-100); + expect(float).toBeLessThanOrEqual(0); + }); + + it('should return a random number between a range', () => { + for (let i = 0; i < 5; i++) { + const randomNumber = faker.number.float({ min: 22, max: 33 }); + expect(randomNumber).toBeGreaterThanOrEqual(22); + expect(randomNumber).toBeLessThanOrEqual(33); + } + }); + + it('provides numbers with a given precision of 0.5 steps', () => { + const results = Array.from( + new Set( + Array.from({ length: 50 }, () => + faker.number.float({ + min: 0, + max: 1.5, + precision: 0.5, + }) + ) + ) + ).sort(); + + expect(results).toEqual([0, 0.5, 1, 1.5]); + }); + + // TODO @Shinigami92 2022-11-24: https://github.com/faker-js/faker/issues/1595 + it.todo('provides numbers with a given precision of 0.4 steps', () => { + const results = Array.from( + new Set( + Array.from({ length: 50 }, () => + faker.number.float({ + min: 0, + max: 1.9, + precision: 0.4, + }) + ) + ) + ).sort(); + + expect(results).toEqual([0, 0.4, 0.8, 1.2, 1.6]); + }); + + it('provides numbers with an exact precision', () => { + for (let i = 0; i < 100; i++) { + const number = faker.number.float({ + min: 0.5, + max: 0.99, + precision: 0.01, + }); + expect(number).toBe(Number(number.toFixed(2))); + } + }); + + it('provides number with a precision 0', () => { + const float = faker.number.float({ precision: 0 }); + + expect(float).toBe(Math.floor(float)); + }); + + it('should not modify the input object', () => { + expect(() => + faker.number.float(Object.freeze({ min: 1, max: 2 })) + ).not.toThrow(); + }); + + it('should throw when min > max', () => { + const min = 10; + const max = 9; + + expect(() => { + faker.number.float({ min, max }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); + }); + + describe('hex', () => { + it('generates single hex character when no additional argument was provided', () => { + const hex = faker.number.hex(); + expect(hex).toBeTypeOf('string'); + expect(hex).toHaveLength(1); + expect(hex).toSatisfy(validator.isHexadecimal); + }); + + it('generates a random hex string', () => { + const hex = faker.number.hex(5); + expect(hex).toSatisfy(validator.isHexadecimal); + }); + + it('generates a random hex in a specific range', () => { + const hex = faker.number.hex({ min: 15, max: 255 }); + + const hexNum = parseInt(hex, 16); + expect(hexNum).toBeLessThanOrEqual(255); + expect(hexNum).greaterThanOrEqual(15); + }); + + it('should throw when min > max', () => { + const min = 10; + const max = 9; + + expect(() => { + faker.number.hex({ min, max }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); + }); + + describe('bigInt', () => { + it('should generate a bigInt value', () => { + const generateBigInt = faker.number.bigInt(); + expect(generateBigInt).toBeTypeOf('bigint'); + }); + + it('should generate a big bigInt value with low delta', () => { + const generateBigInt = faker.number.bigInt({ + min: 999999999n, + max: 1000000000n, + }); + expect(generateBigInt).toBeTypeOf('bigint'); + expect(generateBigInt).toBeGreaterThanOrEqual(999999999n); + expect(generateBigInt).toBeLessThanOrEqual(1000000000n); + }); + + it('should return a random bigint given a maximum value as BigInt', () => { + const generateBigInt = faker.number.bigInt(10n); + expect(generateBigInt).toBeGreaterThanOrEqual(0n); + expect(generateBigInt).toBeLessThanOrEqual(10n); + }); + + it('should return a random bigint given a maximum value as Object', () => { + const generateBigInt = faker.number.bigInt({ max: 10n }); + expect(generateBigInt).toBeGreaterThanOrEqual(0n); + expect(generateBigInt).toBeLessThanOrEqual(10n); + }); + + it('should return a random bigint given a maximum value of 0', () => { + expect(faker.number.bigInt({ max: 0n })).toBe(0n); + }); + + it('should return a random bigint given a negative bigint minimum and maximum value of 0', () => { + const generateBigInt = faker.number.bigInt({ min: -100n, max: 0n }); + expect(generateBigInt).toBeGreaterThanOrEqual(-100n); + expect(generateBigInt).toBeLessThanOrEqual(0n); + }); + + it('should return a random bigint between a range', () => { + const randomBigInt = faker.number.bigInt({ min: 22, max: 33 }); + expect(randomBigInt).toBeGreaterThanOrEqual(22); + expect(randomBigInt).toBeLessThanOrEqual(33); + }); + + it('should return a random bigint for a very large range', () => { + const randomBigInt = faker.number.bigInt({ + min: 0n, + max: 10000000000000000000000n, + }); + expect(randomBigInt).toBeGreaterThanOrEqual(0n); + expect(randomBigInt).toBeLessThanOrEqual(10000000000000000000000n); + }); + + it('should not mutate the input object', () => { + const input: { + min?: bigint; + max?: bigint; + otherProperty: string; + } = Object.freeze({ + min: 1n, + otherProperty: 'hello darkness my old friend', + }); + + expect(() => faker.number.bigInt(input)).not.toThrow(); + }); + + it('should throw when min > max', () => { + const min = 10000n; + const max = 999n; + + expect(() => { + faker.number.bigInt({ min, max }); + }).toThrowError( + new FakerError(`Max ${max} should be larger then min ${min}.`) + ); + }); + }); + }); +}); diff --git a/test/string.spec.ts b/test/string.spec.ts index 32dc2ddfefc..f3073c0fcfc 100644 --- a/test/string.spec.ts +++ b/test/string.spec.ts @@ -513,16 +513,16 @@ describe('string', () => { }); it('should return empty string if negative length is passed', () => { - const negativeValue = faker.datatype.number({ min: -1000, max: -1 }); + const negativeValue = faker.number.int({ min: -1000, max: -1 }); const generatedString = faker.string.sample(negativeValue); expect(generatedString).toBe(''); expect(generatedString).toHaveLength(0); }); it('should return string with length of 2^20 if bigger length value is passed', () => { - const overMaxValue = Math.pow(2, 28); + const overMaxValue = 2 ** 28; const generatedString = faker.string.sample(overMaxValue); - expect(generatedString).toHaveLength(Math.pow(2, 20)); + expect(generatedString).toHaveLength(2 ** 20); }); it('should return string with a specific length', () => {