diff options
| author | Eric Cheng <[email protected]> | 2024-03-01 17:23:52 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-03-01 23:23:52 +0100 |
| commit | 9348138893bb95faa5037c653443fbd525ce2939 (patch) | |
| tree | 8823ebdf2f7ea6e934c7e407f0f6b497dffeddba | |
| parent | 8e880c15da8901eb4694273a79ea4fcd908d1c3f (diff) | |
| download | faker-9348138893bb95faa5037c653443fbd525ce2939.tar.xz faker-9348138893bb95faa5037c653443fbd525ce2939.zip | |
fix(commerce): return fractional prices (#2458)
Co-authored-by: ST-DDT <[email protected]>
| -rw-r--r-- | docs/guide/upgrading_v9/2458.md | 15 | ||||
| -rw-r--r-- | src/modules/commerce/index.ts | 93 | ||||
| -rw-r--r-- | test/modules/__snapshots__/commerce.spec.ts.snap | 72 | ||||
| -rw-r--r-- | test/modules/commerce.spec.ts | 33 |
4 files changed, 161 insertions, 52 deletions
diff --git a/docs/guide/upgrading_v9/2458.md b/docs/guide/upgrading_v9/2458.md new file mode 100644 index 00000000..172e7afc --- /dev/null +++ b/docs/guide/upgrading_v9/2458.md @@ -0,0 +1,15 @@ +### Prices now return more price-like values + +The `faker.commerce.price` method now produces values, that also return fractional values. + +Old price: 828.00 +New price: 828.59 + +The last digit of the price will adjusted to be more price-like: + +- 50% of the time: `9` +- 30% of the time: `5` +- 10% of the time: `0` +- 10% of the time: a random digit from `0` to `9` + +We plan to rethink this method some more in the future: [#2579](https://github.com/faker-js/faker/issues/2579) diff --git a/src/modules/commerce/index.ts b/src/modules/commerce/index.ts index 6750299b..fa3efb26 100644 --- a/src/modules/commerce/index.ts +++ b/src/modules/commerce/index.ts @@ -116,6 +116,13 @@ export class CommerceModule extends ModuleBase { /** * Generates a price between min and max (inclusive). * + * To better represent real-world prices, when `options.dec` is greater than `0`, the final decimal digit in the returned string will be generated as follows: + * + * - 50% of the time: `9` + * - 30% of the time: `5` + * - 10% of the time: `0` + * - 10% of the time: a random digit from `0` to `9` + * * @param options An options object. * @param options.min The minimum price. Defaults to `1`. * @param options.max The maximum price. Defaults to `1000`. @@ -123,9 +130,9 @@ export class CommerceModule extends ModuleBase { * @param options.symbol The currency value to use. Defaults to `''`. * * @example - * faker.commerce.price() // 828.00 - * faker.commerce.price({ min: 100 }) // 904.00 - * faker.commerce.price({ min: 100, max: 200 }) // 154.00 + * faker.commerce.price() // 828.07 + * faker.commerce.price({ min: 100 }) // 904.19 + * faker.commerce.price({ min: 100, max: 200 }) // 154.55 * faker.commerce.price({ min: 100, max: 200, dec: 0 }) // 133 * faker.commerce.price({ min: 100, max: 200, dec: 0, symbol: '$' }) // $114 * @@ -160,15 +167,22 @@ export class CommerceModule extends ModuleBase { /** * Generates a price between min and max (inclusive). * + * To better represent real-world prices, when `options.dec` is greater than `0`, the final decimal digit in the returned string will be generated as follows: + * + * - 50% of the time: `9` + * - 30% of the time: `5` + * - 10% of the time: `0` + * - 10% of the time: a random digit from `0` to `9` + * * @param min The minimum price. Defaults to `1`. * @param max The maximum price. Defaults to `1000`. * @param dec The number of decimal places. Defaults to `2`. * @param symbol The currency value to use. Defaults to `''`. * * @example - * faker.commerce.price() // 828.00 - * faker.commerce.price(100) // 904.00 - * faker.commerce.price(100, 200) // 154.00 + * faker.commerce.price() // 828.07 + * faker.commerce.price(100) // 904.19 + * faker.commerce.price(100, 200) // 154.55 * faker.commerce.price(100, 200, 0) // 133 * faker.commerce.price(100, 200, 0, '$') // $114 * @@ -180,7 +194,14 @@ export class CommerceModule extends ModuleBase { /** * Generates a price between min and max (inclusive). * - * @param options The minimum price or on options object. + * To better represent real-world prices, when `options.dec` is greater than `0`, the final decimal digit in the returned string will be generated as follows: + * + * - 50% of the time: `9` + * - 30% of the time: `5` + * - 10% of the time: `0` + * - 10% of the time: a random digit from `0` to `9` + * + * @param options The minimum price or an options object. * @param options.min The minimum price. Defaults to `1`. * @param options.max The maximum price. Defaults to `1000`. * @param options.dec The number of decimal places. Defaults to `2`. @@ -190,9 +211,9 @@ export class CommerceModule extends ModuleBase { * @param legacySymbol The currency value to use. This argument is deprecated. Defaults to `''`. * * @example - * faker.commerce.price() // 828.00 - * faker.commerce.price({ min: 100 }) // 904.00 - * faker.commerce.price({ min: 100, max: 200 }) // 154.00 + * faker.commerce.price() // 828.07 + * faker.commerce.price({ min: 100 }) // 904.19 + * faker.commerce.price({ min: 100, max: 200 }) // 154.55 * faker.commerce.price({ min: 100, max: 200, dec: 0 }) // 133 * faker.commerce.price({ min: 100, max: 200, dec: 0, symbol: '$' }) // $114 * @@ -234,7 +255,14 @@ export class CommerceModule extends ModuleBase { /** * Generates a price between min and max (inclusive). * - * @param options The minimum price or on options object. + * To better represent real-world prices, when `options.dec` is greater than `0`, the final decimal digit in the returned string will be generated as follows: + * + * - 50% of the time: `9` + * - 30% of the time: `5` + * - 10% of the time: `0` + * - 10% of the time: a random digit from `0` to `9` + * + * @param options The minimum price or an options object. * @param options.min The minimum price. Defaults to `1`. * @param options.max The maximum price. Defaults to `1000`. * @param options.dec The number of decimal places. Defaults to `2`. @@ -244,9 +272,9 @@ export class CommerceModule extends ModuleBase { * @param legacySymbol The currency value to use. This argument is deprecated. Defaults to `''`. * * @example - * faker.commerce.price() // 828.00 - * faker.commerce.price({ min: 100 }) // 904.00 - * faker.commerce.price({ min: 100, max: 200 }) // 154.00 + * faker.commerce.price() // 828.07 + * faker.commerce.price({ min: 100 }) // 904.19 + * faker.commerce.price({ min: 100, max: 200 }) // 154.55 * faker.commerce.price({ min: 100, max: 200, dec: 0 }) // 133 * faker.commerce.price({ min: 100, max: 200, dec: 0, symbol: '$' }) // $114 * @@ -286,10 +314,41 @@ export class CommerceModule extends ModuleBase { return `${symbol}0`; } - // TODO @Shinigami92 2022-11-24: https://github.com/faker-js/faker/issues/350 - const randValue = this.faker.number.int({ min, max }); + if (min === max) { + return `${symbol}${min.toFixed(dec)}`; + } + + const generated = this.faker.number.float({ + min, + max, + fractionDigits: dec, + }); + + if (dec === 0) { + return `${symbol}${generated.toFixed(dec)}`; + } + + const oldLastDigit = (generated * 10 ** dec) % 10; + const newLastDigit = this.faker.helpers.weightedArrayElement([ + { weight: 5, value: 9 }, + { weight: 3, value: 5 }, + { weight: 1, value: 0 }, + { + weight: 1, + value: this.faker.number.int({ min: 0, max: 9 }), + }, + ]); + + const fraction = (1 / 10) ** dec; + const oldLastDigitValue = oldLastDigit * fraction; + const newLastDigitValue = newLastDigit * fraction; + const combined = generated - oldLastDigitValue + newLastDigitValue; + + if (min <= combined && combined <= max) { + return `${symbol}${combined.toFixed(dec)}`; + } - return symbol + randValue.toFixed(dec); + return `${symbol}${generated.toFixed(dec)}`; } /** diff --git a/test/modules/__snapshots__/commerce.spec.ts.snap b/test/modules/__snapshots__/commerce.spec.ts.snap index a494172a..b7841fd5 100644 --- a/test/modules/__snapshots__/commerce.spec.ts.snap +++ b/test/modules/__snapshots__/commerce.spec.ts.snap @@ -12,27 +12,29 @@ exports[`commerce > 42 > isbn > with variant 10 and space separators 1`] = `"0 9 exports[`commerce > 42 > isbn > with variant 13 1`] = `"978-0-9751108-6-7"`; -exports[`commerce > 42 > price > noArgs 1`] = `"375.00"`; +exports[`commerce > 42 > price > noArgs 1`] = `"375.15"`; -exports[`commerce > 42 > price > with max 1`] = `"375.00"`; +exports[`commerce > 42 > price > with float min and float max option 1`] = `"1.05"`; -exports[`commerce > 42 > price > with max option 1`] = `"501.00"`; +exports[`commerce > 42 > price > with max 1`] = `"375.15"`; -exports[`commerce > 42 > price > with min 1`] = `"406.00"`; +exports[`commerce > 42 > price > with max option 1`] = `"501.35"`; -exports[`commerce > 42 > price > with min and max 1`] = `"69.00"`; +exports[`commerce > 42 > price > with min 1`] = `"405.85"`; -exports[`commerce > 42 > price > with min and max and decimals 1`] = `"69.0000"`; +exports[`commerce > 42 > price > with min and max 1`] = `"68.75"`; -exports[`commerce > 42 > price > with min and max and decimals and symbol 1`] = `"$69.0000"`; +exports[`commerce > 42 > price > with min and max and decimals 1`] = `"68.7275"`; -exports[`commerce > 42 > price > with min and max and decimals and symbol option 1`] = `"$69.0000"`; +exports[`commerce > 42 > price > with min and max and decimals and symbol 1`] = `"$68.7275"`; -exports[`commerce > 42 > price > with min and max and decimals option 1`] = `"69.0000"`; +exports[`commerce > 42 > price > with min and max and decimals and symbol option 1`] = `"$68.7275"`; -exports[`commerce > 42 > price > with min and max option 1`] = `"69.00"`; +exports[`commerce > 42 > price > with min and max and decimals option 1`] = `"68.7275"`; -exports[`commerce > 42 > price > with min option 1`] = `"401.00"`; +exports[`commerce > 42 > price > with min and max option 1`] = `"68.75"`; + +exports[`commerce > 42 > price > with min option 1`] = `"400.85"`; exports[`commerce > 42 > product 1`] = `"Pants"`; @@ -56,27 +58,29 @@ exports[`commerce > 1211 > isbn > with variant 10 and space separators 1`] = `"1 exports[`commerce > 1211 > isbn > with variant 13 1`] = `"978-1-82966-736-0"`; -exports[`commerce > 1211 > price > noArgs 1`] = `"929.00"`; +exports[`commerce > 1211 > price > noArgs 1`] = `"928.69"`; + +exports[`commerce > 1211 > price > with float min and float max option 1`] = `"1.10"`; -exports[`commerce > 1211 > price > with max 1`] = `"929.00"`; +exports[`commerce > 1211 > price > with max 1`] = `"928.69"`; -exports[`commerce > 1211 > price > with max option 1`] = `"1242.00"`; +exports[`commerce > 1211 > price > with max option 1`] = `"1241.59"`; -exports[`commerce > 1211 > price > with min 1`] = `"933.00"`; +exports[`commerce > 1211 > price > with min 1`] = `"932.19"`; -exports[`commerce > 1211 > price > with min and max 1`] = `"97.00"`; +exports[`commerce > 1211 > price > with min and max 1`] = `"96.49"`; -exports[`commerce > 1211 > price > with min and max and decimals 1`] = `"97.0000"`; +exports[`commerce > 1211 > price > with min and max and decimals 1`] = `"96.4269"`; -exports[`commerce > 1211 > price > with min and max and decimals and symbol 1`] = `"$97.0000"`; +exports[`commerce > 1211 > price > with min and max and decimals and symbol 1`] = `"$96.4269"`; -exports[`commerce > 1211 > price > with min and max and decimals and symbol option 1`] = `"$97.0000"`; +exports[`commerce > 1211 > price > with min and max and decimals and symbol option 1`] = `"$96.4269"`; -exports[`commerce > 1211 > price > with min and max and decimals option 1`] = `"97.0000"`; +exports[`commerce > 1211 > price > with min and max and decimals option 1`] = `"96.4269"`; -exports[`commerce > 1211 > price > with min and max option 1`] = `"97.00"`; +exports[`commerce > 1211 > price > with min and max option 1`] = `"96.49"`; -exports[`commerce > 1211 > price > with min option 1`] = `"932.00"`; +exports[`commerce > 1211 > price > with min option 1`] = `"931.59"`; exports[`commerce > 1211 > product 1`] = `"Sausages"`; @@ -100,27 +104,29 @@ exports[`commerce > 1337 > isbn > with variant 10 and space separators 1`] = `"0 exports[`commerce > 1337 > isbn > with variant 13 1`] = `"978-0-12-435297-1"`; -exports[`commerce > 1337 > price > noArgs 1`] = `"263.00"`; +exports[`commerce > 1337 > price > noArgs 1`] = `"262.79"`; + +exports[`commerce > 1337 > price > with float min and float max option 1`] = `"1.09"`; -exports[`commerce > 1337 > price > with max 1`] = `"263.00"`; +exports[`commerce > 1337 > price > with max 1`] = `"262.79"`; -exports[`commerce > 1337 > price > with max option 1`] = `"351.00"`; +exports[`commerce > 1337 > price > with max option 1`] = `"351.09"`; -exports[`commerce > 1337 > price > with min 1`] = `"299.00"`; +exports[`commerce > 1337 > price > with min 1`] = `"298.99"`; -exports[`commerce > 1337 > price > with min and max 1`] = `"63.00"`; +exports[`commerce > 1337 > price > with min and max 1`] = `"63.19"`; -exports[`commerce > 1337 > price > with min and max and decimals 1`] = `"63.0000"`; +exports[`commerce > 1337 > price > with min and max and decimals 1`] = `"63.1019"`; -exports[`commerce > 1337 > price > with min and max and decimals and symbol 1`] = `"$63.0000"`; +exports[`commerce > 1337 > price > with min and max and decimals and symbol 1`] = `"$63.1019"`; -exports[`commerce > 1337 > price > with min and max and decimals and symbol option 1`] = `"$63.0000"`; +exports[`commerce > 1337 > price > with min and max and decimals and symbol option 1`] = `"$63.1019"`; -exports[`commerce > 1337 > price > with min and max and decimals option 1`] = `"63.0000"`; +exports[`commerce > 1337 > price > with min and max and decimals option 1`] = `"63.1019"`; -exports[`commerce > 1337 > price > with min and max option 1`] = `"63.00"`; +exports[`commerce > 1337 > price > with min and max option 1`] = `"63.19"`; -exports[`commerce > 1337 > price > with min option 1`] = `"293.00"`; +exports[`commerce > 1337 > price > with min option 1`] = `"293.09"`; exports[`commerce > 1337 > product 1`] = `"Ball"`; diff --git a/test/modules/commerce.spec.ts b/test/modules/commerce.spec.ts index e4a5c821..9ac013c2 100644 --- a/test/modules/commerce.spec.ts +++ b/test/modules/commerce.spec.ts @@ -27,6 +27,7 @@ describe('commerce', () => { .it('with min option', { min: 42 }) .it('with max option', { max: 1337 }) .it('with min and max option', { min: 50, max: 100 }) + .it('with float min and float max option', { min: 1, max: 1.1 }) .it('with min and max and decimals option', { min: 50, max: 100, @@ -124,14 +125,42 @@ describe('commerce', () => { const price = faker.commerce.price(100, 100, 1); expect(price).toBeTruthy(); - expect(price, 'the price should be equal 100.0').toBe('100.0'); + expect(price, 'the price should equal 100.0').toBe('100.0'); }); it('should handle argument dec = 0', () => { const price = faker.commerce.price(100, 100, 0); expect(price).toBeTruthy(); - expect(price, 'the price should be equal 100').toBe('100'); + expect(price, 'the price should equal 100').toBe('100'); + }); + + it('should return decimal values between min and max', () => { + const result = faker.helpers.multiple( + () => faker.commerce.price(1, 1.1, 2), + { count: 50 } + ); + + for (const price of result) { + const parsedPrice = Number.parseFloat(price); + + expect(parsedPrice).toBeLessThanOrEqual(1.1); + expect(parsedPrice).toBeGreaterThanOrEqual(1); + } + }); + + it('should return values with three decimal places between min and max', () => { + const result = faker.helpers.multiple( + () => faker.commerce.price({ min: 0.001, max: 0.009, dec: 3 }), + { count: 50 } + ); + + for (const price of result) { + const parsedPrice = Number.parseFloat(price); + + expect(parsedPrice).toBeLessThanOrEqual(0.009); + expect(parsedPrice).toBeGreaterThanOrEqual(0.001); + } }); }); |
