From d1de8752960757e14fb56c9dbac45335121fef02 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 13:53:45 +0100 Subject: [PATCH 01/26] refactor(datatype): legacy float implementation --- src/modules/datatype/index.ts | 64 +++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts index 7410e149fa8..91df55daff4 100644 --- a/src/modules/datatype/index.ts +++ b/src/modules/datatype/index.ts @@ -1,6 +1,56 @@ import type { Faker } from '../..'; +import { FakerError } from '../..'; import { deprecated } from '../../internal/deprecated'; +/** + * The legcy implementation for datatype float. + * + * @param options An options object. + * @param options.min Lower bound for generated number. Defaults to `0`. + * @param options.max Upper bound for generated number. Defaults to `min + 99999`. + * @param options.getFaker A function to get a Faker instance. + * @param options.precision Precision of the generated number. Defaults to `0.01`. + * + */ +function legacyFloatImplementation(options: { + /** + * A function to get a Faker instance. + */ + getFaker: () => Faker; + /** + * Lower bound for generated number. + */ + min: number; + /** + * Upper bound for generated number. + */ + max: number; + /** + * Precision of the generated number. + */ + precision: number; +}) { + const { max, min, precision, getFaker } = options; + if (max === min) { + return min; + } + + if (max < min) { + throw new FakerError(`Max ${max} should be greater than min ${min}.`); + } + + if (precision <= 0) { + throw new FakerError(`Precision should be greater than 0.`); + } + + const factor = 1 / precision; + const int = getFaker().number.int({ + min: min * factor, + max: max * factor, + }); + return int / factor; +} + /** * Module to generate various primitive values and data types. */ @@ -81,7 +131,12 @@ export class DatatypeModule { const { min = 0, max = min + 99999, precision = 1 } = options; - return this.faker.number.float({ min, max, precision }); + return legacyFloatImplementation({ + getFaker: () => this.faker, + max, + min, + precision, + }); } /** @@ -145,7 +200,12 @@ export class DatatypeModule { const { min = 0, max = min + 99999, precision = 0.01 } = options; - return this.faker.number.float({ min, max, precision }); + return legacyFloatImplementation({ + getFaker: () => this.faker, + max, + min, + precision, + }); } /** From cb35103e5130c744d2584d90a0f87b35d88ae2a9 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 14:31:40 +0100 Subject: [PATCH 02/26] refactor(number): replace precision with fractionDigits --- src/modules/color/index.ts | 18 ++++---- src/modules/finance/index.ts | 2 +- src/modules/helpers/index.ts | 2 +- src/modules/location/index.ts | 8 ++-- src/modules/number/index.ts | 43 +++++++++---------- test/__snapshots__/color.spec.ts.snap | 30 ++++++------- test/__snapshots__/finance.spec.ts.snap | 18 ++++---- test/__snapshots__/location.spec.ts.snap | 54 ++++++++++++------------ test/__snapshots__/number.spec.ts.snap | 6 +-- test/number.spec.ts | 33 +++++++-------- 10 files changed, 105 insertions(+), 109 deletions(-) diff --git a/src/modules/color/index.ts b/src/modules/color/index.ts index 67fa9b9a1c3..4b10cb4c70a 100644 --- a/src/modules/color/index.ts +++ b/src/modules/color/index.ts @@ -379,7 +379,7 @@ export class ColorModule { color = Array.from({ length: 3 }, () => this.faker.number.int(255)); if (includeAlpha) { - color.push(this.faker.number.float({ precision: 0.01 })); + color.push(this.faker.number.float({ fractionDigits: 2 })); cssFunction = 'rgba'; } @@ -460,7 +460,7 @@ export class ColorModule { }): string | number[]; cmyk(options?: { format?: ColorFormat }): string | number[] { const color: string | number[] = Array.from({ length: 4 }, () => - this.faker.number.float({ precision: 0.01 }) + this.faker.number.float({ fractionDigits: 2 }) ); return toColorFormat(color, options?.format || 'decimal', 'cmyk'); } @@ -570,7 +570,7 @@ export class ColorModule { }): string | number[] { const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < (options?.includeAlpha ? 3 : 2); i++) { - hsl.push(this.faker.number.float({ precision: 0.01 })); + hsl.push(this.faker.number.float({ fractionDigits: 2 })); } return toColorFormat( @@ -676,7 +676,7 @@ export class ColorModule { }): string | number[] { const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < 2; i++) { - hsl.push(this.faker.number.float({ precision: 0.01 })); + hsl.push(this.faker.number.float({ fractionDigits: 2 })); } return toColorFormat(hsl, options?.format || 'decimal', 'hwb'); @@ -755,10 +755,10 @@ export class ColorModule { format?: ColorFormat; }): string | number[]; lab(options?: { format?: ColorFormat }): string | number[] { - const lab = [this.faker.number.float({ precision: 0.000001 })]; + const lab = [this.faker.number.float({ fractionDigits: 6 })]; for (let i = 0; i < 2; i++) { lab.push( - this.faker.number.float({ min: -100, max: 100, precision: 0.0001 }) + this.faker.number.float({ min: -100, max: 100, fractionDigits: 4 }) ); } @@ -850,9 +850,9 @@ export class ColorModule { format?: ColorFormat; }): string | number[]; lch(options?: { format?: ColorFormat }): string | number[] { - const lch = [this.faker.number.float({ precision: 0.000001 })]; + const lch = [this.faker.number.float({ fractionDigits: 6 })]; for (let i = 0; i < 2; i++) { - lch.push(this.faker.number.float({ max: 230, precision: 0.1 })); + lch.push(this.faker.number.float({ max: 230, fractionDigits: 1 })); } return toColorFormat(lch, options?.format || 'decimal', 'lch'); @@ -960,7 +960,7 @@ export class ColorModule { } const color = Array.from({ length: 3 }, () => - this.faker.number.float({ precision: 0.0001 }) + this.faker.number.float({ fractionDigits: 4 }) ); return toColorFormat( color, diff --git a/src/modules/finance/index.ts b/src/modules/finance/index.ts index 8db5bfcea99..018e814311a 100644 --- a/src/modules/finance/index.ts +++ b/src/modules/finance/index.ts @@ -543,7 +543,7 @@ export class FinanceModule { const randValue = this.faker.number.float({ max, min, - precision: 10 ** -dec, + fractionDigits: dec, }); let formattedString: string; diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index 55732f4ab99..3276b4e6366 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -544,7 +544,7 @@ export class HelpersModule { const random = this.faker.number.float({ min: 0, max: total, - precision: 1e-9, + fractionDigits: 9, }); let current = 0; for (const { weight, value } of array) { diff --git a/src/modules/location/index.ts b/src/modules/location/index.ts index 4ce28bcf7b1..f64b50b2295 100644 --- a/src/modules/location/index.ts +++ b/src/modules/location/index.ts @@ -476,7 +476,7 @@ export class LocationModule { const { max = 90, min = legacyMin, precision = legacyPrecision } = options; - return this.faker.number.float({ min, max, precision: 10 ** -precision }); + return this.faker.number.float({ min, max, fractionDigits: precision }); } /** @@ -629,7 +629,7 @@ export class LocationModule { const { max = 180, min = legacyMin, precision = legacyPrecision } = options; - return this.faker.number.float({ max, min, precision: 10 ** -precision }); + return this.faker.number.float({ max, min, fractionDigits: precision }); } /** @@ -873,7 +873,7 @@ export class LocationModule { const angleRadians = this.faker.number.float({ max: 2 * Math.PI, - precision: 0.00001, + fractionDigits: 5, }); // in ° radians const radiusMetric = isMetric ? radius : radius * 1.60934; // in km @@ -881,7 +881,7 @@ export class LocationModule { const distanceInKm = this.faker.number.float({ max: radiusMetric, - precision: 0.001, + fractionDigits: 3, }) * errorCorrection; // in km /** diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 3cd35f4061d..ba0852389dd 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -93,15 +93,18 @@ export class NumberModule { * @param options Upper bound or options object. Defaults to `{}`. * @param options.min Lower bound for generated number. Defaults to `0.0`. * @param options.max Upper bound for generated number. Defaults to `1.0`. - * @param options.precision Precision of the generated number, for example `0.01` will round to 2 decimal points. + * @param options.fractionDigits The number of digits to appear after the decimal point. Defaults to `16`. + * + * @throws If options.max is smaller than options.min. + * @throws If options.fractionDigits is negative. * * @example * faker.number.float() // 0.5688541042618454 * faker.number.float(3) // 2.367973240558058 * faker.number.float({ min: -1000000 }) //-780678.849672846 * faker.number.float({ max: 100 }) // 17.3687307164073 - * faker.number.float({ precision: 0.1 }) // 0.9 - * faker.number.float({ min: 10, max: 100, precision: 0.001 }) // 35.415 + * faker.number.float({ fractionDigits: 1 }) // 0.9 + * faker.number.float({ min: 10, max: 100, v: 3 }) // 35.415 * * @since 8.0.0 */ @@ -122,11 +125,11 @@ export class NumberModule { */ max?: number; /** - * Precision of the generated number. + * The number of digits to appear after the decimal point. * - * @default 0.01 + * @default 16 */ - precision?: number; + fractionDigits?: number; } = {} ): number { if (typeof options === 'number') { @@ -135,7 +138,7 @@ export class NumberModule { }; } - const { min = 0, max = 1, precision } = options; + const { min = 0, max = 1, fractionDigits = 16 } = options; if (max === min) { return min; @@ -145,23 +148,17 @@ export class NumberModule { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if (precision !== undefined) { - if (precision <= 0) { - throw new FakerError(`Precision should be greater than 0.`); - } - - const factor = 1 / precision; - const int = this.int({ - min: min * factor, - max: max * factor, - }); - return int / factor; - } else { - // @ts-expect-error: access private member field - const mersenne: Mersenne = this.faker._mersenne; - const real = mersenne.next(); - return real * (max - min) + min; + if (fractionDigits < 0) { + throw new FakerError( + 'The fractional digits count should be greater than 0.' + ); } + + // @ts-expect-error: access private member field + const mersenne: Mersenne = this.faker._mersenne; + const real = mersenne.next() * (max - min) + min; + + return parseFloat(real.toFixed(fractionDigits)); } /** diff --git a/test/__snapshots__/color.spec.ts.snap b/test/__snapshots__/color.spec.ts.snap index 36e50b80cc4..bc2f4d8dafd 100644 --- a/test/__snapshots__/color.spec.ts.snap +++ b/test/__snapshots__/color.spec.ts.snap @@ -4,7 +4,7 @@ exports[`color > 42 > cmyk 1`] = ` [ 0.37, 0.8, - 0.96, + 0.95, 0.18, ] `; @@ -12,8 +12,8 @@ exports[`color > 42 > cmyk 1`] = ` exports[`color > 42 > colorByCSSColorSpace 1`] = ` [ 0.3745, - 0.7966, - 0.9508, + 0.7965, + 0.9507, ] `; @@ -25,7 +25,7 @@ exports[`color > 42 > hsl 1`] = ` [ 135, 0.8, - 0.96, + 0.95, ] `; @@ -35,7 +35,7 @@ exports[`color > 42 > hwb 1`] = ` [ 135, 0.8, - 0.96, + 0.95, ] `; @@ -63,14 +63,14 @@ exports[`color > 1211 > cmyk 1`] = ` [ 0.93, 0.46, - 0.9, + 0.89, 0.78, ] `; exports[`color > 1211 > colorByCSSColorSpace 1`] = ` [ - 0.9286, + 0.9285, 0.459, 0.8935, ] @@ -84,7 +84,7 @@ exports[`color > 1211 > hsl 1`] = ` [ 335, 0.46, - 0.9, + 0.89, ] `; @@ -94,21 +94,21 @@ exports[`color > 1211 > hwb 1`] = ` [ 335, 0.46, - 0.9, + 0.89, ] `; exports[`color > 1211 > lab 1`] = ` [ - 0.928521, + 0.92852, -8.197, - 78.6944, + 78.6943, ] `; exports[`color > 1211 > lch 1`] = ` [ - 0.928521, + 0.92852, 105.6, 205.5, ] @@ -131,7 +131,7 @@ exports[`color > 1337 > colorByCSSColorSpace 1`] = ` [ 0.262, 0.5605, - 0.1586, + 0.1587, ] `; @@ -159,7 +159,7 @@ exports[`color > 1337 > hwb 1`] = ` exports[`color > 1337 > lab 1`] = ` [ - 0.262024, + 0.262025, 12.106, -68.2632, ] @@ -167,7 +167,7 @@ exports[`color > 1337 > lab 1`] = ` exports[`color > 1337 > lch 1`] = ` [ - 0.262024, + 0.262025, 128.9, 36.5, ] diff --git a/test/__snapshots__/finance.spec.ts.snap b/test/__snapshots__/finance.spec.ts.snap index e3736cc8606..50860a16822 100644 --- a/test/__snapshots__/finance.spec.ts.snap +++ b/test/__snapshots__/finance.spec.ts.snap @@ -20,13 +20,13 @@ exports[`finance > 42 > amount > with min and max option 1`] = `"24.98"`; exports[`finance > 42 > amount > with min option 1`] = `"380.79"`; -exports[`finance > 42 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$24.98161"`; +exports[`finance > 42 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$24.98160"`; -exports[`finance > 42 > amount > with min, max and dec option 1`] = `"24.98161"`; +exports[`finance > 42 > amount > with min, max and dec option 1`] = `"24.98160"`; -exports[`finance > 42 > amount > with min, max, dec and symbol option 1`] = `"#24.98161"`; +exports[`finance > 42 > amount > with min, max, dec and symbol option 1`] = `"#24.98160"`; -exports[`finance > 42 > amount > with min, max, dec, symbol and autoFormat option 1`] = `"#24.98161"`; +exports[`finance > 42 > amount > with min, max, dec, symbol and autoFormat option 1`] = `"#24.98160"`; exports[`finance > 42 > bic > noArgs 1`] = `"UYETSCLLG53"`; @@ -102,15 +102,15 @@ exports[`finance > 1211 > accountName 1`] = `"Personal Loan Account"`; exports[`finance > 1211 > amount > noArgs 1`] = `"928.52"`; -exports[`finance > 1211 > amount > with leagcy dec 1`] = `"928.52016"`; +exports[`finance > 1211 > amount > with leagcy dec 1`] = `"928.52015"`; exports[`finance > 1211 > amount > with leagcy max 1`] = `"46.43"`; -exports[`finance > 1211 > amount > with min 1`] = `"929.24"`; +exports[`finance > 1211 > amount > with min 1`] = `"929.23"`; -exports[`finance > 1211 > amount > with min and max option 1`] = `"47.15"`; +exports[`finance > 1211 > amount > with min and max option 1`] = `"47.14"`; -exports[`finance > 1211 > amount > with min option 1`] = `"929.24"`; +exports[`finance > 1211 > amount > with min option 1`] = `"929.23"`; exports[`finance > 1211 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$47.14081"`; @@ -194,7 +194,7 @@ exports[`finance > 1337 > accountName 1`] = `"Money Market Account"`; exports[`finance > 1337 > amount > noArgs 1`] = `"262.02"`; -exports[`finance > 1337 > amount > with leagcy dec 1`] = `"262.02467"`; +exports[`finance > 1337 > amount > with leagcy dec 1`] = `"262.02468"`; exports[`finance > 1337 > amount > with leagcy max 1`] = `"13.10"`; diff --git a/test/__snapshots__/location.spec.ts.snap b/test/__snapshots__/location.spec.ts.snap index 8b67230402f..856743aeb33 100644 --- a/test/__snapshots__/location.spec.ts.snap +++ b/test/__snapshots__/location.spec.ts.snap @@ -66,7 +66,7 @@ exports[`location > 42 > longitude > with precision option 1`] = `-45.1655588485 exports[`location > 42 > nearbyGPSCoordinate > near origin 1`] = ` [ - 0.08140632875358443, + 0.08140632875358447, -0.08093642792425726, ] `; @@ -94,8 +94,8 @@ exports[`location > 42 > nearbyGPSCoordinate > only radius 1`] = ` exports[`location > 42 > nearbyGPSCoordinate > with origin and isMetric 1`] = ` [ - 37.05058762889859, - -13.05029562250138, + 37.050581278455596, + -13.050289308714923, ] `; @@ -186,66 +186,66 @@ exports[`location > 1211 > direction > with boolean 1`] = `"Southwest"`; exports[`location > 1211 > direction > with useAbbr option 1`] = `"SW"`; -exports[`location > 1211 > latitude > noArgs 1`] = `77.1337`; +exports[`location > 1211 > latitude > noArgs 1`] = `77.1336`; -exports[`location > 1211 > latitude > with max 1`] = `2.8521`; +exports[`location > 1211 > latitude > with max 1`] = `2.852`; exports[`location > 1211 > latitude > with max and min option 1`] = `8.5704`; -exports[`location > 1211 > latitude > with max option 1`] = `2.8521`; +exports[`location > 1211 > latitude > with max option 1`] = `2.852`; -exports[`location > 1211 > latitude > with max, min and precision option 1`] = `8.5704030749`; +exports[`location > 1211 > latitude > with max, min and precision option 1`] = `8.5704030748`; -exports[`location > 1211 > latitude > with min 1`] = `82.8521`; +exports[`location > 1211 > latitude > with min 1`] = `82.852`; -exports[`location > 1211 > latitude > with min option 1`] = `82.8521`; +exports[`location > 1211 > latitude > with min option 1`] = `82.852`; -exports[`location > 1211 > latitude > with precision 1`] = `77.1336276737`; +exports[`location > 1211 > latitude > with precision 1`] = `77.1336276736`; -exports[`location > 1211 > latitude > with precision option 1`] = `77.1336276737`; +exports[`location > 1211 > latitude > with precision option 1`] = `77.1336276736`; exports[`location > 1211 > longitude > noArgs 1`] = `154.2673`; -exports[`location > 1211 > longitude > with max 1`] = `-3.5811`; +exports[`location > 1211 > longitude > with max 1`] = `-3.5812`; exports[`location > 1211 > longitude > with max and min option 1`] = `8.5704`; -exports[`location > 1211 > longitude > with max option 1`] = `-3.5811`; +exports[`location > 1211 > longitude > with max option 1`] = `-3.5812`; -exports[`location > 1211 > longitude > with max, min and precision option 1`] = `8.5704030749`; +exports[`location > 1211 > longitude > with max, min and precision option 1`] = `8.5704030748`; -exports[`location > 1211 > longitude > with min 1`] = `166.4189`; +exports[`location > 1211 > longitude > with min 1`] = `166.4188`; -exports[`location > 1211 > longitude > with min option 1`] = `166.4189`; +exports[`location > 1211 > longitude > with min option 1`] = `166.4188`; -exports[`location > 1211 > longitude > with precision 1`] = `154.2672553473`; +exports[`location > 1211 > longitude > with precision 1`] = `154.2672553472`; -exports[`location > 1211 > longitude > with precision option 1`] = `154.2672553473`; +exports[`location > 1211 > longitude > with precision option 1`] = `154.2672553472`; exports[`location > 1211 > nearbyGPSCoordinate > near origin 1`] = ` [ - -0.02872111236834616, + -0.02872111236834621, 0.05959024752564801, ] `; exports[`location > 1211 > nearbyGPSCoordinate > noArgs 1`] = ` [ - 77.1337, + 77.1336, -14.7545, ] `; exports[`location > 1211 > nearbyGPSCoordinate > only isMetric 1`] = ` [ - 77.1337, + 77.1336, -14.7545, ] `; exports[`location > 1211 > nearbyGPSCoordinate > only radius 1`] = ` [ - 77.1337, + 77.1336, -14.7545, ] `; @@ -273,7 +273,7 @@ exports[`location > 1211 > nearbyGPSCoordinate > with origin, radius and isMetri exports[`location > 1211 > nearbyGPSCoordinate > with radius and isMetric 1`] = ` [ - 77.1337, + 77.1336, -14.7545, ] `; @@ -346,17 +346,17 @@ exports[`location > 1337 > direction > with useAbbr option 1`] = `"S"`; exports[`location > 1337 > latitude > noArgs 1`] = `-42.8356`; -exports[`location > 1337 > latitude > with max 1`] = `-63.7976`; +exports[`location > 1337 > latitude > with max 1`] = `-63.7975`; exports[`location > 1337 > latitude > with max and min option 1`] = `-4.7595`; -exports[`location > 1337 > latitude > with max option 1`] = `-63.7976`; +exports[`location > 1337 > latitude > with max option 1`] = `-63.7975`; exports[`location > 1337 > latitude > with max, min and precision option 1`] = `-4.7595064761`; -exports[`location > 1337 > latitude > with min 1`] = `16.2024`; +exports[`location > 1337 > latitude > with min 1`] = `16.2025`; -exports[`location > 1337 > latitude > with min option 1`] = `16.2024`; +exports[`location > 1337 > latitude > with min option 1`] = `16.2025`; exports[`location > 1337 > latitude > with precision 1`] = `-42.835558285`; diff --git a/test/__snapshots__/number.spec.ts.snap b/test/__snapshots__/number.spec.ts.snap index 71523ecf8bb..1b5dd37d220 100644 --- a/test/__snapshots__/number.spec.ts.snap +++ b/test/__snapshots__/number.spec.ts.snap @@ -26,7 +26,7 @@ exports[`number > 42 > float > with min 1`] = `-25.894775084685534`; exports[`number > 42 > float > with min and max 1`] = `-0.4260473116301`; -exports[`number > 42 > float > with min, max and precision 1`] = `-0.4261`; +exports[`number > 42 > float > with min, max and fractionDigits 1`] = `-0.426`; exports[`number > 42 > float > with plain number 1`] = `1.498160457238555`; @@ -74,7 +74,7 @@ exports[`number > 1211 > float > with min 1`] = `-2.073633389081806`; exports[`number > 1211 > float > with min and max 1`] = `61.06573706539348`; -exports[`number > 1211 > float > with min, max and precision 1`] = `61.0658`; +exports[`number > 1211 > float > with min, max and fractionDigits 1`] = `61.0657`; exports[`number > 1211 > float > with plain number 1`] = `3.7140806149691343`; @@ -122,7 +122,7 @@ exports[`number > 1337 > float > with min 1`] = `-30.732938923640177`; exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419991`; -exports[`number > 1337 > float > with min, max and precision 1`] = `-12.9153`; +exports[`number > 1337 > float > with min, max and fractionDigits 1`] = `-12.9153`; exports[`number > 1337 > float > with plain number 1`] = `1.048098704777658`; diff --git a/test/number.spec.ts b/test/number.spec.ts index 78a385b9bac..c1e54a1ada1 100644 --- a/test/number.spec.ts +++ b/test/number.spec.ts @@ -25,10 +25,10 @@ describe('number', () => { .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', { + .it('with min, max and fractionDigits', { min: -42, max: 69, - precision: 0.0001, + fractionDigits: 4, }); }); @@ -178,14 +178,15 @@ describe('number', () => { } }); - it('provides numbers with a given precision of 0.5 steps', () => { + it.todo('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, + fractionDigits: 1, + // steps: 0.5, }) ) ) @@ -194,14 +195,15 @@ describe('number', () => { expect(results).toEqual([0, 0.5, 1, 1.5]); }); - it('provides numbers with a given precision of 0.4 steps', () => { + 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, + fractionDigits: 1, + // steps: 0.4, }) ) ) @@ -210,26 +212,23 @@ describe('number', () => { expect(results).toEqual([0, 0.4, 0.8, 1.2, 1.6]); }); - it('provides numbers with an exact precision', () => { + it('provides numbers with the exact fractional digits', () => { for (let i = 0; i < 100; i++) { const actual = faker.number.float({ min: 0.5, max: 0.99, - precision: 0.01, + fractionDigits: 2, }); + expect(actual).toBe(Number(actual.toFixed(2))); } }); - it('throws an error for precision 0', () => { - expect(() => faker.number.float({ precision: 0 })).toThrowError( - new FakerError('Precision should be greater than 0.') - ); - }); - - it('throws an error for negative precision', () => { - expect(() => faker.number.float({ precision: -0.01 })).toThrowError( - new FakerError('Precision should be greater than 0.') + it('throws an error for negative fractionDigits', () => { + expect(() => faker.number.float({ fractionDigits: -2 })).toThrowError( + new FakerError( + 'The fractional digits count should be greater than 0.' + ) ); }); From c01a82d7173a0e571411f9ca01d0affec5a742b5 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 16:27:25 +0100 Subject: [PATCH 03/26] fix(datatype): circular deps --- src/modules/datatype/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts index 91df55daff4..7cbb26255dc 100644 --- a/src/modules/datatype/index.ts +++ b/src/modules/datatype/index.ts @@ -1,5 +1,5 @@ import type { Faker } from '../..'; -import { FakerError } from '../..'; +import { FakerError } from '../../errors/faker-error'; import { deprecated } from '../../internal/deprecated'; /** @@ -8,15 +8,15 @@ import { deprecated } from '../../internal/deprecated'; * @param options An options object. * @param options.min Lower bound for generated number. Defaults to `0`. * @param options.max Upper bound for generated number. Defaults to `min + 99999`. - * @param options.getFaker A function to get a Faker instance. + * @param options.faker A faker instance. * @param options.precision Precision of the generated number. Defaults to `0.01`. * */ function legacyFloatImplementation(options: { /** - * A function to get a Faker instance. + * A faker instance. */ - getFaker: () => Faker; + faker: Faker; /** * Lower bound for generated number. */ @@ -30,7 +30,7 @@ function legacyFloatImplementation(options: { */ precision: number; }) { - const { max, min, precision, getFaker } = options; + const { max, min, precision, faker } = options; if (max === min) { return min; } @@ -44,7 +44,7 @@ function legacyFloatImplementation(options: { } const factor = 1 / precision; - const int = getFaker().number.int({ + const int = faker.number.int({ min: min * factor, max: max * factor, }); @@ -132,7 +132,7 @@ export class DatatypeModule { const { min = 0, max = min + 99999, precision = 1 } = options; return legacyFloatImplementation({ - getFaker: () => this.faker, + faker: this.faker, max, min, precision, @@ -201,7 +201,7 @@ export class DatatypeModule { const { min = 0, max = min + 99999, precision = 0.01 } = options; return legacyFloatImplementation({ - getFaker: () => this.faker, + faker: this.faker, max, min, precision, From fabc1908b6b95a54d86166eedb5dddfb2f1437f9 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 17:09:50 +0100 Subject: [PATCH 04/26] docs(number): fix typo in JSDocs --- src/modules/number/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index ba0852389dd..be1f1917b76 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -104,7 +104,7 @@ export class NumberModule { * faker.number.float({ min: -1000000 }) //-780678.849672846 * faker.number.float({ max: 100 }) // 17.3687307164073 * faker.number.float({ fractionDigits: 1 }) // 0.9 - * faker.number.float({ min: 10, max: 100, v: 3 }) // 35.415 + * faker.number.float({ min: 10, max: 100, fractionDigits: 3 }) // 35.415 * * @since 8.0.0 */ From 92ec99c6890c3ce8c504b956e4bc715967887c5e Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 17:42:25 +0100 Subject: [PATCH 05/26] test(datatype): add legacy error testcase --- test/datatype.spec.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/datatype.spec.ts b/test/datatype.spec.ts index 2c09ea05a10..cbbca87c698 100644 --- a/test/datatype.spec.ts +++ b/test/datatype.spec.ts @@ -228,6 +228,18 @@ describe('datatype', () => { faker.datatype.number({ min, max }); }).toThrowError(`Max ${max} should be greater than min ${min}.`); }); + + it('should throw when precision <= 0', () => { + const min = 10; + const max = 9; + + expect(() => { + faker.datatype.number({ min, max, precision: 0 }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + expect(() => { + faker.datatype.number({ min, max, precision: -1 }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); }); describe('float', () => { @@ -302,6 +314,27 @@ describe('datatype', () => { expect(opts.min).toBe(min); expect(opts.max).toBe(max); }); + + it('should throw when min > max', () => { + const min = 1; + const max = 2; + + expect(() => { + faker.datatype.float({ min, max }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); + + it('should throw when precision <= 0', () => { + const min = 1; + const max = 2; + + expect(() => { + faker.datatype.float({ min, max, precision: 0 }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + expect(() => { + faker.datatype.float({ min, max, precision: -1 }); + }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }); }); describe('datetime', () => { From 12503b3de8e157bbb4a53957b0b2b19c741a431c Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 17 Feb 2023 18:25:51 +0100 Subject: [PATCH 06/26] test(datatype): fix expectations --- test/datatype.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/datatype.spec.ts b/test/datatype.spec.ts index cbbca87c698..08b0477700a 100644 --- a/test/datatype.spec.ts +++ b/test/datatype.spec.ts @@ -316,8 +316,8 @@ describe('datatype', () => { }); it('should throw when min > max', () => { - const min = 1; - const max = 2; + const min = 2; + const max = 1; expect(() => { faker.datatype.float({ min, max }); @@ -330,10 +330,10 @@ describe('datatype', () => { expect(() => { faker.datatype.float({ min, max, precision: 0 }); - }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }).toThrowError('Precision should be greater than 0.'); expect(() => { faker.datatype.float({ min, max, precision: -1 }); - }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }).toThrowError('Precision should be greater than 0.'); }); }); From 07fc37020a6925a612023a091bcc471649980b35 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Sat, 18 Feb 2023 13:25:47 +0100 Subject: [PATCH 07/26] refactor(number): remove default for fractionalDigits --- src/modules/number/index.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index be1f1917b76..fb332f29899 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -93,7 +93,7 @@ export class NumberModule { * @param options Upper bound or options object. Defaults to `{}`. * @param options.min Lower bound for generated number. Defaults to `0.0`. * @param options.max Upper bound for generated number. Defaults to `1.0`. - * @param options.fractionDigits The number of digits to appear after the decimal point. Defaults to `16`. + * @param options.fractionDigits The number of digits to appear after the decimal point. * * @throws If options.max is smaller than options.min. * @throws If options.fractionDigits is negative. @@ -126,8 +126,6 @@ export class NumberModule { max?: number; /** * The number of digits to appear after the decimal point. - * - * @default 16 */ fractionDigits?: number; } = {} @@ -138,7 +136,7 @@ export class NumberModule { }; } - const { min = 0, max = 1, fractionDigits = 16 } = options; + const { min = 0, max = 1, fractionDigits } = options; if (max === min) { return min; @@ -156,9 +154,13 @@ export class NumberModule { // @ts-expect-error: access private member field const mersenne: Mersenne = this.faker._mersenne; - const real = mersenne.next() * (max - min) + min; + let real = mersenne.next() * (max - min) + min; + + if (fractionDigits !== undefined) { + real = parseFloat(real.toFixed(fractionDigits)); + } - return parseFloat(real.toFixed(fractionDigits)); + return real; } /** From f8aafecfdb231648614d73c38bcf9458c8f58e00 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 10:36:24 +0100 Subject: [PATCH 08/26] test: remove duplicated test case --- test/modules/number.spec.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 3b4ed96aad0..731e6d8bcb0 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -233,25 +233,6 @@ describe('number', () => { } }); - it.todo( - 'provides numbers with a given precision of 0.5 steps' - // () => { - // const results = [ - // ...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]); - // } - ); - it('provides numbers with a given multipleOf of 0.5 steps', () => { const results = [ ...new Set( From 7a1f5bc50f178c10f6cd335432c0c5f1064688c7 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 10:56:20 +0100 Subject: [PATCH 09/26] test: add case with multipleOf in combination with fractionDigits --- test/modules/number.spec.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 731e6d8bcb0..2b8d2509693 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -276,7 +276,19 @@ describe('number', () => { } }); - it('throws an error for negative fractional digits', () => { + it('should ignore fractionDigits if multipleOf is provided at the same time', () => { + const actual = faker.number.float({ + min: 0, + max: 10, + multipleOf: 0.25, + fractionDigits: 6, + }); + const fractionCount = actual.toString().split('.')[1]?.length ?? 0; + expect(actual % 0.25).toBe(0); + expect(fractionCount).toBeLessThanOrEqual(2); + }); + + it('throws an error for negative fractionDigits', () => { expect(() => faker.number.float({ fractionDigits: -2 })).toThrow( new FakerError( 'The fractional digits count should be greater than 0.' From ca20b1c52d58790fce171942e5678952a9410205 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 15:11:47 +0100 Subject: [PATCH 10/26] refactor: more linear code flow --- src/modules/number/index.ts | 51 +++++++++---------- test/modules/__snapshots__/color.spec.ts.snap | 30 +++++------ .../__snapshots__/finance.spec.ts.snap | 18 +++---- .../__snapshots__/location.spec.ts.snap | 50 +++++++++--------- .../modules/__snapshots__/number.spec.ts.snap | 14 ++--- test/modules/number.spec.ts | 25 +++++---- 6 files changed, 95 insertions(+), 93 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index a619e4394f3..16fdae782af 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -114,7 +114,8 @@ export class NumberModule extends SimpleModuleBase { * faker.number.float({ max: 100 }) // 17.3687307164073 * faker.number.float({ multipleOf: 0.25 }) // 3.75 * faker.number.float({ fractionDigits: 1 }) // 0.9 - * faker.number.float({ min: 10, max: 100, multipleOf: 0.002, fractionDigits: 3 }) // 35.416 + * faker.number.float({ min: 10, max: 100, multipleOf: 0.02 }) // 35.42 + * faker.number.float({ min: 10, max: 100, fractionDigits: 3 }) // 65.716 * * @since 8.0.0 */ @@ -163,7 +164,7 @@ export class NumberModule extends SimpleModuleBase { min = 0, max = 1, precision, - multipleOf = precision, + multipleOf: possibleMultipleOf = precision, fractionDigits = 16, } = options; @@ -184,23 +185,13 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if (multipleOf !== undefined) { - if (multipleOf <= 0) { - // TODO @xDivisionByZerox: Clean up in v9.0 - throw new FakerError(`multipleOf/precision should be greater than 0.`); - } - - const logPrecision = Math.log10(multipleOf); - // Workaround to get integer values for the inverse of all multiples of the form 10^-n - const factor = - multipleOf < 1 && Number.isInteger(logPrecision) - ? 10 ** -logPrecision - : 1 / multipleOf; - const int = this.int({ - min: min * factor, - max: max * factor, - }); - return int / factor; + if ( + typeof options.fractionDigits === 'number' && + typeof options.multipleOf === 'number' + ) { + throw new FakerError( + 'multipleOf and fractionDigits cannot exist at the same time.' + ); } if (fractionDigits < 0) { @@ -209,15 +200,23 @@ export class NumberModule extends SimpleModuleBase { ); } - // @ts-expect-error: access private member field - const randomizer = this.faker._randomizer; - let real = randomizer.next() * (max - min) + min; - - if (fractionDigits !== undefined) { - real = Number.parseFloat(real.toFixed(fractionDigits)); + if (possibleMultipleOf !== undefined && possibleMultipleOf <= 0) { + // TODO @xDivisionByZerox: Clean up in v9.0 + throw new FakerError(`multipleOf/precision should be greater than 0.`); } - return real; + const multipleOf = possibleMultipleOf ?? 10 ** -fractionDigits; + const logPrecision = Math.log10(multipleOf); + // Workaround to get integer values for the inverse of all multiples of the form 10^-n + const factor = + multipleOf < 1 && Number.isInteger(logPrecision) + ? 10 ** -logPrecision + : 1 / multipleOf; + const int = this.int({ + min: min * factor, + max: max * factor, + }); + return int / factor; } /** diff --git a/test/modules/__snapshots__/color.spec.ts.snap b/test/modules/__snapshots__/color.spec.ts.snap index 31790b75ce4..a39ce0f3558 100644 --- a/test/modules/__snapshots__/color.spec.ts.snap +++ b/test/modules/__snapshots__/color.spec.ts.snap @@ -4,7 +4,7 @@ exports[`color > 42 > cmyk 1`] = ` [ 0.37, 0.8, - 0.95, + 0.96, 0.18, ] `; @@ -12,8 +12,8 @@ exports[`color > 42 > cmyk 1`] = ` exports[`color > 42 > colorByCSSColorSpace 1`] = ` [ 0.3745, - 0.7965, - 0.9507, + 0.7966, + 0.9508, ] `; @@ -25,7 +25,7 @@ exports[`color > 42 > hsl 1`] = ` [ 135, 0.8, - 0.95, + 0.96, ] `; @@ -35,7 +35,7 @@ exports[`color > 42 > hwb 1`] = ` [ 135, 0.8, - 0.95, + 0.96, ] `; @@ -63,14 +63,14 @@ exports[`color > 1211 > cmyk 1`] = ` [ 0.93, 0.46, - 0.89, + 0.9, 0.78, ] `; exports[`color > 1211 > colorByCSSColorSpace 1`] = ` [ - 0.9285, + 0.9286, 0.459, 0.8935, ] @@ -84,7 +84,7 @@ exports[`color > 1211 > hsl 1`] = ` [ 335, 0.46, - 0.89, + 0.9, ] `; @@ -94,21 +94,21 @@ exports[`color > 1211 > hwb 1`] = ` [ 335, 0.46, - 0.89, + 0.9, ] `; exports[`color > 1211 > lab 1`] = ` [ - 0.92852, + 0.928521, -8.197, - 78.6943, + 78.6944, ] `; exports[`color > 1211 > lch 1`] = ` [ - 0.92852, + 0.928521, 105.6, 205.5, ] @@ -131,7 +131,7 @@ exports[`color > 1337 > colorByCSSColorSpace 1`] = ` [ 0.262, 0.5605, - 0.1587, + 0.1586, ] `; @@ -159,7 +159,7 @@ exports[`color > 1337 > hwb 1`] = ` exports[`color > 1337 > lab 1`] = ` [ - 0.262025, + 0.262024, 12.106, -68.2632, ] @@ -167,7 +167,7 @@ exports[`color > 1337 > lab 1`] = ` exports[`color > 1337 > lch 1`] = ` [ - 0.262025, + 0.262024, 128.9, 36.5, ] diff --git a/test/modules/__snapshots__/finance.spec.ts.snap b/test/modules/__snapshots__/finance.spec.ts.snap index 6a9d535ae29..b2478f58ea4 100644 --- a/test/modules/__snapshots__/finance.spec.ts.snap +++ b/test/modules/__snapshots__/finance.spec.ts.snap @@ -124,15 +124,15 @@ exports[`finance > 1211 > accountNumber > with length option 1`] = `"9487219061" exports[`finance > 1211 > amount > noArgs 1`] = `"928.52"`; -exports[`finance > 1211 > amount > with leagcy dec 1`] = `"928.52015"`; +exports[`finance > 1211 > amount > with leagcy dec 1`] = `"928.52016"`; exports[`finance > 1211 > amount > with leagcy max 1`] = `"46.43"`; -exports[`finance > 1211 > amount > with min 1`] = `"929.23"`; +exports[`finance > 1211 > amount > with min 1`] = `"929.24"`; -exports[`finance > 1211 > amount > with min and max option 1`] = `"47.14"`; +exports[`finance > 1211 > amount > with min and max option 1`] = `"47.15"`; -exports[`finance > 1211 > amount > with min option 1`] = `"929.23"`; +exports[`finance > 1211 > amount > with min option 1`] = `"929.24"`; exports[`finance > 1211 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$47.14081"`; @@ -234,7 +234,7 @@ exports[`finance > 1337 > accountNumber > with length option 1`] = `"2512254032" exports[`finance > 1337 > amount > noArgs 1`] = `"262.02"`; -exports[`finance > 1337 > amount > with leagcy dec 1`] = `"262.02468"`; +exports[`finance > 1337 > amount > with leagcy dec 1`] = `"262.02467"`; exports[`finance > 1337 > amount > with leagcy max 1`] = `"13.10"`; @@ -244,13 +244,13 @@ exports[`finance > 1337 > amount > with min and max option 1`] = `"20.48"`; exports[`finance > 1337 > amount > with min option 1`] = `"269.40"`; -exports[`finance > 1337 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$20.48099"`; +exports[`finance > 1337 > amount > with min, leagcy max, leagcy dec and leagcy symbol 1`] = `"$20.48098"`; -exports[`finance > 1337 > amount > with min, max and dec option 1`] = `"20.48099"`; +exports[`finance > 1337 > amount > with min, max and dec option 1`] = `"20.48098"`; -exports[`finance > 1337 > amount > with min, max, dec and symbol option 1`] = `"#20.48099"`; +exports[`finance > 1337 > amount > with min, max, dec and symbol option 1`] = `"#20.48098"`; -exports[`finance > 1337 > amount > with min, max, dec, symbol and autoFormat option 1`] = `"#20.48099"`; +exports[`finance > 1337 > amount > with min, max, dec, symbol and autoFormat option 1`] = `"#20.48098"`; exports[`finance > 1337 > bic > noArgs 1`] = `"OEFHLYG18IL"`; diff --git a/test/modules/__snapshots__/location.spec.ts.snap b/test/modules/__snapshots__/location.spec.ts.snap index 4cffd581075..b4e2586b184 100644 --- a/test/modules/__snapshots__/location.spec.ts.snap +++ b/test/modules/__snapshots__/location.spec.ts.snap @@ -102,8 +102,8 @@ exports[`location > 42 > nearbyGPSCoordinate > only radius 1`] = ` exports[`location > 42 > nearbyGPSCoordinate > with origin and isMetric 1`] = ` [ - 37.050581278455596, - -13.050289308714923, + 37.05058762889859, + -13.05029562250138, ] `; @@ -196,41 +196,41 @@ exports[`location > 1211 > direction > with abbreviated option 1`] = `"SW"`; exports[`location > 1211 > direction > with boolean 1`] = `"Southwest"`; -exports[`location > 1211 > latitude > noArgs 1`] = `77.1336`; +exports[`location > 1211 > latitude > noArgs 1`] = `77.1337`; -exports[`location > 1211 > latitude > with max 1`] = `2.852`; +exports[`location > 1211 > latitude > with max 1`] = `2.8521`; exports[`location > 1211 > latitude > with max and min option 1`] = `8.5704`; -exports[`location > 1211 > latitude > with max option 1`] = `2.852`; +exports[`location > 1211 > latitude > with max option 1`] = `2.8521`; -exports[`location > 1211 > latitude > with max, min and precision option 1`] = `8.5704030748`; +exports[`location > 1211 > latitude > with max, min and precision option 1`] = `8.5704030749`; -exports[`location > 1211 > latitude > with min 1`] = `82.852`; +exports[`location > 1211 > latitude > with min 1`] = `82.8521`; -exports[`location > 1211 > latitude > with min option 1`] = `82.852`; +exports[`location > 1211 > latitude > with min option 1`] = `82.8521`; -exports[`location > 1211 > latitude > with precision 1`] = `77.1336276736`; +exports[`location > 1211 > latitude > with precision 1`] = `77.1336276737`; -exports[`location > 1211 > latitude > with precision option 1`] = `77.1336276736`; +exports[`location > 1211 > latitude > with precision option 1`] = `77.1336276737`; exports[`location > 1211 > longitude > noArgs 1`] = `154.2673`; -exports[`location > 1211 > longitude > with max 1`] = `-3.5812`; +exports[`location > 1211 > longitude > with max 1`] = `-3.5811`; exports[`location > 1211 > longitude > with max and min option 1`] = `8.5704`; -exports[`location > 1211 > longitude > with max option 1`] = `-3.5812`; +exports[`location > 1211 > longitude > with max option 1`] = `-3.5811`; -exports[`location > 1211 > longitude > with max, min and precision option 1`] = `8.5704030748`; +exports[`location > 1211 > longitude > with max, min and precision option 1`] = `8.5704030749`; -exports[`location > 1211 > longitude > with min 1`] = `166.4188`; +exports[`location > 1211 > longitude > with min 1`] = `166.4189`; -exports[`location > 1211 > longitude > with min option 1`] = `166.4188`; +exports[`location > 1211 > longitude > with min option 1`] = `166.4189`; -exports[`location > 1211 > longitude > with precision 1`] = `154.2672553472`; +exports[`location > 1211 > longitude > with precision 1`] = `154.2672553473`; -exports[`location > 1211 > longitude > with precision option 1`] = `154.2672553472`; +exports[`location > 1211 > longitude > with precision option 1`] = `154.2672553473`; exports[`location > 1211 > nearbyGPSCoordinate > near origin 1`] = ` [ @@ -241,21 +241,21 @@ exports[`location > 1211 > nearbyGPSCoordinate > near origin 1`] = ` exports[`location > 1211 > nearbyGPSCoordinate > noArgs 1`] = ` [ - 77.1336, + 77.1337, -14.7545, ] `; exports[`location > 1211 > nearbyGPSCoordinate > only isMetric 1`] = ` [ - 77.1336, + 77.1337, -14.7545, ] `; exports[`location > 1211 > nearbyGPSCoordinate > only radius 1`] = ` [ - 77.1336, + 77.1337, -14.7545, ] `; @@ -283,7 +283,7 @@ exports[`location > 1211 > nearbyGPSCoordinate > with origin, radius and isMetri exports[`location > 1211 > nearbyGPSCoordinate > with radius and isMetric 1`] = ` [ - 77.1336, + 77.1337, -14.7545, ] `; @@ -358,17 +358,17 @@ exports[`location > 1337 > direction > with boolean 1`] = `"South"`; exports[`location > 1337 > latitude > noArgs 1`] = `-42.8356`; -exports[`location > 1337 > latitude > with max 1`] = `-63.7975`; +exports[`location > 1337 > latitude > with max 1`] = `-63.7976`; exports[`location > 1337 > latitude > with max and min option 1`] = `-4.7595`; -exports[`location > 1337 > latitude > with max option 1`] = `-63.7975`; +exports[`location > 1337 > latitude > with max option 1`] = `-63.7976`; exports[`location > 1337 > latitude > with max, min and precision option 1`] = `-4.7595064761`; -exports[`location > 1337 > latitude > with min 1`] = `16.2025`; +exports[`location > 1337 > latitude > with min 1`] = `16.2024`; -exports[`location > 1337 > latitude > with min option 1`] = `16.2025`; +exports[`location > 1337 > latitude > with min option 1`] = `16.2024`; exports[`location > 1337 > latitude > with precision 1`] = `-42.835558285`; diff --git a/test/modules/__snapshots__/number.spec.ts.snap b/test/modules/__snapshots__/number.spec.ts.snap index e0d3f62b087..85c45ea9f27 100644 --- a/test/modules/__snapshots__/number.spec.ts.snap +++ b/test/modules/__snapshots__/number.spec.ts.snap @@ -24,9 +24,9 @@ exports[`number > 42 > float > with max 1`] = `25.843267887365073`; exports[`number > 42 > float > with min 1`] = `-25.894775084685534`; -exports[`number > 42 > float > with min and max 1`] = `-0.4260473116301`; +exports[`number > 42 > float > with min and max 1`] = `-0.4260473116300992`; -exports[`number > 42 > float > with min, max and fractionDigits 1`] = `-0.426`; +exports[`number > 42 > float > with min, max and fractionDigits 1`] = `-0.4261`; exports[`number > 42 > float > with min, max and multipleOf 1`] = `-0.4261`; @@ -72,11 +72,11 @@ exports[`number > 1211 > binary > with value 1`] = `"1"`; exports[`number > 1211 > float > with max 1`] = `64.06789060821757`; -exports[`number > 1211 > float > with min 1`] = `-2.073633389081806`; +exports[`number > 1211 > float > with min 1`] = `-2.0736333890818046`; -exports[`number > 1211 > float > with min and max 1`] = `61.06573706539348`; +exports[`number > 1211 > float > with min and max 1`] = `61.06573706539347`; -exports[`number > 1211 > float > with min, max and fractionDigits 1`] = `61.0657`; +exports[`number > 1211 > float > with min, max and fractionDigits 1`] = `61.0658`; exports[`number > 1211 > float > with min, max and multipleOf 1`] = `61.0658`; @@ -122,9 +122,9 @@ exports[`number > 1337 > binary > with value 1`] = `"0"`; exports[`number > 1337 > float > with max 1`] = `18.0797026574146`; -exports[`number > 1337 > float > with min 1`] = `-30.732938923640177`; +exports[`number > 1337 > float > with min 1`] = `-30.73293892364018`; -exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419991`; +exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419994`; exports[`number > 1337 > float > with min, max and fractionDigits 1`] = `-12.9153`; diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 2b8d2509693..df4c5c8a060 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -276,16 +276,19 @@ describe('number', () => { } }); - it('should ignore fractionDigits if multipleOf is provided at the same time', () => { - const actual = faker.number.float({ - min: 0, - max: 10, - multipleOf: 0.25, - fractionDigits: 6, - }); - const fractionCount = actual.toString().split('.')[1]?.length ?? 0; - expect(actual % 0.25).toBe(0); - expect(fractionCount).toBeLessThanOrEqual(2); + it('throws an error if fractionDigits and multipleOf is provided at the same time', () => { + expect(() => + faker.number.float({ + min: 0, + max: 10, + multipleOf: 0.25, + fractionDigits: 6, + }) + ).toThrow( + new FakerError( + 'multipleOf and fractionDigits cannot exist at the same time.' + ) + ); }); it('throws an error for negative fractionDigits', () => { @@ -332,7 +335,7 @@ describe('number', () => { ); }); - it('throws an error for multipleOf 0', () => { + it('throws an error for and0', () => { expect(() => faker.number.float({ multipleOf: 0 })).toThrow( new FakerError('multipleOf/precision should be greater than 0.') ); From a387b3d0d19cfb3048bfdaefbd954bc5e88a4961 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 15:23:39 +0100 Subject: [PATCH 11/26] revert: color module uses number fractionDigits --- src/modules/color/index.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/modules/color/index.ts b/src/modules/color/index.ts index ec451bd99f2..a9b39377668 100644 --- a/src/modules/color/index.ts +++ b/src/modules/color/index.ts @@ -389,7 +389,7 @@ export class ColorModule extends ModuleBase { color = Array.from({ length: 3 }, () => this.faker.number.int(255)); if (includeAlpha) { - color.push(this.faker.number.float({ fractionDigits: 2 })); + color.push(this.faker.number.float({ multipleOf: 0.01 })); cssFunction = 'rgba'; } @@ -470,7 +470,7 @@ export class ColorModule extends ModuleBase { }): string | number[]; cmyk(options?: { format?: ColorFormat }): string | number[] { const color: string | number[] = Array.from({ length: 4 }, () => - this.faker.number.float({ fractionDigits: 2 }) + this.faker.number.float({ multipleOf: 0.01 }) ); return toColorFormat(color, options?.format || 'decimal', 'cmyk'); } @@ -580,7 +580,7 @@ export class ColorModule extends ModuleBase { }): string | number[] { const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < (options?.includeAlpha ? 3 : 2); i++) { - hsl.push(this.faker.number.float({ fractionDigits: 2 })); + hsl.push(this.faker.number.float({ multipleOf: 0.01 })); } return toColorFormat( @@ -686,7 +686,7 @@ export class ColorModule extends ModuleBase { }): string | number[] { const hsl: number[] = [this.faker.number.int(360)]; for (let i = 0; i < 2; i++) { - hsl.push(this.faker.number.float({ fractionDigits: 2 })); + hsl.push(this.faker.number.float({ multipleOf: 0.01 })); } return toColorFormat(hsl, options?.format || 'decimal', 'hwb'); @@ -765,10 +765,10 @@ export class ColorModule extends ModuleBase { format?: ColorFormat; }): string | number[]; lab(options?: { format?: ColorFormat }): string | number[] { - const lab = [this.faker.number.float({ fractionDigits: 6 })]; + const lab = [this.faker.number.float({ multipleOf: 0.000001 })]; for (let i = 0; i < 2; i++) { lab.push( - this.faker.number.float({ min: -100, max: 100, fractionDigits: 4 }) + this.faker.number.float({ min: -100, max: 100, multipleOf: 0.0001 }) ); } @@ -860,9 +860,9 @@ export class ColorModule extends ModuleBase { format?: ColorFormat; }): string | number[]; lch(options?: { format?: ColorFormat }): string | number[] { - const lch = [this.faker.number.float({ fractionDigits: 6 })]; + const lch = [this.faker.number.float({ multipleOf: 0.000001 })]; for (let i = 0; i < 2; i++) { - lch.push(this.faker.number.float({ max: 230, fractionDigits: 1 })); + lch.push(this.faker.number.float({ max: 230, multipleOf: 0.1 })); } return toColorFormat(lch, options?.format || 'decimal', 'lch'); @@ -970,7 +970,7 @@ export class ColorModule extends ModuleBase { } const color = Array.from({ length: 3 }, () => - this.faker.number.float({ fractionDigits: 4 }) + this.faker.number.float({ multipleOf: 0.0001 }) ); return toColorFormat( color, From a8bcc8e535ee3fa11e3817b0c30636329effff9c Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 17:31:18 +0100 Subject: [PATCH 12/26] revert: changes to datatype module --- src/modules/datatype/index.ts | 65 ++--------------------------------- test/modules/datatype.spec.ts | 4 +-- 2 files changed, 4 insertions(+), 65 deletions(-) diff --git a/src/modules/datatype/index.ts b/src/modules/datatype/index.ts index 8142c65fecd..daec9640dca 100644 --- a/src/modules/datatype/index.ts +++ b/src/modules/datatype/index.ts @@ -1,56 +1,5 @@ -import { FakerError } from '../../errors/faker-error'; import { deprecated } from '../../internal/deprecated'; import { SimpleModuleBase } from '../../internal/module-base'; -import type { SimpleFaker } from '../../simple-faker'; - -/** - * The legacy implementation for datatype float. - * - * @param options An options object. - * @param options.min Lower bound for generated number. Defaults to `0`. - * @param options.max Upper bound for generated number. Defaults to `min + 99999`. - * @param options.faker A faker instance. - * @param options.precision Precision of the generated number. Defaults to `0.01`. - * - */ -function legacyFloatImplementation(options: { - /** - * A faker instance. - */ - faker: SimpleFaker; - /** - * Lower bound for generated number. - */ - min: number; - /** - * Upper bound for generated number. - */ - max: number; - /** - * Precision of the generated number. - */ - precision: number; -}) { - const { max, min, precision, faker } = options; - if (max === min) { - return min; - } - - if (max < min) { - throw new FakerError(`Max ${max} should be greater than min ${min}.`); - } - - if (precision <= 0) { - throw new FakerError(`Precision should be greater than 0.`); - } - - const factor = 1 / precision; - const int = faker.number.int({ - min: min * factor, - max: max * factor, - }); - return int / factor; -} /** * Module to generate various primitive values and data types. @@ -126,12 +75,7 @@ export class DatatypeModule extends SimpleModuleBase { const { min = 0, max = min + 99999, precision = 1 } = options; - return legacyFloatImplementation({ - faker: this.faker, - max, - min, - precision, - }); + return this.faker.number.float({ min, max, multipleOf: precision }); } /** @@ -198,12 +142,7 @@ export class DatatypeModule extends SimpleModuleBase { const { min = 0, max = min + 99999, precision = 0.01 } = options; - return legacyFloatImplementation({ - faker: this.faker, - max, - min, - precision, - }); + return this.faker.number.float({ min, max, multipleOf: precision }); } /** diff --git a/test/modules/datatype.spec.ts b/test/modules/datatype.spec.ts index ef56441c9ad..cc4c75ef320 100644 --- a/test/modules/datatype.spec.ts +++ b/test/modules/datatype.spec.ts @@ -334,10 +334,10 @@ describe('datatype', () => { expect(() => { faker.datatype.float({ min, max, precision: 0 }); - }).toThrowError('Precision should be greater than 0.'); + }).toThrowError('multipleOf/precision should be greater than 0.'); expect(() => { faker.datatype.float({ min, max, precision: -1 }); - }).toThrowError('Precision should be greater than 0.'); + }).toThrowError('multipleOf/precision should be greater than 0.'); }); }); From b566c452029f8213348e6ed6edf1f305efc81232 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:07:44 +0100 Subject: [PATCH 13/26] test: readd removed min, max and precision option test case --- test/modules/__snapshots__/number.spec.ts.snap | 6 ++++++ test/modules/number.spec.ts | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/test/modules/__snapshots__/number.spec.ts.snap b/test/modules/__snapshots__/number.spec.ts.snap index 85c45ea9f27..c980889a082 100644 --- a/test/modules/__snapshots__/number.spec.ts.snap +++ b/test/modules/__snapshots__/number.spec.ts.snap @@ -30,6 +30,8 @@ exports[`number > 42 > float > with min, max and fractionDigits 1`] = `-0.4261`; exports[`number > 42 > float > with min, max and multipleOf 1`] = `-0.4261`; +exports[`number > 42 > float > with min, max and precision 1`] = `-0.4261`; + exports[`number > 42 > float > with plain number 1`] = `1.498160457238555`; exports[`number > 42 > hex > noArgs 1`] = `"5"`; @@ -80,6 +82,8 @@ exports[`number > 1211 > float > with min, max and fractionDigits 1`] = `61.0658 exports[`number > 1211 > float > with min, max and multipleOf 1`] = `61.0658`; +exports[`number > 1211 > float > with min, max and precision 1`] = `61.0658`; + exports[`number > 1211 > float > with plain number 1`] = `3.7140806149691343`; exports[`number > 1211 > hex > noArgs 1`] = `"e"`; @@ -130,6 +134,8 @@ exports[`number > 1337 > float > with min, max and fractionDigits 1`] = `-12.915 exports[`number > 1337 > float > with min, max and multipleOf 1`] = `-12.9153`; +exports[`number > 1337 > float > with min, max and precision 1`] = `-12.9153`; + exports[`number > 1337 > float > with plain number 1`] = `1.048098704777658`; exports[`number > 1337 > hex > noArgs 1`] = `"4"`; diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index df4c5c8a060..35ae1f141ee 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -23,6 +23,11 @@ describe('number', () => { .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, + }) .it('with min, max and fractionDigits', { min: -42, max: 69, From 3c2a83935402a5fc5166baf594feb3588da916a8 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:18:28 +0100 Subject: [PATCH 14/26] test: add accidental removed test case --- test/modules/number.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 35ae1f141ee..6cc9a93becf 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -238,6 +238,22 @@ describe('number', () => { } }); + it('provides numbers with a given precision of 0.5 steps', () => { + const results = [ + ...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]); + }); + it('provides numbers with a given multipleOf of 0.5 steps', () => { const results = [ ...new Set( From 2b9a74e951c55658a4677da736c86c84cb0b120e Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:19:53 +0100 Subject: [PATCH 15/26] revert: accidental changed test name --- test/modules/number.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 6cc9a93becf..e77753d574a 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -356,7 +356,7 @@ describe('number', () => { ); }); - it('throws an error for and0', () => { + it('throws an error for multipleOf 0', () => { expect(() => faker.number.float({ multipleOf: 0 })).toThrow( new FakerError('multipleOf/precision should be greater than 0.') ); From 9d394bbd6b63d0cf2e8c8c50cd026146e3181a51 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:23:30 +0100 Subject: [PATCH 16/26] docs: adjust throw tags wordings --- src/modules/number/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 16fdae782af..72d8cf855b8 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -102,10 +102,10 @@ export class NumberModule extends SimpleModuleBase { * This parameter is ignored if `multipleOf` is given as well. * Defaults to `16`. * - * @throws If `max` is smaller than `min`. - * @throws If `precision` is negative. - * @throws If `multipleOf` is negative. - * @throws If `fractionDigits` is negative. + * @throws When `min` is greater than `max`. + * @throws When `precision` is negative. + * @throws When `multipleOf` is negative. + * @throws When `fractionDigits` is negative. * * @example * faker.number.float() // 0.5688541042618454 From 89ede36824b313a6a666ca4d83775f303225ec8d Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:28:44 +0100 Subject: [PATCH 17/26] docs: rephrasing regarding excluding options properties --- src/modules/number/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 72d8cf855b8..c4aa89ff624 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -97,15 +97,16 @@ export class NumberModule extends SimpleModuleBase { * @param options.multipleOf The generated number will be a multiple of this property. * This property can be used to limit the result to a specific number of decimal digits. * For example `0.01` will round to 2 decimal points. - * If multipleOf is passed, the upper bound is inclusive. + * If multipleOf is passed, the upper bound is inclusive and the `fractionDigits` option has to be excluded. * @param options.fractionDigits The number of digits to appear after the decimal point. - * This parameter is ignored if `multipleOf` is given as well. + * This parameter has to be excluded if `multipleOf` is provided. * Defaults to `16`. * * @throws When `min` is greater than `max`. * @throws When `precision` is negative. * @throws When `multipleOf` is negative. * @throws When `fractionDigits` is negative. + * @throws When `fractionDigits` and `multipleOf` is passed in the same options object. * * @example * faker.number.float() // 0.5688541042618454 From b2af5a888ae2bc748f3401df889cc6f77282b1d1 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 18:29:32 +0100 Subject: [PATCH 18/26] docs: reword fractionDigits description --- src/modules/number/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index c4aa89ff624..01bb934ba77 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -98,7 +98,7 @@ export class NumberModule extends SimpleModuleBase { * This property can be used to limit the result to a specific number of decimal digits. * For example `0.01` will round to 2 decimal points. * If multipleOf is passed, the upper bound is inclusive and the `fractionDigits` option has to be excluded. - * @param options.fractionDigits The number of digits to appear after the decimal point. + * @param options.fractionDigits The maximum number of digits to appear after the decimal point. * This parameter has to be excluded if `multipleOf` is provided. * Defaults to `16`. * From 23b222c418dc2ff4e55381ea69318bd2e93019ab Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Fri, 5 Jan 2024 19:07:34 +0100 Subject: [PATCH 19/26] refactor: remove default from fractionDigits --- src/modules/number/index.ts | 67 ++++++++++--------- .../modules/__snapshots__/number.spec.ts.snap | 10 +-- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 01bb934ba77..46e08a19b82 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -100,7 +100,6 @@ export class NumberModule extends SimpleModuleBase { * If multipleOf is passed, the upper bound is inclusive and the `fractionDigits` option has to be excluded. * @param options.fractionDigits The maximum number of digits to appear after the decimal point. * This parameter has to be excluded if `multipleOf` is provided. - * Defaults to `16`. * * @throws When `min` is greater than `max`. * @throws When `precision` is negative. @@ -138,8 +137,6 @@ export class NumberModule extends SimpleModuleBase { max?: number; /** * The number of digits to appear after the decimal point. - * - * @default 16 */ fractionDigits?: number; /* @@ -161,13 +158,8 @@ export class NumberModule extends SimpleModuleBase { }; } - const { - min = 0, - max = 1, - precision, - multipleOf: possibleMultipleOf = precision, - fractionDigits = 16, - } = options; + const { min = 0, max = 1, precision, fractionDigits } = options; + let { multipleOf = precision } = options; if (precision !== undefined) { deprecated({ @@ -186,38 +178,47 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if ( - typeof options.fractionDigits === 'number' && - typeof options.multipleOf === 'number' - ) { + if (typeof fractionDigits === 'number' && typeof multipleOf === 'number') { throw new FakerError( 'multipleOf and fractionDigits cannot exist at the same time.' ); } - if (fractionDigits < 0) { - throw new FakerError( - 'The fractional digits count should be greater than 0.' - ); + if (fractionDigits !== undefined) { + if (fractionDigits < 0) { + throw new FakerError( + 'The fractional digits count should be greater than 0.' + ); + } + + if (multipleOf === undefined) { + multipleOf = 10 ** -fractionDigits; + } } - if (possibleMultipleOf !== undefined && possibleMultipleOf <= 0) { - // TODO @xDivisionByZerox: Clean up in v9.0 - throw new FakerError(`multipleOf/precision should be greater than 0.`); + if (multipleOf !== undefined) { + if (multipleOf <= 0) { + // TODO @xDivisionByZerox: Clean up in v9.0 + throw new FakerError(`multipleOf/precision should be greater than 0.`); + } + + const logPrecision = Math.log10(multipleOf); + // Workaround to get integer values for the inverse of all multiples of the form 10^-n + const factor = + multipleOf < 1 && Number.isInteger(logPrecision) + ? 10 ** -logPrecision + : 1 / multipleOf; + const int = this.int({ + min: min * factor, + max: max * factor, + }); + return int / factor; } - const multipleOf = possibleMultipleOf ?? 10 ** -fractionDigits; - const logPrecision = Math.log10(multipleOf); - // Workaround to get integer values for the inverse of all multiples of the form 10^-n - const factor = - multipleOf < 1 && Number.isInteger(logPrecision) - ? 10 ** -logPrecision - : 1 / multipleOf; - const int = this.int({ - min: min * factor, - max: max * factor, - }); - return int / factor; + // @ts-expect-error: access private member field + const randomizer = this.faker._randomizer; + const real = randomizer.next(); + return real * (max - min) + min; } /** diff --git a/test/modules/__snapshots__/number.spec.ts.snap b/test/modules/__snapshots__/number.spec.ts.snap index c980889a082..fc528fd96ad 100644 --- a/test/modules/__snapshots__/number.spec.ts.snap +++ b/test/modules/__snapshots__/number.spec.ts.snap @@ -24,7 +24,7 @@ exports[`number > 42 > float > with max 1`] = `25.843267887365073`; exports[`number > 42 > float > with min 1`] = `-25.894775084685534`; -exports[`number > 42 > float > with min and max 1`] = `-0.4260473116300992`; +exports[`number > 42 > float > with min and max 1`] = `-0.4260473116301`; exports[`number > 42 > float > with min, max and fractionDigits 1`] = `-0.4261`; @@ -74,9 +74,9 @@ exports[`number > 1211 > binary > with value 1`] = `"1"`; exports[`number > 1211 > float > with max 1`] = `64.06789060821757`; -exports[`number > 1211 > float > with min 1`] = `-2.0736333890818046`; +exports[`number > 1211 > float > with min 1`] = `-2.073633389081806`; -exports[`number > 1211 > float > with min and max 1`] = `61.06573706539347`; +exports[`number > 1211 > float > with min and max 1`] = `61.06573706539348`; exports[`number > 1211 > float > with min, max and fractionDigits 1`] = `61.0658`; @@ -126,9 +126,9 @@ exports[`number > 1337 > binary > with value 1`] = `"0"`; exports[`number > 1337 > float > with max 1`] = `18.0797026574146`; -exports[`number > 1337 > float > with min 1`] = `-30.73293892364018`; +exports[`number > 1337 > float > with min 1`] = `-30.732938923640177`; -exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419994`; +exports[`number > 1337 > float > with min and max 1`] = `-12.915260942419991`; exports[`number > 1337 > float > with min, max and fractionDigits 1`] = `-12.9153`; From a8aed91ba39e8c3cadfb979d9eb3d491d97a4bf0 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Wed, 10 Jan 2024 19:59:41 +0100 Subject: [PATCH 20/26] refactor: better input validation --- src/modules/number/index.ts | 26 +++++++++++++++++--------- test/modules/datatype.spec.ts | 14 +++++--------- test/modules/number.spec.ts | 10 +++++++--- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 46e08a19b82..0fa60589d6d 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -158,8 +158,15 @@ export class NumberModule extends SimpleModuleBase { }; } - const { min = 0, max = 1, precision, fractionDigits } = options; - let { multipleOf = precision } = options; + const { + min = 0, + max = 1, + fractionDigits, + precision, + multipleOf: originalMultipleOf = precision, + multipleOf = precision ?? + (fractionDigits == null ? undefined : 10 ** -fractionDigits), + } = options; if (precision !== undefined) { deprecated({ @@ -178,21 +185,22 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if (typeof fractionDigits === 'number' && typeof multipleOf === 'number') { + if ( + typeof fractionDigits === 'number' && + typeof originalMultipleOf === 'number' + ) { throw new FakerError( 'multipleOf and fractionDigits cannot exist at the same time.' ); } if (fractionDigits !== undefined) { - if (fractionDigits < 0) { - throw new FakerError( - 'The fractional digits count should be greater than 0.' - ); + if (fractionDigits % 1 !== 0) { + throw new FakerError('fractionDigits should be an integer.'); } - if (multipleOf === undefined) { - multipleOf = 10 ** -fractionDigits; + if (fractionDigits < 0) { + throw new FakerError('fractionDigits should be greater than 0.'); } } diff --git a/test/modules/datatype.spec.ts b/test/modules/datatype.spec.ts index cc4c75ef320..66ed6d3c55f 100644 --- a/test/modules/datatype.spec.ts +++ b/test/modules/datatype.spec.ts @@ -233,16 +233,12 @@ describe('datatype', () => { ); }); - it('should throw when precision <= 0', () => { - const min = 10; - const max = 9; - + it('should throw when precision is negative', () => { expect(() => { - faker.datatype.number({ min, max, precision: 0 }); - }).toThrowError(`Max ${max} should be greater than min ${min}.`); - expect(() => { - faker.datatype.number({ min, max, precision: -1 }); - }).toThrowError(`Max ${max} should be greater than min ${min}.`); + faker.datatype.number({ precision: -0.01 }); + }).toThrow( + new FakerError('multipleOf/precision should be greater than 0.') + ); }); }); diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index e77753d574a..c6bf8e14055 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -312,11 +312,15 @@ describe('number', () => { ); }); + it('throws an error for non integer fractionDigits numbers', () => { + expect(() => faker.number.float({ fractionDigits: 1.337 })).toThrow( + new FakerError('fractionDigits should be an integer.') + ); + }); + it('throws an error for negative fractionDigits', () => { expect(() => faker.number.float({ fractionDigits: -2 })).toThrow( - new FakerError( - 'The fractional digits count should be greater than 0.' - ) + new FakerError('fractionDigits should be greater than 0.') ); }); From 29de20ab33dd8273f5f410e05ea3f61e19ea0364 Mon Sep 17 00:00:00 2001 From: xDivisionByZerox Date: Thu, 11 Jan 2024 00:14:44 +0100 Subject: [PATCH 21/26] refactor: make input validation more linear --- src/modules/number/index.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 0fa60589d6d..4f41d023d03 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -185,17 +185,14 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if ( - typeof fractionDigits === 'number' && - typeof originalMultipleOf === 'number' - ) { - throw new FakerError( - 'multipleOf and fractionDigits cannot exist at the same time.' - ); - } - if (fractionDigits !== undefined) { - if (fractionDigits % 1 !== 0) { + if (typeof originalMultipleOf === 'number') { + throw new FakerError( + 'multipleOf and fractionDigits cannot exist at the same time.' + ); + } + + if (!Number.isInteger(fractionDigits)) { throw new FakerError('fractionDigits should be an integer.'); } From 89e8b9964dc41d26940450f60c282fd65413de20 Mon Sep 17 00:00:00 2001 From: DivisionByZero Date: Thu, 11 Jan 2024 14:39:53 +0100 Subject: [PATCH 22/26] refactor: rephrase error messages Co-authored-by: ST-DDT --- src/modules/number/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 4f41d023d03..c654b895b1f 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -188,7 +188,7 @@ export class NumberModule extends SimpleModuleBase { if (fractionDigits !== undefined) { if (typeof originalMultipleOf === 'number') { throw new FakerError( - 'multipleOf and fractionDigits cannot exist at the same time.' + 'multipleOf and fractionDigits cannot be set at the same time.' ); } @@ -197,7 +197,7 @@ export class NumberModule extends SimpleModuleBase { } if (fractionDigits < 0) { - throw new FakerError('fractionDigits should be greater than 0.'); + throw new FakerError('fractionDigits should be greater than or equal to 0.'); } } From 85579522d15dc0aa35194a34d15cc639878f239a Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sun, 14 Jan 2024 11:17:58 +0100 Subject: [PATCH 23/26] chore: fix minor issues --- src/modules/number/index.ts | 4 +++- test/modules/number.spec.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index c654b895b1f..32a8541b85a 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -197,7 +197,9 @@ export class NumberModule extends SimpleModuleBase { } if (fractionDigits < 0) { - throw new FakerError('fractionDigits should be greater than or equal to 0.'); + throw new FakerError( + 'fractionDigits should be greater than or equal to 0.' + ); } } diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index c6bf8e14055..949fd9a6243 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -307,7 +307,7 @@ describe('number', () => { }) ).toThrow( new FakerError( - 'multipleOf and fractionDigits cannot exist at the same time.' + 'multipleOf and fractionDigits cannot be set at the same time.' ) ); }); @@ -320,7 +320,7 @@ describe('number', () => { it('throws an error for negative fractionDigits', () => { expect(() => faker.number.float({ fractionDigits: -2 })).toThrow( - new FakerError('fractionDigits should be greater than 0.') + new FakerError('fractionDigits should be greater than or equal to 0.') ); }); From fe998196a7192741eb4747042eb01e10463d6cbf Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sun, 14 Jan 2024 11:53:09 +0100 Subject: [PATCH 24/26] chore: use consistent nullish check --- src/modules/number/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 32a8541b85a..84f70615228 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -168,7 +168,7 @@ export class NumberModule extends SimpleModuleBase { (fractionDigits == null ? undefined : 10 ** -fractionDigits), } = options; - if (precision !== undefined) { + if (precision != null) { deprecated({ deprecated: 'faker.number.float({ precision })', proposed: 'faker.number.float({ multipleOf })', @@ -185,8 +185,8 @@ export class NumberModule extends SimpleModuleBase { throw new FakerError(`Max ${max} should be greater than min ${min}.`); } - if (fractionDigits !== undefined) { - if (typeof originalMultipleOf === 'number') { + if (fractionDigits != null) { + if (originalMultipleOf != null) { throw new FakerError( 'multipleOf and fractionDigits cannot be set at the same time.' ); @@ -203,7 +203,7 @@ export class NumberModule extends SimpleModuleBase { } } - if (multipleOf !== undefined) { + if (multipleOf != null) { if (multipleOf <= 0) { // TODO @xDivisionByZerox: Clean up in v9.0 throw new FakerError(`multipleOf/precision should be greater than 0.`); From e1d13a5a97574fceabb0c1e5b56657b1cb11475d Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Thu, 18 Jan 2024 11:31:41 +0100 Subject: [PATCH 25/26] chore: apply suggestions --- src/modules/number/index.ts | 5 +++-- test/modules/datatype.spec.ts | 12 +++++++++--- test/modules/number.spec.ts | 7 ++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/modules/number/index.ts b/src/modules/number/index.ts index 84f70615228..cb32938220f 100644 --- a/src/modules/number/index.ts +++ b/src/modules/number/index.ts @@ -97,9 +97,10 @@ export class NumberModule extends SimpleModuleBase { * @param options.multipleOf The generated number will be a multiple of this property. * This property can be used to limit the result to a specific number of decimal digits. * For example `0.01` will round to 2 decimal points. - * If multipleOf is passed, the upper bound is inclusive and the `fractionDigits` option has to be excluded. + * If `multipleOf` is passed, the upper bound is inclusive. + * This option is incompatible with the `fractionDigits` option. * @param options.fractionDigits The maximum number of digits to appear after the decimal point. - * This parameter has to be excluded if `multipleOf` is provided. + * This option is incompatible with the `multipleOf` option. * * @throws When `min` is greater than `max`. * @throws When `precision` is negative. diff --git a/test/modules/datatype.spec.ts b/test/modules/datatype.spec.ts index 66ed6d3c55f..3008a27111f 100644 --- a/test/modules/datatype.spec.ts +++ b/test/modules/datatype.spec.ts @@ -321,7 +321,9 @@ describe('datatype', () => { expect(() => { faker.datatype.float({ min, max }); - }).toThrowError(`Max ${max} should be greater than min ${min}.`); + }).toThrow( + new FakerError(`Max ${max} should be greater than min ${min}.`) + ); }); it('should throw when precision <= 0', () => { @@ -330,10 +332,14 @@ describe('datatype', () => { expect(() => { faker.datatype.float({ min, max, precision: 0 }); - }).toThrowError('multipleOf/precision should be greater than 0.'); + }).toThrow( + new FakerError('multipleOf/precision should be greater than 0.') + ); expect(() => { faker.datatype.float({ min, max, precision: -1 }); - }).toThrowError('multipleOf/precision should be greater than 0.'); + }).toThrow( + new FakerError('multipleOf/precision should be greater than 0.') + ); }); }); diff --git a/test/modules/number.spec.ts b/test/modules/number.spec.ts index 949fd9a6243..922819c094a 100644 --- a/test/modules/number.spec.ts +++ b/test/modules/number.spec.ts @@ -286,8 +286,9 @@ describe('number', () => { expect(results).toEqual([0, 0.4, 0.8, 1.2, 1.6]); }); - it('provides numbers with an exact fractional digits', () => { - for (let i = 0; i < 100; i++) { + it.each(times(100))( + 'provides numbers with an exact fractional digits', + () => { const actual = faker.number.float({ min: 0.5, max: 0.99, @@ -295,7 +296,7 @@ describe('number', () => { }); expect(actual).toBe(Number(actual.toFixed(2))); } - }); + ); it('throws an error if fractionDigits and multipleOf is provided at the same time', () => { expect(() => From 0e9171c723a476a8808ac6acd384e0a7cd728113 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Thu, 18 Jan 2024 11:38:56 +0100 Subject: [PATCH 26/26] Update test/modules/datatype.spec.ts --- test/modules/datatype.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/modules/datatype.spec.ts b/test/modules/datatype.spec.ts index 3008a27111f..7a75ce58066 100644 --- a/test/modules/datatype.spec.ts +++ b/test/modules/datatype.spec.ts @@ -316,8 +316,8 @@ describe('datatype', () => { }); it('should throw when min > max', () => { - const min = 2; - const max = 1; + const min = 10; + const max = 9; expect(() => { faker.datatype.float({ min, max });