Format amount/price fields with locale grouping and decimal
markers (like apps/firma), and normalize locale input before validation
and send math so non-en-US users can enter amounts naturally.
Details
- Reviewers
Fabien - Group Reviewers
Restricted Project - Commits
- rABC423b5af74eb1: [Cashtab] Locale thousands separators and decimals in number inputs
npm test
- Manually: in en-US type 1234.5 → see 1,234.5; in de-DE type 1234,5 → see 1.234,5; max/bip21 amounts still send correctly
Diff Detail
- Repository
- rABC Bitcoin ABC
- Lint
Lint Not Applicable - Unit
Tests Not Applicable
Event Timeline
Failed tests logs:
====== CashTab Unit Tests: <OrderBook /> We can see a rendered offer ======
TestingLibraryElementError: Unable to find an element with the text: 1.7 CACHET. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<section
aria-atomic="false"
aria-label="Notifications"
aria-live="polite"
aria-relevant="additions text"
class="Toastify"
/>
<div>
Test
</div>
<div
class="sc-elJkPf lcZeYn"
>
<div
class="sc-jtRfpW jPFUbw"
>
<button
aria-label="View larger icon for Cachet"
class="sc-dfVpRl jYFaTN"
size="64"
title="aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
/>
<div
class="sc-dqBHgY eekOiO"
>
<a
class="sc-gxMtzJ hiCoVB"
font-size="var(--text-2xl)"
href="#/token/aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
>
Cachet
</a>
<span>
CACHET
</span>
<div
class="sc-lhVmIH cfuLzI"
>
aed
...
cb1
<button
aria-label="Copy Token ID"
class="sc-jqCOkK jKBkhX"
>
<svg
title="copy-paste"
/>
</button>
</div>
</div>
<div
class="sc-kTUwUJ elfyii"
>
<div>
1
Offer
</div>
<div
class="sc-fAjcbJ fOpxfV"
>
<div
class="sc-caSCKo gYoTCj"
>
<input
class="sc-TOsTZ SvZry"
id="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
name="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
title="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
type="checkbox"
/>
<label
class="sc-gisBJw bNwAUh"
for="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
>
<span
class="sc-kjoXOD kybLSq"
data-off="XEC"
data-on="usd"
/>
<span
class="sc-cHGsZl fYqMqO"
/>
</label>
</div>
</div>
</div>
</div>
<div
class="sc-gzOgki kgmzuS"
>
<div
class="sc-iyvyFf hbSTMH"
>
<button
class="sc-hwwEjo OCCfh"
data-tooltip-content="100.00 CACHET (100.00 total)"
data-tooltip-id="cashtab-tooltip"
>
<div
class="sc-kfGgVZ fMVgTU"
/>
<div
class="sc-esjQYD evNLSa"
/>
<div
class="sc-kPVwWT fePPDZ"
>
$0.3300 USD
</div>
</button>
</div>
<div
class="sc-epnACN dDVIbm"
>
<span
class="sc-kIPQKe dENjKC"
>
Quantity
</span>
<div
class="sc-iQNlJl jfOdIZ"
>
<div
class="sc-cSHVUG fLNrXC"
>
<input
aria-labelledby="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
class="sc-iRbamj deoJtU"
max="100.00"
min=".10"
name="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
step="0.01"
type="range"
value="1.70"
/>
<div
class="sc-csuQGl bEcGlB"
>
<input
class="sc-jlyJG kRLVJP"
inputmode="decimal"
name="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1-typed"
placeholder="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
type="text"
value="1.70"
/>
</div>
<div
class="sc-eHgmQL doLYEs"
>
Buy price (17k XEC) exceeds available balance (4.2k XEC).
</div>
</div>
</div>
</div>
<div
class="sc-eXEjpC kkNpfk"
>
<div
class="sc-ibxdXY hGKGGM"
>
<span
class="sc-RefOD kjaTsL"
>
Buying
</span>
<span
class="sc-iQKALj kpBiQm"
>
1.70
CACHET
</span>
</div>
<div
class="sc-ibxdXY hGKGGM"
>
<span
class="sc-RefOD kjaTsL"
>
For
</span>
<span
class="sc-bwCtUz fFRXGH"
>
$0.5611 USD
</span>
</div>
<button
class="sc-hmzhuo sc-jbKcbu idWQse"
>
Cancel your offer
</button>
</div>
</div>
</div>
</div>
</body>
at Object.getElementError (/work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/config.js:37:19)
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:76:38
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:52:17
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:95:19
at Object.getByText (/work/cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx:295:23)
====== CashTab Unit Tests: <OrderBook /> We can see a rendered offer in an OrderBook with noIcon ======
TestingLibraryElementError: Unable to find an element with the text: 1.7 CACHET. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
Ignored nodes: comments, script, style
<body>
<div>
<section
aria-atomic="false"
aria-label="Notifications"
aria-live="polite"
aria-relevant="additions text"
class="Toastify"
/>
<div>
Test
</div>
<div
class="sc-elJkPf jgTkeW"
>
<div
class="sc-jtRfpW jTrLnW"
>
<div
class="sc-kTUwUJ kCgeGF"
>
<div>
1
Offer
</div>
<div
class="sc-fAjcbJ fOpxfV"
>
<div
class="sc-caSCKo gYoTCj"
>
<input
class="sc-TOsTZ SvZry"
id="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
name="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
title="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
type="checkbox"
/>
<label
class="sc-gisBJw bNwAUh"
for="Toggle price for aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
>
<span
class="sc-kjoXOD kybLSq"
data-off="XEC"
data-on="usd"
/>
<span
class="sc-cHGsZl fYqMqO"
/>
</label>
</div>
</div>
</div>
</div>
<div
class="sc-gzOgki kgmzuS"
>
<div
class="sc-iyvyFf cBHUNz"
>
<button
class="sc-hwwEjo OCCfh"
data-tooltip-content="100.00 CACHET (100.00 total)"
data-tooltip-id="cashtab-tooltip"
>
<div
class="sc-kfGgVZ fMVgTU"
/>
<div
class="sc-esjQYD evNLSa"
/>
<div
class="sc-kPVwWT fePPDZ"
>
$0.3300 USD
</div>
</button>
</div>
<div
class="sc-epnACN dDVIbm"
>
<span
class="sc-kIPQKe dENjKC"
>
Quantity
</span>
<div
class="sc-iQNlJl jfOdIZ"
>
<div
class="sc-cSHVUG fLNrXC"
>
<input
aria-labelledby="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
class="sc-iRbamj deoJtU"
max="100.00"
min=".10"
name="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
step="0.01"
type="range"
value="1.70"
/>
<div
class="sc-csuQGl bEcGlB"
>
<input
class="sc-jlyJG kRLVJP"
inputmode="decimal"
name="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1-typed"
placeholder="Select buy qty aed861a31b96934b88c0252ede135cb9700d7649f69191235087a3030e553cb1"
type="text"
value="1.70"
/>
</div>
<div
class="sc-eHgmQL doLYEs"
>
Buy price (17k XEC) exceeds available balance (4.2k XEC).
</div>
</div>
</div>
</div>
<div
class="sc-eXEjpC kkNpfk"
>
<div
class="sc-ibxdXY hGKGGM"
>
<span
class="sc-RefOD kjaTsL"
>
Buying
</span>
<span
class="sc-iQKALj kpBiQm"
>
1.70
CACHET
</span>
</div>
<div
class="sc-ibxdXY hGKGGM"
>
<span
class="sc-RefOD kjaTsL"
>
For
</span>
<span
class="sc-bwCtUz fFRXGH"
>
$0.5611 USD
</span>
</div>
<button
class="sc-hmzhuo sc-jbKcbu idWQse"
>
Cancel your offer
</button>
</div>
</div>
</div>
</div>
</body>
at Object.getElementError (/work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/config.js:37:19)
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:76:38
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:52:17
at /work/node_modules/.pnpm/@testing-library+dom@10.4.1/node_modules/@testing-library/dom/dist/query-helpers.js:95:19
at Object.getByText (/work/cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx:424:23)
====== CashTab Unit Tests: <OrderBook /> We can buy an offer ======
Error: expect(element).toHaveValue(0.1)
Expected the element to have value:
0.1 (number)
Received:
.10 (string)
at Object.toHaveValue (/work/cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx:702:38)
====== CashTab Unit Tests: <OrderBook /> We can type input and see a previewed offer of actual input with a delta from our typed input ======
Error: expect(element).toHaveValue(960000)
Expected the element to have value:
960000 (number)
Received:
960000.00 (string)
at Object.toHaveValue (/work/cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx:946:36)
====== CashTab Unit Tests: <App /> Clicking "reply" on a Cashtab Msg correctly populates the SendXec to address and amount fields ======
Error: expect(element).toHaveValue(5.46)
Expected the element to have value:
5.46 (number)
Received:
5.46 (string)
at Object.toHaveValue (/work/cashtab/src/components/App/__tests__/App.test.js:449:55)
====== CashTab Unit Tests: <CreateTokenForm /> Validation works as expected for ALP and SLP max supply ======
Error: expect(element).toHaveValue(18446744073709551615)
Expected the element to have value:
18446744073709551615
Received:
18,446,744,073,709,551,615
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/CreateTokenForm.test.tsx:711:38)
====== CashTab Unit Tests: <Token /> available actions rendered We can list an SLP1 fungible token ======
Error: expect(element).toHaveValue(5460)
Expected the element to have value:
5460 (number)
Received:
5460 (string)
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:333:29)
====== CashTab Unit Tests: <Token /> available actions rendered We can list an SLP1 NFT ======
Error: expect(element).toHaveValue(null)
Expected the element to have value:
null
Received:
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:1036:28)
====== CashTab Unit Tests: <Token /> available actions rendered We can list an ALP fungible token ======
Error: expect(element).toHaveValue(5460)
Expected the element to have value:
5460 (number)
Received:
5460 (string)
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:1927:29)
====== CashTab Unit Tests: <Token /> available actions rendered We can redeem XECX for XEC 1:1 using a workflow unique to XECX ======
Error: expect(element).toHaveValue(10000)
Expected the element to have value:
10000
Received:
10,000
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2145:58)
====== CashTab Unit Tests: <Token /> available actions rendered We see expected alert in XECX redemption workflow for hot wallet balance ======
Error: expect(element).toHaveValue(10000)
Expected the element to have value:
10000
Received:
10,000
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2253:58)
====== CashTab Unit Tests: <Token /> available actions rendered We DO NOT see expected alert in XECX redemption workflow for hot wallet balance if there is some error determining the hot wallet balance ======
Error: expect(element).toHaveValue(10000)
Expected the element to have value:
10000
Received:
10,000
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2346:58)
====== CashTab Unit Tests: <Token /> available actions rendered We can redeem 1 Firma for $1 of XEC using a workflow unique to Firma ======
Error: expect(element).toHaveValue(10)
Expected the element to have value:
10 (number)
Received:
10 (string)
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2480:58)
====== CashTab Unit Tests: <Token /> available actions rendered We show expected error if we are unable to get FIRMA bid price from API ======
Error: expect(element).toHaveValue(10)
Expected the element to have value:
10 (number)
Received:
10 (string)
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2741:58)
====== CashTab Unit Tests: <Token /> available actions rendered We can list a MINT VAULT fungible token ======
Error: expect(element).toHaveValue(6)
Expected the element to have value:
6 (number)
Received:
6 (string)
at Object.toHaveValue (/work/cashtab/src/components/Etokens/__tests__/TokenActions.test.js:2993:29)Each failure log is accessible here:
CashTab Unit Tests: <OrderBook /> We can see a rendered offer
CashTab Unit Tests: <OrderBook /> We can see a rendered offer in an OrderBook with noIcon
CashTab Unit Tests: <OrderBook /> We can buy an offer
CashTab Unit Tests: <OrderBook /> We can type input and see a previewed offer of actual input with a delta from our typed input
CashTab Unit Tests: <App /> Clicking "reply" on a Cashtab Msg correctly populates the SendXec to address and amount fields
CashTab Unit Tests: <CreateTokenForm /> Validation works as expected for ALP and SLP max supply
CashTab Unit Tests: <Token /> available actions rendered We can list an SLP1 fungible token
CashTab Unit Tests: <Token /> available actions rendered We can list an SLP1 NFT
CashTab Unit Tests: <Token /> available actions rendered We can list an ALP fungible token
CashTab Unit Tests: <Token /> available actions rendered We can redeem XECX for XEC 1:1 using a workflow unique to XECX
CashTab Unit Tests: <Token /> available actions rendered We see expected alert in XECX redemption workflow for hot wallet balance
CashTab Unit Tests: <Token /> available actions rendered We DO NOT see expected alert in XECX redemption workflow for hot wallet balance if there is some error determining the hot wallet balance
CashTab Unit Tests: <Token /> available actions rendered We can redeem 1 Firma for $1 of XEC using a workflow unique to Firma
CashTab Unit Tests: <Token /> available actions rendered We show expected error if we are unable to get FIRMA bid price from API
CashTab Unit Tests: <Token /> available actions rendered We can list a MINT VAULT fungible token
Fix OrderBook qty-delta math and remaining locale input test expectations.
Normalize take qty before BigNumber delta/target display; update OrderBook,
TokenActions, App, and CreateTokenForm assertions for string/locale-formatted
input values. Full cashtab suite green locally (CI=true pnpm test).
| cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx | ||
|---|---|---|
| 779 ↗ | (On Diff #60287) | this is equivalent ? |
| cashtab/src/components/Common/Inputs.tsx | ||
| 21 ↗ | (On Diff #60287) | I trust you tested that live because it's not obvious this doesn't create confusion, my understanding is that it's changing the content as you type |
| cashtab/src/validation/index.ts | ||
| 102 ↗ | (On Diff #60287) | This is now guaranteed |
| cashtab/src/components/Agora/OrderBook/__tests__/index.test.tsx | ||
|---|---|---|
| 779 ↗ | (On Diff #60287) | I don't think so actually. If there are more than one, we get an array instead of the entry, and react test library won't pass it. this is still a bit of a code smell; I prefer to expect the exact length instead of "there should be at least one". This helps to better track later changes in behavior. But in this case, "toBeGreaterThanOrEqual(1)" is the exact test as before. Probably there should be no change. I'll take a look locally before landing. |
| cashtab/src/components/Common/Inputs.tsx | ||
| 21 ↗ | (On Diff #60287) | Yes, from my testing it just works. A similar implementation is on the firma app. But there could still be some experience edge cases for special locales. Not guaranteed to be perfect here but on balance I think it's better than nothing, especially since typical txs involve amounts in the millions and it's quite easy to be 10x off without thousands separators. |