Skip to content

Commit a353458

Browse files
committed
Re-structure API and remove unnecessary layers.
1 parent 908500d commit a353458

File tree

16 files changed

+1239
-485
lines changed

16 files changed

+1239
-485
lines changed

decimal-formatter-compose/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ kotlin {
3636

3737

3838
implementation(compose.components.resources)
39+
implementation(libs.ionspin.kotlin.bignum)
3940
implementation(compose.material3)
4041
implementation(libs.foundation)
4142
}

decimal-formatter-compose/src/androidTest/kotlin/dev/robercoding/decimal/formatter/compose/components/DecimalTextFieldTest.kt

Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import androidx.compose.ui.test.performClick
1515
import androidx.compose.ui.test.performTextClearance
1616
import androidx.compose.ui.test.performTextInput
1717
import androidx.compose.ui.test.runComposeUiTest
18-
import dev.robercoding.decimal.formatter.compose.formatter.UiDecimalFormatter
1918
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatter
2019
import dev.robercoding.decimal.formatter.core.formatter.DecimalFormatterConfiguration
2120
import kotlin.test.Test
@@ -28,15 +27,12 @@ class DecimalTextFieldTest {
2827
@OptIn(ExperimentalTestApi::class)
2928
@Test
3029
fun userSeesCorrectFullDisplayAndDeveloperGetsExpectedDataUs() = runComposeUiTest {
31-
val decimalFormatter = UiDecimalFormatter(
32-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.us()),
33-
prefix = "$"
34-
)
30+
val decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.usd())
3531
var currentValue by mutableStateOf(decimalFormatter.format(""))
3632

