@@ -98,13 +98,20 @@ describe("formatPriceInput", () => {
9898 } ) ;
9999
100100 describe ( "currency-specific decimal places" , ( ) => {
101- it ( "handles zero decimal places for currencies like JPY" , ( ) => {
101+ it ( "truncates to zero decimal places for currencies like JPY" , ( ) => {
102102 expect ( formatPriceInput ( "1000.99" , 0 ) ) . toBe ( "1000" ) ;
103103 expect ( formatPriceInput ( "1000,99" , 0 ) ) . toBe ( "1000" ) ;
104+ expect ( formatPriceInput ( "1,234.56" , 0 ) ) . toBe ( "1234" ) ;
105+ expect ( formatPriceInput ( "1'234.56" , 0 ) ) . toBe ( "1234" ) ;
104106 } ) ;
105107
106- it ( "handles three decimal places for currencies like KWD" , ( ) => {
108+ it ( "truncates to three decimal places for currencies like KWD" , ( ) => {
107109 expect ( formatPriceInput ( "10.12345" , 3 ) ) . toBe ( "10.123" ) ;
110+ expect ( formatPriceInput ( "1,234.5678" , 3 ) ) . toBe ( "1234.567" ) ;
111+ expect ( formatPriceInput ( "1.234,5678" , 3 ) ) . toBe ( "1234.567" ) ;
112+ expect ( formatPriceInput ( "1'234.5678" , 3 ) ) . toBe ( "1234.567" ) ;
113+ expect ( formatPriceInput ( "1'234,5678" , 3 ) ) . toBe ( "1234.567" ) ;
114+ expect ( formatPriceInput ( "1,000" , 3 ) ) . toBe ( "1000" ) ;
108115 } ) ;
109116 } ) ;
110117
@@ -126,6 +133,18 @@ describe("formatPriceInput", () => {
126133 expect ( formatPriceInput ( "1.2.3.4" , 2 ) ) . toBe ( "1.23" ) ;
127134 } ) ;
128135
136+ it ( "handles double-tap comma as typing accident" , ( ) => {
137+ expect ( formatPriceInput ( "123,,45" , 2 ) ) . toBe ( "123.45" ) ;
138+ expect ( formatPriceInput ( "10,,50" , 2 ) ) . toBe ( "10.50" ) ;
139+ } ) ;
140+
141+ it ( "collapses trailing adjacent separators (double-tap at end)" , ( ) => {
142+ expect ( formatPriceInput ( "123.," , 2 ) ) . toBe ( "123." ) ;
143+ expect ( formatPriceInput ( "123,." , 2 ) ) . toBe ( "123." ) ;
144+ expect ( formatPriceInput ( "123,," , 2 ) ) . toBe ( "123." ) ;
145+ expect ( formatPriceInput ( "123.." , 2 ) ) . toBe ( "123." ) ;
146+ } ) ;
147+
129148 it ( "returns empty for just separators (no digits)" , ( ) => {
130149 expect ( formatPriceInput ( "." , 2 ) ) . toBe ( "" ) ;
131150 expect ( formatPriceInput ( "," , 2 ) ) . toBe ( "" ) ;
@@ -154,25 +173,113 @@ describe("formatPriceInput", () => {
154173 } ) ;
155174 } ) ;
156175
176+ describe ( "comma-only thousands detection" , ( ) => {
177+ it ( "recognizes comma as thousand separator when all groups are exactly 3 digits" , ( ) => {
178+ expect ( formatPriceInput ( "1,000" , 2 ) ) . toBe ( "1000" ) ;
179+ expect ( formatPriceInput ( "1,000,000" , 2 ) ) . toBe ( "1000000" ) ;
180+ } ) ;
181+
182+ it ( "still treats single comma as decimal when group after is not 3 digits" , ( ) => {
183+ expect ( formatPriceInput ( "10,50" , 2 ) ) . toBe ( "10.50" ) ;
184+ expect ( formatPriceInput ( "10,5" , 2 ) ) . toBe ( "10.5" ) ;
185+ } ) ;
186+ } ) ;
187+
188+ describe ( "apostrophe as thousand separator" , ( ) => {
189+ it ( "strips apostrophes and parses with dot decimal" , ( ) => {
190+ expect ( formatPriceInput ( "1'222.33" , 2 ) ) . toBe ( "1222.33" ) ;
191+ expect ( formatPriceInput ( "1'234'567.89" , 2 ) ) . toBe ( "1234567.89" ) ;
192+ } ) ;
193+
194+ it ( "handles apostrophe-only without decimal point" , ( ) => {
195+ expect ( formatPriceInput ( "1'000" , 2 ) ) . toBe ( "1000" ) ;
196+ } ) ;
197+
198+ it ( "handles apostrophe with dot decimal and trailing zeros" , ( ) => {
199+ expect ( formatPriceInput ( "1'000.00" , 2 ) ) . toBe ( "1000.00" ) ;
200+ } ) ;
201+
202+ it ( "handles apostrophe thousands with comma decimal (Swiss-EU hybrid)" , ( ) => {
203+ expect ( formatPriceInput ( "1'222,33" , 2 ) ) . toBe ( "1222.33" ) ;
204+ } ) ;
205+
206+ it ( "strips leading zeros from formatted numbers" , ( ) => {
207+ expect ( formatPriceInput ( "01'234.56" , 2 ) ) . toBe ( "1234.56" ) ;
208+ expect ( formatPriceInput ( "001,234.56" , 2 ) ) . toBe ( "1234.56" ) ;
209+ } ) ;
210+ } ) ;
211+
157212 describe ( "edge cases" , ( ) => {
213+ it ( "handles zero" , ( ) => {
214+ expect ( formatPriceInput ( "0" , 2 ) ) . toBe ( "0" ) ;
215+ expect ( formatPriceInput ( "0.00" , 2 ) ) . toBe ( "0.00" ) ;
216+ } ) ;
217+
218+ it ( "handles large numbers without separators" , ( ) => {
219+ expect ( formatPriceInput ( "9999999.99" , 2 ) ) . toBe ( "9999999.99" ) ;
220+ expect ( formatPriceInput ( "1000000.99" , 2 ) ) . toBe ( "1000000.99" ) ;
221+ } ) ;
222+
158223 it ( "strips negative sign (prices are positive)" , ( ) => {
159224 expect ( formatPriceInput ( "-10.50" , 2 ) ) . toBe ( "10.50" ) ;
160225 } ) ;
161226
162- it ( "keeps leading zeros" , ( ) => {
163- expect ( formatPriceInput ( "007.50" , 2 ) ) . toBe ( "007.50" ) ;
227+ it ( "strips leading zeros from integer part" , ( ) => {
228+ expect ( formatPriceInput ( "007.50" , 2 ) ) . toBe ( "7.50" ) ;
229+ expect ( formatPriceInput ( "0001.23" , 2 ) ) . toBe ( "1.23" ) ;
230+ expect ( formatPriceInput ( "00100" , 2 ) ) . toBe ( "100" ) ;
164231 } ) ;
165232
166- it ( "handles malformed mixed format (multiple of both separators)" , ( ) => {
167- // "1.222,333.88" has both multiple dots AND a comma - not a clean format
168- // Falls back to "first separator wins" (typing mode)
169- expect ( formatPriceInput ( "1.222,333.88" , 2 ) ) . toBe ( "1.22" ) ;
170- expect ( formatPriceInput ( "1,222.333,88" , 2 ) ) . toBe ( "1.22" ) ;
233+ it ( "rejects malformed mixed format (multiple of both separators)" , ( ) => {
234+ expect ( formatPriceInput ( "1.222,333.88" , 2 ) ) . toBe ( "" ) ;
235+ expect ( formatPriceInput ( "1,222.333,88" , 2 ) ) . toBe ( "" ) ;
171236 } ) ;
172237
173238 it ( "limits integer part to 15 digits (float64 precision)" , ( ) => {
174239 expect ( formatPriceInput ( "123456789012345678" , 2 ) ) . toBe ( "123456789012345" ) ;
175240 expect ( formatPriceInput ( "12345678901234567.89" , 2 ) ) . toBe ( "123456789012345.89" ) ;
176241 } ) ;
177242 } ) ;
243+
244+ describe ( "invalid formatted inputs" , ( ) => {
245+ it ( "rejects doubled separators in formatted numbers" , ( ) => {
246+ expect ( formatPriceInput ( "1,,000.01" , 2 ) ) . toBe ( "" ) ;
247+ expect ( formatPriceInput ( "1''222.33" , 2 ) ) . toBe ( "" ) ;
248+ } ) ;
249+
250+ it ( "rejects leading thousand separator" , ( ) => {
251+ expect ( formatPriceInput ( ",1000.01" , 2 ) ) . toBe ( "" ) ;
252+ expect ( formatPriceInput ( "'999.99" , 2 ) ) . toBe ( "" ) ;
253+ } ) ;
254+
255+ it ( "rejects inconsistent comma grouping" , ( ) => {
256+ expect ( formatPriceInput ( "1,00,001.01" , 2 ) ) . toBe ( "" ) ;
257+ expect ( formatPriceInput ( "1,2345.67" , 2 ) ) . toBe ( "" ) ;
258+ } ) ;
259+
260+ it ( "rejects inconsistent apostrophe grouping" , ( ) => {
261+ expect ( formatPriceInput ( "1'22'2.33" , 2 ) ) . toBe ( "" ) ;
262+ } ) ;
263+
264+ it ( "rejects adjacent different separators" , ( ) => {
265+ expect ( formatPriceInput ( "1,000,.01" , 2 ) ) . toBe ( "" ) ;
266+ } ) ;
267+
268+ it ( "rejects multiple decimal points in formatted number" , ( ) => {
269+ expect ( formatPriceInput ( "1,000,001.0.1" , 2 ) ) . toBe ( "" ) ;
270+ } ) ;
271+
272+ it ( "rejects ambiguous all-comma with non-3-digit final group" , ( ) => {
273+ expect ( formatPriceInput ( "1,234,567,89" , 2 ) ) . toBe ( "" ) ;
274+ expect ( formatPriceInput ( "1,222,33" , 2 ) ) . toBe ( "" ) ;
275+ } ) ;
276+
277+ it ( "rejects wrong final apostrophe group" , ( ) => {
278+ expect ( formatPriceInput ( "1'234'567'89" , 2 ) ) . toBe ( "" ) ;
279+ } ) ;
280+
281+ it ( "rejects space as thousand separator" , ( ) => {
282+ expect ( formatPriceInput ( "1 222.33" , 2 ) ) . toBe ( "" ) ;
283+ } ) ;
284+ } ) ;
178285} ) ;
0 commit comments