Files
overleaf-cep/services/web/test/unit/src/Subscription/SubscriptionHelperTests.js
Antoine Clausse b2ef7a935f [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

265 lines
7.1 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 SandboxedModule = require('sandboxed-module')
const { expect } = require('chai')
const { formatCurrencyLocalized } = require('../../../../app/src/util/currency')
const modulePath =
'../../../../app/src/Features/Subscription/SubscriptionHelper'
const plans = {
expensive: {
planCode: 'expensive',
price_in_cents: 1500,
},
cheaper: {
planCode: 'cheaper',
price_in_cents: 500,
},
alsoCheap: {
plancode: 'also-cheap',
price_in_cents: 500,
},
expensiveGroup: {
plancode: 'group_expensive',
price_in_cents: 49500,
groupPlan: true,
},
cheapGroup: {
plancode: 'group_cheap',
price_in_cents: 1000,
groupPlan: true,
},
bad: {},
}
describe('SubscriptionHelper', function () {
beforeEach(function () {
this.INITIAL_LICENSE_SIZE = 2
this.settings = {
groupPlanModalOptions: {
currencySymbols: {
USD: '$',
CHF: 'Fr',
DKK: 'kr',
NOK: 'kr',
SEK: 'kr',
},
},
}
this.GroupPlansData = {
enterprise: {
collaborator: {
CHF: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 1000,
},
},
DKK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 2000,
},
},
SEK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 3000,
},
},
NOK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 4000,
},
},
USD: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 5000,
},
},
},
professional: {
CHF: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 10000,
},
},
DKK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 20000,
},
},
SEK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 30000,
},
},
NOK: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 40000,
},
},
USD: {
[this.INITIAL_LICENSE_SIZE]: {
price_in_cents: 50000,
},
},
},
},
}
this.SubscriptionHelper = SandboxedModule.require(modulePath, {
requires: {
'@overleaf/settings': this.settings,
'./GroupPlansData': this.GroupPlansData,
},
})
})
describe('shouldPlanChangeAtTermEnd', function () {
it('should return true if the new plan is less expensive', function () {
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
plans.expensive,
plans.cheaper
)
expect(changeAtTermEnd).to.be.true
})
it('should return false if the new plan is more exepensive', function () {
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
plans.cheaper,
plans.expensive
)
expect(changeAtTermEnd).to.be.false
})
it('should return false if the new plan is the same price', function () {
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
plans.cheaper,
plans.alsoCheap
)
expect(changeAtTermEnd).to.be.false
})
it('should return false if the change is from an individual plan to a more expensive group plan', function () {
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
plans.expensive,
plans.expensiveGroup
)
expect(changeAtTermEnd).to.be.false
})
it('should return true if the change is from an individual plan to a cheaper group plan', function () {
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
plans.expensive,
plans.cheapGroup
)
expect(changeAtTermEnd).to.be.true
})
})
describe('generateInitialLocalizedGroupPrice', function () {
describe('CHF currency', function () {
it('should return the correct localized price for every plan', function () {
const localizedPrice =
this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
'CHF',
'fr',
formatCurrencyLocalized
)
expect(localizedPrice).to.deep.equal({
price: {
collaborator: '10 CHF',
professional: '100 CHF',
},
pricePerUser: {
collaborator: '5 CHF',
professional: '50 CHF',
},
})
})
})
describe('DKK currency', function () {
it('should return the correct localized price for every plan', function () {
const localizedPrice =
this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
'DKK',
'da',
formatCurrencyLocalized
)
expect(localizedPrice).to.deep.equal({
price: {
collaborator: '20 kr.',
professional: '200 kr.',
},
pricePerUser: {
collaborator: '10 kr.',
professional: '100 kr.',
},
})
})
})
describe('SEK currency', function () {
it('should return the correct localized price for every plan', function () {
const localizedPrice =
this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
'SEK',
'sv',
formatCurrencyLocalized
)
expect(localizedPrice).to.deep.equal({
price: {
collaborator: '30 kr',
professional: '300 kr',
},
pricePerUser: {
collaborator: '15 kr',
professional: '150 kr',
},
})
})
})
describe('NOK currency', function () {
it('should return the correct localized price for every plan', function () {
const localizedPrice =
this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
'NOK',
// there seem to be possible inconsistencies with the CI
// maybe it depends on what languages are installed on the server?
'en',
formatCurrencyLocalized
)
expect(localizedPrice).to.deep.equal({
price: {
collaborator: 'kr 40',
professional: 'kr 400',
},
pricePerUser: {
collaborator: 'kr 20',
professional: 'kr 200',
},
})
})
})
describe('other supported currencies', function () {
it('should return the correct localized price for every plan', function () {
const localizedPrice =
this.SubscriptionHelper.generateInitialLocalizedGroupPrice(
'USD',
'en',
formatCurrencyLocalized
)
expect(localizedPrice).to.deep.equal({
price: {
collaborator: '$50',
professional: '$500',
},
pricePerUser: {
collaborator: '$25',
professional: '$250',
},
})
})
})
})
})