3733
setContent {
3834
OutlinedDecimalTextField(
39-
decimalValue = currentValue,
35+
value = currentValue,
4036
onValueChange = { currentValue = it },
4137
decimalFormatter = decimalFormatter,
4238
modifier = Modifier.testTag("decimal_field")
@@ -54,22 +50,19 @@ class DecimalTextFieldTest {
5450

5551
// ✅ Developer gets structured data
5652
assertEquals("2999", currentValue.rawDigits)
57-
assertEquals("29.99", currentValue.display)
58-
assertEquals("$29.99", currentValue.fullDisplay)
53+
assertEquals("29.99", currentValue.parseableValue)
54+
assertEquals("$29.99", currentValue.displayValue)
5955
}
6056

6157
@OptIn(ExperimentalTestApi::class)
6258
@Test
6359
fun userSeesCorrectFullDisplayAndDeveloperGetsExpectedDataEuropean() = runComposeUiTest {
64-
val decimalFormatter = UiDecimalFormatter(
65-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.european()),
66-
prefix = ""
67-
)
60+
val decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.euro())
6861
var currentValue by mutableStateOf(decimalFormatter.format(""))
6962

7063
setContent {
7164
OutlinedDecimalTextField(
72-
decimalValue = currentValue,
65+
value = currentValue,
7366
onValueChange = { currentValue = it },
7467
decimalFormatter = decimalFormatter,
7568
modifier = Modifier.testTag("decimal_field")
@@ -83,12 +76,12 @@ class DecimalTextFieldTest {
8376
}
8477

8578
// ✅ User sees prefix in display
86-
onNodeWithTag("decimal_field").assertTextEquals("29,99")
79+
onNodeWithTag("decimal_field").assertTextEquals("29,99")
8780

8881
// ✅ Developer gets structured data
8982
assertEquals("2999", currentValue.rawDigits)
90-
assertEquals("29,99", currentValue.display)
91-
assertEquals("29,99", currentValue.fullDisplay)
83+
assertEquals("29.99", currentValue.parseableValue)
84+
assertEquals("29,99", currentValue.displayValue)
9285
}
9386

9487
// ===== 2. DIFFERENT DECIMAL FORMATTER CONFIGURATIONS =====
@@ -98,21 +91,15 @@ class DecimalTextFieldTest {
9891
fun userSeesCorrectFormattingForDifferentDecimalFormatterConfigurationOnSwitch() = runComposeUiTest {
9992
var isEuropean by mutableStateOf(false)
10093

101-
val usFormatter = UiDecimalFormatter(
102-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.us()),
103-
prefix = null
104-
)
105-
val europeanFormatter = UiDecimalFormatter(
106-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.european()),
107-
prefix = null
108-
)
94+
val usFormatter = DecimalFormatter(DecimalFormatterConfiguration.us())
95+
val europeanFormatter = DecimalFormatter(DecimalFormatterConfiguration.european())
10996

11097
var currentValue by mutableStateOf(usFormatter.format("123456"))
11198

11299
setContent {
113100
Column {
114101
OutlinedDecimalTextField(
115-
decimalValue = currentValue,
102+
value = currentValue,
116103
onValueChange = { currentValue = it },
117104
decimalFormatter = if (isEuropean) europeanFormatter else usFormatter,
118105
modifier = Modifier.testTag("decimal_field")
@@ -134,7 +121,9 @@ class DecimalTextFieldTest {
134121

135122
// User sees initial US format
136123
onNodeWithTag("decimal_field").assertTextEquals("1,234.56")
137-
assertEquals("1,234.56", currentValue.display)
124+
assertEquals("1,234.56", currentValue.displayValue)
125+
assertEquals("1234.56", currentValue.parseableValue) // Format changed
126+
assertEquals("123456", currentValue.rawDigits) // Raw data unchanged
138127

139128
// User switches to European format
140129
onNodeWithTag("switch_locale").performClick()
@@ -143,84 +132,82 @@ class DecimalTextFieldTest {
143132
// Same raw data, different formatting
144133
onNodeWithTag("decimal_field").assertTextEquals("1.234,56") // European format
145134
assertEquals("123456", currentValue.rawDigits) // Raw data unchanged
146-
assertEquals("1.234,56", currentValue.display) // Format changed
135+
assertEquals("1234.56", currentValue.parseableValue) // Format changed
136+
assertEquals("1.234,56", currentValue.displayValue) // Format changed
147137
}
148138

149139
// ===== 3. PROGRESSIVE INPUT (User typing experience) =====
150140

151141
@OptIn(ExperimentalTestApi::class)
152142
@Test
153143
fun userSeesProgressiveFormattingWhileTyping() = runComposeUiTest {
154-
val decimalFormatter = UiDecimalFormatter(
155-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.us()),
156-
prefix = null
157-
)
158-
var currentValue by mutableStateOf(decimalFormatter.format(""))
144+
val usFormatter = DecimalFormatter(DecimalFormatterConfiguration.us())
145+
var currentValue by mutableStateOf(usFormatter.format(""))
159146

160147
setContent {
161148
OutlinedDecimalTextField(
162-
decimalValue = currentValue,
149+
value = currentValue,
163150
onValueChange = { currentValue = it },
164-
decimalFormatter = decimalFormatter,
151+
decimalFormatter = usFormatter,
165152
modifier = Modifier.testTag("decimal_field")
166153
)
167154
}
168155

156+
// Triple(input, expectedParseableValue, expectedDisplayValue)
169157
val testCases = listOf(
170-
"1" to "0.01",
171-
"12" to "0.12",
172-
"123" to "1.23",
173-
"1234" to "12.34",
174-
"12345" to "123.45",
175-
"123456" to "1,234.56"
158+
Triple("1", "0.01", "0.01"),
159+
Triple("12", "0.12", "0.12"),
160+
Triple("123", "1.23", "1.23"),
161+
Triple("1234", "12.34", "12.34"),
162+
Triple("12345", "123.45", "123.45"),
163+
Triple("123456", "1234.56", "1,234.56")
176164
)
177165

178-
testCases.forEach { (input, expectedDisplay) ->
166+
testCases.forEach { (input, expectedParseable, expectedDisplay) ->
179167
onNodeWithTag("decimal_field").performTextClearance()
180168
onNodeWithTag("decimal_field").performTextInput(input)
181169

182170
waitUntil(timeoutMillis = 5000) {
183171
currentValue.rawDigits == input
184172
}
185173

186-
// User sees progressive formatting
174+
// User sees progressive formatting in the UI
187175
onNodeWithTag("decimal_field").assertTextEquals(expectedDisplay)
188176

189-
// Developer gets consistent raw data
190-
assertEquals(input, currentValue.rawDigits)
191-
assertEquals(expectedDisplay, currentValue.display)
177+
// Developer gets consistent data from the FormattedDecimal object
178+
assertEquals(input, currentValue.rawDigits, "Raw digits should match input for: $input")
179+
assertEquals(expectedParseable, currentValue.parseableValue, "Parseable value should be correct for input: $input")
180+
assertEquals(expectedDisplay, currentValue.displayValue, "Display value should be correct for input: $input")
192181
}
193182
}
194183

184+
195185
// ===== 4. STATE SYNCHRONIZATION =====
196186

197187
@OptIn(ExperimentalTestApi::class)
198188
@Test
199189
fun componentSyncsStateChanges() = runComposeUiTest {
200-
val decimalFormatter = UiDecimalFormatter(
201-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.us()),
202-
prefix = null
203-
)
204-
var currentValue by mutableStateOf(decimalFormatter.format("123"))
190+
val usFormatter = DecimalFormatter(DecimalFormatterConfiguration.us())
191+
var currentValue by mutableStateOf(usFormatter.format("123"))
205192

206193
setContent {
207194
Column {
208195
OutlinedDecimalTextField(
209-
decimalValue = currentValue,
196+
value = currentValue,
210197
onValueChange = { currentValue = it },
211-
decimalFormatter = decimalFormatter,
198+
decimalFormatter = usFormatter,
212199
modifier = Modifier.testTag("decimal_field")
213200
)
214201

215202
Button(
216-
onClick = { currentValue = decimalFormatter.format("56789") },
203+
onClick = { currentValue = usFormatter.format("56789") },
217204
modifier = Modifier.testTag("update_button")
218205
) {
219206
Text("Update Value")
220207
}
221208

222209
Button(
223-
onClick = { currentValue = decimalFormatter.format("") },
210+
onClick = { currentValue = usFormatter.format("") },
224211
modifier = Modifier.testTag("clear_button")
225212
) {
226213
Text("Clear")
@@ -255,32 +242,32 @@ class DecimalTextFieldTest {
255242

256243
@OptIn(ExperimentalTestApi::class)
257244
@Test
258-
fun componentHandlesInvalidInputGracefully() = runComposeUiTest {
259-
val decimalFormatter = UiDecimalFormatter(
260-
decimalFormatter = DecimalFormatter(DecimalFormatterConfiguration.us()),
261-
prefix = null
262-
)
263-
var currentValue by mutableStateOf(decimalFormatter.format(""))
245+
fun componentHandlesInvalidInputGracefullySimple() = runComposeUiTest {
246+
val usFormatter = DecimalFormatter(DecimalFormatterConfiguration.us())
247+
var currentValue by mutableStateOf(usFormatter.format(""))
264248

265249
setContent {
266250
OutlinedDecimalTextField(
267-
decimalValue = currentValue,
251+
value = currentValue,
268252
onValueChange = { currentValue = it },
269-
decimalFormatter = decimalFormatter,
253+
decimalFormatter = usFormatter,
270254
modifier = Modifier.testTag("decimal_field")
271255
)
272256
}
273257

274258
val testCases = listOf(
275-
"abc123def" to ("123" to "1.23"), // Letters filtered out
276-
"..1234.." to ("1234" to "12.34"), // Special chars filtered
277-
"000123" to ("123" to "1.23"), // Leading zeros removed
278-
"" to ("" to "0.00") // Empty input
259+
"abc123def" to Triple("123", "1.23", "1.23"), // Letters filtered out
260+
"..1234.." to Triple("1234", "12.34", "12.34"), // Special chars filtered
261+
"000123" to Triple("123", "1.23", "1.23"), // Leading zeros removed
262+
"" to Triple("", "0.00", "0.00"), // Empty input
263+
"$#@!" to Triple("", "0.00", "0.00"), // No digits
264+
"1a2b3c4d5e6f" to Triple("123456", "1234.56", "1,234.56") // Mixed with thousand separator
279265
)
280266

281267
testCases.forEach { (input, expected) ->
282268
val expectedRawDigits = expected.first
283-
val expectedDisplay = expected.second
269+
val expectedParseable = expected.second
270+
val expectedDisplay = expected.third
284271

285272
onNodeWithTag("decimal_field").performTextClearance()
286273
onNodeWithTag("decimal_field").performTextInput(input)
@@ -289,12 +276,11 @@ class DecimalTextFieldTest {
289276
currentValue.rawDigits == expectedRawDigits
290277
}
291278

292-
// User sees clean formatted output
293279
onNodeWithTag("decimal_field").assertTextEquals(expectedDisplay)
294280

295-
// Developer gets cleaned raw data
296281
assertEquals(expectedRawDigits, currentValue.rawDigits)
297-
assertEquals(expectedDisplay, currentValue.display)
282+
assertEquals(expectedParseable, currentValue.parseableValue)
283+
assertEquals(expectedDisplay, currentValue.displayValue)
298284
}
299285
}
300286
}

0 commit comments

Comments
 (0)