-
Notifications
You must be signed in to change notification settings - Fork 160
fix(explorer): formatPercentage - force "." decimal symbol for any locale #6598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(explorer): formatPercentage - force "." decimal symbol for any locale #6598
Conversation
|
@crutch12 is attempting to deploy a commit to the cow Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a test helper to mock Intl.NumberFormat, refactors two unit tests to run across multiple locales using dynamic imports and lifecycle hooks, and forces locale-agnostic decimal symbol in formatPercentage by passing Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
080d39c to
980db56
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/explorer/src/test/mockNumberLocale.ts (1)
1-4: Locale mock helper is simple and effective; consider forwarding args if needs growThis helper cleanly centralizes locale mocking and ensures tests see deterministic
Intl.NumberFormatbehavior per locale. One minor future‑proofing idea: if any code under test starts relying onIntl.NumberFormatoptions, you may want the mock implementation to accept and forward(...args)rather than ignoring parameters, so behavior stays closer to the real API; for the current use (just decimal symbol), this is fine.If you later expand tests that depend on
Intl.NumberFormatoptions (e.g.,maximumFractionDigits), double‑check that this mock still matches expectations.apps/explorer/src/test/utils/operator/orderFormatAmount.test.ts (1)
2-49: Order amount tests align well with new locale‑aware patternUsing
mockNumberLocale('en-US')inbeforeEachand dynamically importingformattedAmountafter the mock is applied is consistent with the new locale‑aware testing approach and should make these tests stable across developer locales. The type‑onlyTokenErc20import andisTokenErc20checks look correct for the simple ERC20 shape you’re exercising. If you want to tighten things further, you could drop the unnecessaryasynckeyword from tests that don’tawaitanything, but that’s purely cosmetic.Please re-run this suite with a non‑en‑US OS locale to confirm
formattedAmountassertions remain stable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/explorer/src/test/mockNumberLocale.ts(1 hunks)apps/explorer/src/test/utils/format/formatPercentage.spec.ts(2 hunks)apps/explorer/src/test/utils/operator/orderFormatAmount.test.ts(2 hunks)apps/explorer/src/utils/format.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx:23-33
Timestamp: 2025-07-24T16:42:53.154Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx, the use of toFixed(2) for percentage formatting in tooltip content is intentional and differs from the banner message formatting that uses toSignificant(2, undefined, Rounding.ROUND_DOWN). This formatting difference serves different UX purposes and should not be flagged as inconsistent.
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts:18-20
Timestamp: 2025-07-24T16:43:47.639Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts, the formatFeePercentage function uses ROUND_DOWN with toSignificant(2) for "at least X%" messaging. This ensures the displayed percentage is never higher than the actual fee, making the "at least" phrasing accurate. For example, if the actual fee is 25.4%, displaying "at least 25%" is correct, but "at least 26%" would be misleading.
📚 Learning: 2025-07-24T16:42:53.154Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx:23-33
Timestamp: 2025-07-24T16:42:53.154Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx, the use of toFixed(2) for percentage formatting in tooltip content is intentional and differs from the banner message formatting that uses toSignificant(2, undefined, Rounding.ROUND_DOWN). This formatting difference serves different UX purposes and should not be flagged as inconsistent.
Applied to files:
apps/explorer/src/utils/format.ts
📚 Learning: 2025-07-24T16:43:47.639Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts:18-20
Timestamp: 2025-07-24T16:43:47.639Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts, the formatFeePercentage function uses ROUND_DOWN with toSignificant(2) for "at least X%" messaging. This ensures the displayed percentage is never higher than the actual fee, making the "at least" phrasing accurate. For example, if the actual fee is 25.4%, displaying "at least 25%" is correct, but "at least 26%" would be misleading.
Applied to files:
apps/explorer/src/utils/format.ts
📚 Learning: 2025-03-10T16:03:11.687Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5495
File: apps/cowswap-frontend/src/legacy/state/orders/utils.ts:444-452
Timestamp: 2025-03-10T16:03:11.687Z
Learning: When working with large numbers in JavaScript, especially token amounts with many decimals, avoid conversions through Number() before BigInt to prevent precision loss. Instead, pass string representations directly to JSBI.BigInt().
Applied to files:
apps/explorer/src/utils/format.tsapps/explorer/src/test/utils/format/formatPercentage.spec.ts
📚 Learning: 2025-07-28T16:26:08.051Z
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6034
File: apps/cowswap-frontend-e2e/src/e2e/fiat-amounts.test.ts:44-47
Timestamp: 2025-07-28T16:26:08.051Z
Learning: In the cowswap codebase, using trivial placeholder tests like `it('should be true', () => { expect(true).to.be.true })` in e2e test files is an intentional pattern when disabling broken tests to keep CI green while maintaining build efficiency.
Applied to files:
apps/explorer/src/test/utils/operator/orderFormatAmount.test.ts
🧬 Code graph analysis (1)
apps/explorer/src/test/utils/format/formatPercentage.spec.ts (2)
apps/explorer/src/test/mockNumberLocale.ts (1)
mockNumberLocale(1-4)apps/explorer/src/utils/format.ts (1)
formatPercentage(114-134)
🔇 Additional comments (2)
apps/explorer/src/utils/format.ts (1)
125-133: formatPercentage: locale‑agnostic decimal looks correctUsing
isLocaleAware: falsehere keeps the existing rounding/threshold behavior while guaranteeing"."as the decimal separator for all locales, which matches the PR objective and the new tests. No other callers are affected since this is scoped to percentage display only.Please re-run the explorer test suite on a machine with a non‑en‑US locale to confirm there are no remaining locale‑dependent regressions.
apps/explorer/src/test/utils/format/formatPercentage.spec.ts (1)
3-42: Locale‑matrix tests and dynamic import pattern look solidThe test structure is good: you reset modules, mock
Intl.NumberFormatper locale, then dynamically importformatPercentage, which ensures each test runs under its intended locale context. Iterating over bothCASESandLOCALESgives strong coverage that the newisLocaleAware: falsebehavior consistently returns"."as the decimal separator regardless of environment locale.Keep an eye on runtime if more cases/locales are added; if the matrix grows large, consider
describe.each/it.eachto keep Jest output readable and focused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/explorer/src/test/utils/format/formatPercentage.spec.ts (1)
36-42: Dynamic import per-locale is a good way to bind the mocked IntlDeferring
import('../../../utils')until aftermockNumberLocale(locale)inside eachitensuresformatPercentageis initialized under the correct mockedIntl.NumberFormat. The nestedCASES.forEach+LOCALES.forEachstructure is clear and achieves the goal of asserting locale-invariant"."decimals.If you want, this could be slightly more declarative with
describe.each(LOCALES)/it.each(CASES)in the future, but that’s purely stylistic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/explorer/src/test/mockNumberLocale.ts(1 hunks)apps/explorer/src/test/utils/format/formatPercentage.spec.ts(2 hunks)apps/explorer/src/test/utils/operator/orderFormatAmount.test.ts(2 hunks)apps/explorer/src/utils/format.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- apps/explorer/src/test/mockNumberLocale.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/explorer/src/utils/format.ts
- apps/explorer/src/test/utils/operator/orderFormatAmount.test.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx:23-33
Timestamp: 2025-07-24T16:42:53.154Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/HighFeeWarningTooltipContent.tsx, the use of toFixed(2) for percentage formatting in tooltip content is intentional and differs from the banner message formatting that uses toSignificant(2, undefined, Rounding.ROUND_DOWN). This formatting difference serves different UX purposes and should not be flagged as inconsistent.
Learnt from: cowdan
Repo: cowprotocol/cowswap PR: 6009
File: apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts:18-20
Timestamp: 2025-07-24T16:43:47.639Z
Learning: In apps/cowswap-frontend/src/modules/tradeWidgetAddons/containers/HighFeeWarning/highFeeWarningHelpers.ts, the formatFeePercentage function uses ROUND_DOWN with toSignificant(2) for "at least X%" messaging. This ensures the displayed percentage is never higher than the actual fee, making the "at least" phrasing accurate. For example, if the actual fee is 25.4%, displaying "at least 25%" is correct, but "at least 26%" would be misleading.
📚 Learning: 2025-03-10T16:03:11.687Z
Learnt from: alfetopito
Repo: cowprotocol/cowswap PR: 5495
File: apps/cowswap-frontend/src/legacy/state/orders/utils.ts:444-452
Timestamp: 2025-03-10T16:03:11.687Z
Learning: When working with large numbers in JavaScript, especially token amounts with many decimals, avoid conversions through Number() before BigInt to prevent precision loss. Instead, pass string representations directly to JSBI.BigInt().
Applied to files:
apps/explorer/src/test/utils/format/formatPercentage.spec.ts
🧬 Code graph analysis (1)
apps/explorer/src/test/utils/format/formatPercentage.spec.ts (2)
apps/explorer/src/test/mockNumberLocale.ts (1)
mockNumberLocale(1-4)apps/explorer/src/utils/format.ts (1)
formatPercentage(114-134)
🔇 Additional comments (2)
apps/explorer/src/test/utils/format/formatPercentage.spec.ts (2)
3-3: Import and LOCALES setup look correct
mockNumberLocaleis imported from the expected test helper location andLOCALEScovers a good spread of decimal formats (including RTLar-SA), which should robustly catch regressions in locale handling. No changes needed.Also applies to: 24-25
27-33: Lifecycle hooks correctly isolate locale/mocks between testsUsing
jest.resetModules()inbeforeEachtogether withjest.restoreAllMocks()inafterEachensures that each test re-imports the module with a clean module registry and no leftover spies, which is important when mockingIntl.NumberFormatat import time. This is a solid pattern for this scenario; just be aware thatresetModulescan make tests a bit slower if many modules are loaded.If you see any unexpected performance hit in this suite, consider checking Jest docs for
jest.resetModules()vs alternatives likejest.isolateModules()to confirm this is still the recommended approach for your Jest version.
Summary
yarn testfails some tests if developer's OS has non en-USIntl.NumberFormatlocale (e.g. with ru-RU or de-DE)This is because of
formatSmartusage:How it generally works:
So
formatSmartpreserves user's locale settings and it works correctly. But we don't need user's locale decimal for percentages, we need "." decimal .So every
formatSmart()should be called withisLocaleAware: false, when it's called fromformatPercentageWhat I did:
formatPercentagereturnformatSmart()withisLocaleAware: falseNow
To Test
yarn testshouldn't fail with anyIntl.NumberFormatlocaleBackground
instead of own
formatPercentageimplementationmockNumberLocaleshould be called in advance, soformattedAmount/formatPercentageare imported dynamicallymockNumberLocaleis located in separated file, because it shouldn't import anotherutilsfilesSummary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.