Files
overleaf-cep/services/web/test/unit/src/Subscription/SubscriptionFormattersTests.js
T
Antoine Clausse 7ed5000e29 [web] Use localized number formatting for currencies (#17622)
* Add a unit test on `SubscriptionFormatters.formatPrice`

* Add JSDoc to `formatPrice`

Also: Name the functions before exporting:
This fixes my IDE (WebStorm) navigation

* Make `'USD'` the default param instead of reassigning

* Create `formatCurrency` function

* Use `formatCurrency` in SubscriptionFormatters

* Use an `isNoCentsCurrency` logic for `CLP` `JPY` `KRW` `VND`

And remove custom `CLP` logic and locale

* Add `locale` param to `formatPrice`

* Generate `groups.json` and `localizedPlanPricing.json`

```
bin/exec web node ./scripts/recurly/recurly_prices.js --download -o prices.json
bin/exec web node ./scripts/plan-prices/plans.js -f ../../prices.json -o dir
```

* Update scripts/plan-prices/plans.js to generate numbers instead of localized amounts

* Generate `groups.json` and `localizedPlanPricing.json`

```
bin/exec web node ./scripts/recurly/recurly_prices.js --download -o prices.json
bin/exec web node ./scripts/plan-prices/plans.js -f ../../prices.json -o dir
```

* Remove generation of `plans.json`

As /services/web/frontend/js/main/plans.js was removed in https://github.com/overleaf/internal/pull/12593

* Sort currencies in alphabetical order in scripts/plan-prices/plans.js

* Generate `groups.json` and `localizedPlanPricing.json`

```
bin/exec web node ./scripts/recurly/recurly_prices.js --download -o prices.json
bin/exec web node ./scripts/plan-prices/plans.js -f ../../prices.json -o dir
```

* Use `formatCurrency` in price-summary.tsx

* Use `formatCurrency` in Subscription Pug files

* Fix unit tests SubscriptionHelperTests.js

* Remove unused `currencySymbol`

* Change to `formatCurrency` in other React components

* Add `CurrencyCode` JSDoc types

* Duplicate `formatCurrency` into services/web/app/src/util

* Wrap tests in a top-level describe block

* Use `narrowSymbol`

* Fix tests with `narrowSymbol` expects

* Revert deletion of old `formatPrice` in SubscriptionFormatters.js

* Rename `formatCurrency` -> `formatCurrencyLocalized`

* Revert deletion of `CurrencySymbol`

* Add split-test in SubscriptionController.js

* Add split-test in SubscriptionViewModelBuilder.js

* Add split-test in plans

* Add split-test in subscription-dashboard-context.tsx

* Add split-test in 4 more components

* Update tests

* Show currency and payment methods in interstitial page

* Fix `–` being printed. Use `–` instead

* Fix test with NOK

* Storybook: Fix missing `SplitTestProvider`

* Storybook: Revert "Remove unused `currencySymbol`"

This reverts commit e55387d4753f97bbf8e39e0fdc3ad17312122aaa.

* Replace `getSplitTestVariant` by `useSplitTestContext`

* Use parameterize currencyFormat in `generateInitialLocalizedGroupPrice`

* Fixup import paths of `formatCurrencyLocalized`

* Replace `% 1 === 0` by `Number.isInteger`

* Add comment explaining that any combinations of languages/currencies could happen

* Fixup after rebase: import `useSplitTestContext`

* Revert "Remove SplitTestProvider from subscription root"

This reverts commit be9f378fda715b86589ab0759737581c72321d87.

* Revert "Remove split test provider from some tests"

This reverts commit 985522932b550cfd38fa6a4f4c3d2ebaee6ff7df.

GitOrigin-RevId: 59a83cbbe0f7cc7e45f189c654e23fcf9bfa37af
2024-04-19 08:03:54 +00:00

301 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const chai = require('chai')
const SubscriptionFormatters = require('../../../../app/src/Features/Subscription/SubscriptionFormatters')
const { expect } = chai
/*
Users can select any language we support, regardless of the country where they are located.
Which mean that any combination of "supported language"-"supported currency" can be displayed
on the user's screen.
Users located in the USA visiting https://fr.overleaf.com/user/subscription/plans
should see amounts in USD (because of their IP address),
but with French text, number formatting and currency formats (because of language choice).
(e.g. 1 000,00 $)
Users located in the France visiting https://www.overleaf.com/user/subscription/plans
should see amounts in EUR (because of their IP address),
but with English text, number formatting and currency formats (because of language choice).
(e.g. €1,000.00)
*/
describe('SubscriptionFormatters.formatPrice', function () {
describe('en', function () {
const format = currency => priceInCents =>
SubscriptionFormatters.formatPriceLocalized(priceInCents, currency)
describe('USD', function () {
const formatUSD = format('USD')
it('should format basic amounts', function () {
expect(formatUSD(0)).to.equal('$0.00')
expect(formatUSD(1234)).to.equal('$12.34')
})
it('should format thousand separators', function () {
expect(formatUSD(100_000)).to.equal('$1,000.00')
expect(formatUSD(9_876_543_210)).to.equal('$98,765,432.10')
})
it('should format negative amounts', function () {
expect(formatUSD(-1)).to.equal('-$0.01')
expect(formatUSD(-1234)).to.equal('-$12.34')
})
})
describe('EUR', function () {
const formatEUR = format('EUR')
it('should format basic amounts', function () {
expect(formatEUR(0)).to.equal('€0.00')
expect(formatEUR(1234)).to.equal('€12.34')
})
it('should format thousand separators', function () {
expect(formatEUR(100_000)).to.equal('€1,000.00')
expect(formatEUR(9_876_543_210)).to.equal('€98,765,432.10')
})
it('should format negative amounts', function () {
expect(formatEUR(-1)).to.equal('-€0.01')
expect(formatEUR(-1234)).to.equal('-€12.34')
})
})
describe('HUF', function () {
const formatHUF = format('HUF')
it('should format basic amounts', function () {
expect(formatHUF(0)).to.equal('Ft 0.00')
expect(formatHUF(1234)).to.equal('Ft 12.34')
})
it('should format thousand separators', function () {
expect(formatHUF(100_000)).to.equal('Ft 1,000.00')
expect(formatHUF(9_876_543_210)).to.equal('Ft 98,765,432.10')
})
it('should format negative amounts', function () {
expect(formatHUF(-1)).to.equal('-Ft 0.01')
expect(formatHUF(-1234)).to.equal('-Ft 12.34')
})
})
describe('CLP', function () {
const formatCLP = format('CLP')
it('should format basic amounts', function () {
expect(formatCLP(0)).to.equal('$0')
expect(formatCLP(1234)).to.equal('$1,234')
})
it('should format thousand separators', function () {
expect(formatCLP(100_000)).to.equal('$100,000')
expect(formatCLP(9_876_543_210)).to.equal('$9,876,543,210')
})
it('should format negative amounts', function () {
expect(formatCLP(-1)).to.equal('-$1')
expect(formatCLP(-1234)).to.equal('-$1,234')
})
})
describe('all currencies', function () {
it('should format 100 "minimal atomic units"', function () {
const amount = 100
// "no cents currencies"
expect(format('CLP')(amount)).to.equal('$100')
expect(format('JPY')(amount)).to.equal('¥100')
expect(format('KRW')(amount)).to.equal('₩100')
expect(format('VND')(amount)).to.equal('₫100')
// other currencies
expect(format('AUD')(amount)).to.equal('$1.00')
expect(format('BRL')(amount)).to.equal('R$1.00')
expect(format('CAD')(amount)).to.equal('$1.00')
expect(format('CHF')(amount)).to.equal('CHF 1.00')
expect(format('CNY')(amount)).to.equal('¥1.00')
expect(format('COP')(amount)).to.equal('$1.00')
expect(format('DKK')(amount)).to.equal('kr 1.00')
expect(format('EUR')(amount)).to.equal('€1.00')
expect(format('GBP')(amount)).to.equal('£1.00')
expect(format('HUF')(amount)).to.equal('Ft 1.00')
expect(format('IDR')(amount)).to.equal('Rp 1.00')
expect(format('INR')(amount)).to.equal('₹1.00')
expect(format('MXN')(amount)).to.equal('$1.00')
expect(format('MYR')(amount)).to.equal('RM 1.00')
expect(format('NOK')(amount)).to.equal('kr 1.00')
expect(format('NZD')(amount)).to.equal('$1.00')
expect(format('PEN')(amount)).to.equal('PEN 1.00')
expect(format('PHP')(amount)).to.equal('₱1.00')
expect(format('SEK')(amount)).to.equal('kr 1.00')
expect(format('SGD')(amount)).to.equal('$1.00')
expect(format('THB')(amount)).to.equal('฿1.00')
expect(format('USD')(amount)).to.equal('$1.00')
})
it('should format 123_456_789.987_654 "minimal atomic units"', function () {
const amount = 123_456_789.987_654
// "no cents currencies"
expect(format('CLP')(amount)).to.equal('$123,456,790')
expect(format('JPY')(amount)).to.equal('¥123,456,790')
expect(format('KRW')(amount)).to.equal('₩123,456,790')
expect(format('VND')(amount)).to.equal('₫123,456,790')
// other currencies
expect(format('AUD')(amount)).to.equal('$1,234,567.90')
expect(format('BRL')(amount)).to.equal('R$1,234,567.90')
expect(format('CAD')(amount)).to.equal('$1,234,567.90')
expect(format('CHF')(amount)).to.equal('CHF 1,234,567.90')
expect(format('CNY')(amount)).to.equal('¥1,234,567.90')
expect(format('COP')(amount)).to.equal('$1,234,567.90')
expect(format('DKK')(amount)).to.equal('kr 1,234,567.90')
expect(format('EUR')(amount)).to.equal('€1,234,567.90')
expect(format('GBP')(amount)).to.equal('£1,234,567.90')
expect(format('HUF')(amount)).to.equal('Ft 1,234,567.90')
expect(format('IDR')(amount)).to.equal('Rp 1,234,567.90')
expect(format('INR')(amount)).to.equal('₹1,234,567.90')
expect(format('MXN')(amount)).to.equal('$1,234,567.90')
expect(format('MYR')(amount)).to.equal('RM 1,234,567.90')
expect(format('NOK')(amount)).to.equal('kr 1,234,567.90')
expect(format('NZD')(amount)).to.equal('$1,234,567.90')
expect(format('PEN')(amount)).to.equal('PEN 1,234,567.90')
expect(format('PHP')(amount)).to.equal('₱1,234,567.90')
expect(format('SEK')(amount)).to.equal('kr 1,234,567.90')
expect(format('SGD')(amount)).to.equal('$1,234,567.90')
expect(format('THB')(amount)).to.equal('฿1,234,567.90')
expect(format('USD')(amount)).to.equal('$1,234,567.90')
})
})
})
describe('fr', function () {
const format = currency => priceInCents =>
SubscriptionFormatters.formatPriceLocalized(priceInCents, currency, 'fr')
describe('USD', function () {
const formatUSD = format('USD')
it('should format basic amounts', function () {
expect(formatUSD(0)).to.equal('0,00 $')
expect(formatUSD(1234)).to.equal('12,34 $')
})
it('should format thousand separators', function () {
expect(formatUSD(100_000)).to.equal('1000,00 $')
expect(formatUSD(9_876_543_210)).to.equal('98765432,10 $')
})
it('should format negative amounts', function () {
expect(formatUSD(-1)).to.equal('-0,01 $')
expect(formatUSD(-1234)).to.equal('-12,34 $')
})
})
describe('EUR', function () {
const formatEUR = format('EUR')
it('should format basic amounts', function () {
expect(formatEUR(0)).to.equal('0,00 €')
expect(formatEUR(1234)).to.equal('12,34 €')
})
it('should format thousand separators', function () {
expect(formatEUR(100_000)).to.equal('1000,00 €')
expect(formatEUR(9_876_543_210)).to.equal('98765432,10 €')
})
it('should format negative amounts', function () {
expect(formatEUR(-1)).to.equal('-0,01 €')
expect(formatEUR(-1234)).to.equal('-12,34 €')
})
})
describe('HUF', function () {
const formatHUF = format('HUF')
it('should format basic amounts', function () {
expect(formatHUF(0)).to.equal('0,00 Ft')
expect(formatHUF(1234)).to.equal('12,34 Ft')
})
it('should format thousand separators', function () {
expect(formatHUF(100_000)).to.equal('1000,00 Ft')
expect(formatHUF(9_876_543_210)).to.equal('98765432,10 Ft')
})
it('should format negative amounts', function () {
expect(formatHUF(-1)).to.equal('-0,01 Ft')
expect(formatHUF(-1234)).to.equal('-12,34 Ft')
})
})
describe('CLP', function () {
const formatCLP = format('CLP')
it('should format basic amounts', function () {
expect(formatCLP(0)).to.equal('0 $')
expect(formatCLP(1234)).to.equal('1234 $')
})
it('should format thousand separators', function () {
expect(formatCLP(100_000)).to.equal('100000 $')
expect(formatCLP(9_876_543_210)).to.equal('9876543210 $')
})
it('should format negative amounts', function () {
expect(formatCLP(-1)).to.equal('-1 $')
expect(formatCLP(-1234)).to.equal('-1234 $')
})
})
describe('all currencies', function () {
it('should format 100 "minimal atomic units"', function () {
const amount = 100
// "no cents currencies"
expect(format('CLP')(amount)).to.equal('100 $')
expect(format('JPY')(amount)).to.equal('100 ¥')
expect(format('KRW')(amount)).to.equal('100 ₩')
expect(format('VND')(amount)).to.equal('100 ₫')
// other currencies
expect(format('AUD')(amount)).to.equal('1,00 $')
expect(format('BRL')(amount)).to.equal('1,00 R$')
expect(format('CAD')(amount)).to.equal('1,00 $')
expect(format('CHF')(amount)).to.equal('1,00 CHF')
expect(format('CNY')(amount)).to.equal('1,00 ¥')
expect(format('COP')(amount)).to.equal('1,00 $')
expect(format('EUR')(amount)).to.equal('1,00 €')
expect(format('GBP')(amount)).to.equal('1,00 £')
expect(format('USD')(amount)).to.equal('1,00 $')
})
it('should format 123_456_789.987_654 "minimal atomic units"', function () {
const amount = 123_456_789.987_654
// "no cents currencies"
expect(format('CLP')(amount)).to.equal('123456790 $')
expect(format('JPY')(amount)).to.equal('123456790 ¥')
expect(format('KRW')(amount)).to.equal('123456790 ₩')
expect(format('VND')(amount)).to.equal('123456790 ₫')
// other currencies
expect(format('AUD')(amount)).to.equal('1234567,90 $')
expect(format('BRL')(amount)).to.equal('1234567,90 R$')
expect(format('CAD')(amount)).to.equal('1234567,90 $')
expect(format('CHF')(amount)).to.equal('1234567,90 CHF')
expect(format('CNY')(amount)).to.equal('1234567,90 ¥')
expect(format('COP')(amount)).to.equal('1234567,90 $')
expect(format('EUR')(amount)).to.equal('1234567,90 €')
expect(format('GBP')(amount)).to.equal('1234567,90 £')
expect(format('USD')(amount)).to.equal('1234567,90 $')
})
})
})
})