@stripe/stripe-react-native 0.57.2 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -0
- package/android/gradle.properties +1 -1
- package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementView.kt +0 -3
- package/android/src/main/java/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt +5 -3
- package/android/src/main/java/com/reactnativestripesdk/NavigationBarView.kt +12 -1
- package/android/src/main/java/com/reactnativestripesdk/PaymentElementConfig.kt +18 -0
- package/android/src/main/java/com/reactnativestripesdk/PaymentSheetManager.kt +136 -35
- package/android/src/main/java/com/reactnativestripesdk/StripeAbstractComposeView.kt +17 -5
- package/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt +9 -2
- package/android/src/main/java/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt +38 -13
- package/android/src/main/java/com/reactnativestripesdk/utils/Mappers.kt +0 -2
- package/android/src/test/java/com/reactnativestripesdk/DrawableConversionPropertyTest.kt +224 -0
- package/android/src/test/java/com/reactnativestripesdk/DrawableConversionTest.kt +146 -0
- package/android/src/test/java/com/reactnativestripesdk/DrawableLoadingTest.kt +150 -0
- package/android/src/test/java/com/reactnativestripesdk/PaymentElementConfigTest.kt +138 -1
- package/android/src/test/java/com/reactnativestripesdk/PaymentOptionImageConsistencyTest.kt +186 -0
- package/ios/ConnectAccountOnboarding/ConnectAccountOnboardingView.swift +13 -19
- package/ios/StripeSdkImpl+Embedded.swift +4 -1
- package/ios/StripeSdkImpl+PaymentSheet.swift +28 -1
- package/ios/StripeSdkImpl.swift +26 -2
- package/jest/mock.js +6 -0
- package/jest/setup.js +30 -0
- package/lib/commonjs/components/AddToWalletButton.js +1 -1
- package/lib/commonjs/components/AddToWalletButton.js.map +1 -1
- package/lib/commonjs/components/AddressSheet.js +1 -1
- package/lib/commonjs/components/AddressSheet.js.map +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js +1 -1
- package/lib/commonjs/components/AuBECSDebitForm.js.map +1 -1
- package/lib/commonjs/components/CardField.js +1 -1
- package/lib/commonjs/components/CardField.js.map +1 -1
- package/lib/commonjs/components/CardForm.js +1 -1
- package/lib/commonjs/components/CardForm.js.map +1 -1
- package/lib/commonjs/components/PlatformPayButton.js +1 -1
- package/lib/commonjs/components/PlatformPayButton.js.map +1 -1
- package/lib/commonjs/components/StripeContainer.js +1 -1
- package/lib/commonjs/components/StripeContainer.js.map +1 -1
- package/lib/commonjs/connect/Components.js +1 -1
- package/lib/commonjs/connect/Components.js.map +1 -1
- package/lib/commonjs/connect/ConnectComponentsProvider.js +1 -1
- package/lib/commonjs/connect/ConnectComponentsProvider.js.map +1 -1
- package/lib/commonjs/connect/EmbeddedComponent.js +9 -4
- package/lib/commonjs/connect/EmbeddedComponent.js.map +1 -1
- package/lib/commonjs/connect/ModalCloseButton.js +1 -1
- package/lib/commonjs/connect/ModalCloseButton.js.map +1 -1
- package/lib/commonjs/connect/NavigationBar.js +1 -1
- package/lib/commonjs/connect/NavigationBar.js.map +1 -1
- package/lib/commonjs/helpers.js +1 -1
- package/lib/commonjs/hooks/useOnramp.js +1 -1
- package/lib/commonjs/hooks/useOnramp.js.map +1 -1
- package/lib/commonjs/specs/NativeAddToWalletButton.js +1 -1
- package/lib/commonjs/specs/NativeAddressSheet.js +1 -1
- package/lib/commonjs/specs/NativeApplePayButton.js +1 -1
- package/lib/commonjs/specs/NativeAuBECSDebitForm.js +1 -1
- package/lib/commonjs/specs/NativeCardField.js +1 -1
- package/lib/commonjs/specs/NativeCardField.js.map +1 -1
- package/lib/commonjs/specs/NativeCardForm.js +1 -1
- package/lib/commonjs/specs/NativeCardForm.js.map +1 -1
- package/lib/commonjs/specs/NativeConnectAccountOnboardingView.js +1 -1
- package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js +1 -1
- package/lib/commonjs/specs/NativeEmbeddedPaymentElement.js.map +1 -1
- package/lib/commonjs/specs/NativeGooglePayButton.js +1 -1
- package/lib/commonjs/specs/NativeNavigationBar.js +1 -1
- package/lib/commonjs/specs/NativeStripeContainer.js +1 -1
- package/lib/commonjs/types/EmbeddedPaymentElement.js +1 -1
- package/lib/commonjs/types/EmbeddedPaymentElement.js.map +1 -1
- package/lib/commonjs/types/FinancialConnections.js.map +1 -1
- package/lib/commonjs/types/PaymentSheet.js +1 -1
- package/lib/commonjs/types/PaymentSheet.js.map +1 -1
- package/lib/module/components/AddToWalletButton.js +1 -1
- package/lib/module/components/AddToWalletButton.js.map +1 -1
- package/lib/module/components/AddressSheet.js +1 -1
- package/lib/module/components/AddressSheet.js.map +1 -1
- package/lib/module/components/AuBECSDebitForm.js +1 -1
- package/lib/module/components/AuBECSDebitForm.js.map +1 -1
- package/lib/module/components/CardField.js +1 -1
- package/lib/module/components/CardField.js.map +1 -1
- package/lib/module/components/CardForm.js +1 -1
- package/lib/module/components/CardForm.js.map +1 -1
- package/lib/module/components/PlatformPayButton.js +1 -1
- package/lib/module/components/PlatformPayButton.js.map +1 -1
- package/lib/module/components/StripeContainer.js +1 -1
- package/lib/module/components/StripeContainer.js.map +1 -1
- package/lib/module/connect/Components.js +1 -1
- package/lib/module/connect/Components.js.map +1 -1
- package/lib/module/connect/ConnectComponentsProvider.js +1 -1
- package/lib/module/connect/ConnectComponentsProvider.js.map +1 -1
- package/lib/module/connect/EmbeddedComponent.js +9 -4
- package/lib/module/connect/EmbeddedComponent.js.map +1 -1
- package/lib/module/connect/ModalCloseButton.js +1 -1
- package/lib/module/connect/ModalCloseButton.js.map +1 -1
- package/lib/module/connect/NavigationBar.js +1 -1
- package/lib/module/connect/NavigationBar.js.map +1 -1
- package/lib/module/helpers.js +1 -1
- package/lib/module/hooks/useOnramp.js +1 -1
- package/lib/module/hooks/useOnramp.js.map +1 -1
- package/lib/module/specs/NativeAddToWalletButton.js +1 -1
- package/lib/module/specs/NativeAddressSheet.js +1 -1
- package/lib/module/specs/NativeApplePayButton.js +1 -1
- package/lib/module/specs/NativeAuBECSDebitForm.js +1 -1
- package/lib/module/specs/NativeCardField.js +1 -1
- package/lib/module/specs/NativeCardField.js.map +1 -1
- package/lib/module/specs/NativeCardForm.js +1 -1
- package/lib/module/specs/NativeCardForm.js.map +1 -1
- package/lib/module/specs/NativeConnectAccountOnboardingView.js +1 -1
- package/lib/module/specs/NativeEmbeddedPaymentElement.js +1 -1
- package/lib/module/specs/NativeEmbeddedPaymentElement.js.map +1 -1
- package/lib/module/specs/NativeGooglePayButton.js +1 -1
- package/lib/module/specs/NativeNavigationBar.js +1 -1
- package/lib/module/specs/NativeStripeContainer.js +1 -1
- package/lib/module/types/EmbeddedPaymentElement.js +1 -1
- package/lib/module/types/EmbeddedPaymentElement.js.map +1 -1
- package/lib/module/types/FinancialConnections.js.map +1 -1
- package/lib/module/types/PaymentSheet.js +1 -1
- package/lib/module/types/PaymentSheet.js.map +1 -1
- package/lib/typescript/src/connect/Components.d.ts.map +1 -1
- package/lib/typescript/src/connect/EmbeddedComponent.d.ts.map +1 -1
- package/lib/typescript/src/connect/connectTypes.d.ts +5 -1
- package/lib/typescript/src/connect/connectTypes.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useOnramp.d.ts +2 -1
- package/lib/typescript/src/hooks/useOnramp.d.ts.map +1 -1
- package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts +6 -1
- package/lib/typescript/src/types/EmbeddedPaymentElement.d.ts.map +1 -1
- package/lib/typescript/src/types/FinancialConnections.d.ts +2 -0
- package/lib/typescript/src/types/FinancialConnections.d.ts.map +1 -1
- package/lib/typescript/src/types/PaymentSheet.d.ts +30 -0
- package/lib/typescript/src/types/PaymentSheet.d.ts.map +1 -1
- package/package.json +15 -5
- package/src/connect/Components.tsx +18 -11
- package/src/connect/EmbeddedComponent.tsx +223 -12
- package/src/connect/connectTypes.ts +5 -1
- package/src/hooks/useOnramp.tsx +5 -1
- package/src/types/EmbeddedPaymentElement.tsx +6 -1
- package/src/types/FinancialConnections.ts +2 -0
- package/src/types/PaymentSheet.ts +32 -0
- package/stripe-react-native.podspec +1 -1
- package/android/.idea/AndroidProjectSystem.xml +0 -6
- package/android/.idea/caches/deviceStreaming.xml +0 -1029
- package/android/.idea/compiler.xml +0 -6
- package/android/.idea/gradle.xml +0 -19
- package/android/.idea/migrations.xml +0 -10
- package/android/.idea/misc.xml +0 -10
- package/android/.idea/runConfigurations.xml +0 -17
- package/android/.idea/vcs.xml +0 -6
- package/android/local.properties +0 -8
- package/ios/StripeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/ios/StripeSdk.xcodeproj/project.xcworkspace/xcuserdata/tianzhao.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/StripeSdk.xcodeproj/xcuserdata/tianzhao.xcuserdatad/xcschemes/xcschememanagement.plist +0 -19
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
package com.reactnativestripesdk
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import android.graphics.drawable.ColorDrawable
|
|
5
|
+
import kotlinx.coroutines.test.runTest
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Assert.assertNotEquals
|
|
8
|
+
import org.junit.Assert.assertNotNull
|
|
9
|
+
import org.junit.Assert.assertTrue
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
import org.robolectric.RobolectricTestRunner
|
|
13
|
+
import kotlin.random.Random
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Property-based tests for drawable conversion.
|
|
17
|
+
* These tests verify mathematical properties that should always hold true.
|
|
18
|
+
*/
|
|
19
|
+
@RunWith(RobolectricTestRunner::class)
|
|
20
|
+
class DrawableConversionPropertyTest {
|
|
21
|
+
@Test
|
|
22
|
+
fun `PROPERTY - conversion is idempotent`() =
|
|
23
|
+
runTest {
|
|
24
|
+
// Property: Converting the same drawable multiple times yields same result
|
|
25
|
+
repeat(20) {
|
|
26
|
+
val color =
|
|
27
|
+
Color.rgb(
|
|
28
|
+
Random.nextInt(256),
|
|
29
|
+
Random.nextInt(256),
|
|
30
|
+
Random.nextInt(256),
|
|
31
|
+
)
|
|
32
|
+
val width = Random.nextInt(50, 200)
|
|
33
|
+
val height = Random.nextInt(50, 200)
|
|
34
|
+
|
|
35
|
+
val drawable =
|
|
36
|
+
ColorDrawable(color).apply {
|
|
37
|
+
setBounds(0, 0, width, height)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
val result1 = convertDrawableToBase64(drawable)
|
|
41
|
+
val result2 = convertDrawableToBase64(drawable)
|
|
42
|
+
|
|
43
|
+
assertEquals(
|
|
44
|
+
"Same drawable should always produce same base64 (iteration $it, ${width}x$height)",
|
|
45
|
+
result1,
|
|
46
|
+
result2,
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `PROPERTY - different drawables produce different base64`() =
|
|
53
|
+
runTest {
|
|
54
|
+
val drawable1 =
|
|
55
|
+
ColorDrawable(Color.RED).apply {
|
|
56
|
+
setBounds(0, 0, 100, 100)
|
|
57
|
+
}
|
|
58
|
+
val drawable2 =
|
|
59
|
+
ColorDrawable(Color.BLUE).apply {
|
|
60
|
+
setBounds(0, 0, 100, 100)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
val result1 = convertDrawableToBase64(drawable1)
|
|
64
|
+
val result2 = convertDrawableToBase64(drawable2)
|
|
65
|
+
|
|
66
|
+
assertNotEquals(
|
|
67
|
+
"Different colored drawables should produce different base64",
|
|
68
|
+
result1,
|
|
69
|
+
result2,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun `PROPERTY - base64 length scales with drawable size`() =
|
|
75
|
+
runTest {
|
|
76
|
+
val small =
|
|
77
|
+
ColorDrawable(Color.RED).apply {
|
|
78
|
+
setBounds(0, 0, 50, 50)
|
|
79
|
+
}
|
|
80
|
+
val medium =
|
|
81
|
+
ColorDrawable(Color.RED).apply {
|
|
82
|
+
setBounds(0, 0, 100, 100)
|
|
83
|
+
}
|
|
84
|
+
val large =
|
|
85
|
+
ColorDrawable(Color.RED).apply {
|
|
86
|
+
setBounds(0, 0, 200, 200)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
val smallResult = convertDrawableToBase64(small)
|
|
90
|
+
val mediumResult = convertDrawableToBase64(medium)
|
|
91
|
+
val largeResult = convertDrawableToBase64(large)
|
|
92
|
+
|
|
93
|
+
assertNotNull(smallResult)
|
|
94
|
+
assertNotNull(mediumResult)
|
|
95
|
+
assertNotNull(largeResult)
|
|
96
|
+
|
|
97
|
+
assertTrue(
|
|
98
|
+
"Medium drawable should produce longer base64 than small",
|
|
99
|
+
mediumResult!!.length > smallResult!!.length,
|
|
100
|
+
)
|
|
101
|
+
assertTrue(
|
|
102
|
+
"Large drawable should produce longer base64 than medium",
|
|
103
|
+
largeResult!!.length > mediumResult.length,
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
fun `PROPERTY - conversion preserves drawable dimensions`() =
|
|
109
|
+
runTest {
|
|
110
|
+
val testSizes =
|
|
111
|
+
listOf(
|
|
112
|
+
Pair(50, 50),
|
|
113
|
+
Pair(100, 75),
|
|
114
|
+
Pair(147, 105), // Typical card icon
|
|
115
|
+
Pair(200, 150),
|
|
116
|
+
Pair(250, 200),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
testSizes.forEach { (width, height) ->
|
|
120
|
+
val drawable =
|
|
121
|
+
ColorDrawable(Color.GREEN).apply {
|
|
122
|
+
setBounds(0, 0, width, height)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
val bitmap = getBitmapFromDrawable(drawable)
|
|
126
|
+
|
|
127
|
+
assertNotNull("Bitmap should not be null for ${width}x$height", bitmap)
|
|
128
|
+
assertEquals("Width should be preserved for ${width}x$height", width, bitmap!!.width)
|
|
129
|
+
assertEquals("Height should be preserved for ${width}x$height", height, bitmap.height)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@Test
|
|
134
|
+
fun `PROPERTY - null input produces null output consistently`() =
|
|
135
|
+
runTest {
|
|
136
|
+
// Invalid drawables (no bounds set) should consistently return null
|
|
137
|
+
val invalidDrawable1 = ColorDrawable(Color.RED)
|
|
138
|
+
val invalidDrawable2 = ColorDrawable(Color.BLUE)
|
|
139
|
+
|
|
140
|
+
val result1 = convertDrawableToBase64(invalidDrawable1)
|
|
141
|
+
val result2 = convertDrawableToBase64(invalidDrawable2)
|
|
142
|
+
|
|
143
|
+
assertNotNull("Result1 should be null for invalid drawable", result1 == null)
|
|
144
|
+
assertNotNull("Result2 should be null for invalid drawable", result2 == null)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@Test
|
|
148
|
+
fun `PROPERTY - aspect ratio is preserved in bitmap`() {
|
|
149
|
+
val testCases =
|
|
150
|
+
listOf(
|
|
151
|
+
Triple(100, 100, 1.0), // Square
|
|
152
|
+
Triple(200, 100, 2.0), // 2:1
|
|
153
|
+
Triple(147, 105, 1.4), // Card icon ratio
|
|
154
|
+
Triple(100, 200, 0.5), // Portrait
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
testCases.forEach { (width, height, expectedRatio) ->
|
|
158
|
+
val drawable =
|
|
159
|
+
ColorDrawable(Color.YELLOW).apply {
|
|
160
|
+
setBounds(0, 0, width, height)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
val bitmap = getBitmapFromDrawable(drawable)
|
|
164
|
+
|
|
165
|
+
assertNotNull(bitmap)
|
|
166
|
+
val actualRatio = bitmap!!.width.toDouble() / bitmap.height.toDouble()
|
|
167
|
+
assertEquals(
|
|
168
|
+
"Aspect ratio should be preserved for ${width}x$height",
|
|
169
|
+
expectedRatio,
|
|
170
|
+
actualRatio,
|
|
171
|
+
0.01, // Allow small floating point error
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@Test
|
|
177
|
+
fun `PROPERTY - conversion is deterministic across multiple runs`() =
|
|
178
|
+
runTest {
|
|
179
|
+
// Same drawable, converted multiple times in sequence, should always yield same result
|
|
180
|
+
val drawable =
|
|
181
|
+
ColorDrawable(Color.CYAN).apply {
|
|
182
|
+
setBounds(0, 0, 120, 90)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
val results = (1..15).map { convertDrawableToBase64(drawable) }
|
|
186
|
+
|
|
187
|
+
// All results should be identical
|
|
188
|
+
val firstResult = results.first()
|
|
189
|
+
results.forEach { result ->
|
|
190
|
+
assertEquals("All conversions should be identical", firstResult, result)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@Test
|
|
195
|
+
fun `PROPERTY - different sizes with same color produce different base64`() =
|
|
196
|
+
runTest {
|
|
197
|
+
val color = Color.rgb(100, 150, 200)
|
|
198
|
+
|
|
199
|
+
val sizes =
|
|
200
|
+
listOf(
|
|
201
|
+
Pair(50, 50),
|
|
202
|
+
Pair(75, 75),
|
|
203
|
+
Pair(100, 100),
|
|
204
|
+
Pair(125, 125),
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
val results =
|
|
208
|
+
sizes.map { (width, height) ->
|
|
209
|
+
val drawable =
|
|
210
|
+
ColorDrawable(color).apply {
|
|
211
|
+
setBounds(0, 0, width, height)
|
|
212
|
+
}
|
|
213
|
+
convertDrawableToBase64(drawable)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// All results should be different (different sizes → different images)
|
|
217
|
+
val uniqueResults = results.toSet()
|
|
218
|
+
assertEquals(
|
|
219
|
+
"Different sizes should produce different base64 even with same color",
|
|
220
|
+
results.size,
|
|
221
|
+
uniqueResults.size,
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
package com.reactnativestripesdk
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.drawable.ColorDrawable
|
|
6
|
+
import kotlinx.coroutines.test.runTest
|
|
7
|
+
import org.junit.Assert.assertEquals
|
|
8
|
+
import org.junit.Assert.assertNotNull
|
|
9
|
+
import org.junit.Assert.assertNull
|
|
10
|
+
import org.junit.Assert.assertTrue
|
|
11
|
+
import org.junit.Test
|
|
12
|
+
import org.junit.runner.RunWith
|
|
13
|
+
import org.robolectric.RobolectricTestRunner
|
|
14
|
+
|
|
15
|
+
@RunWith(RobolectricTestRunner::class)
|
|
16
|
+
class DrawableConversionTest {
|
|
17
|
+
// ============================================
|
|
18
|
+
// convertDrawableToBase64 Tests
|
|
19
|
+
// ============================================
|
|
20
|
+
|
|
21
|
+
@Test
|
|
22
|
+
fun `convertDrawableToBase64 returns non-null for valid drawable`() =
|
|
23
|
+
runTest {
|
|
24
|
+
val drawable =
|
|
25
|
+
ColorDrawable(Color.RED).apply {
|
|
26
|
+
setBounds(0, 0, 100, 100)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
val result = convertDrawableToBase64(drawable)
|
|
30
|
+
|
|
31
|
+
assertNotNull("Base64 should not be null for valid drawable", result)
|
|
32
|
+
assertTrue("Base64 should not be empty", result!!.isNotEmpty())
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
fun `convertDrawableToBase64 returns consistent results for same drawable`() =
|
|
37
|
+
runTest {
|
|
38
|
+
val drawable =
|
|
39
|
+
ColorDrawable(Color.BLUE).apply {
|
|
40
|
+
setBounds(0, 0, 100, 100)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
val result1 = convertDrawableToBase64(drawable)
|
|
44
|
+
val result2 = convertDrawableToBase64(drawable)
|
|
45
|
+
val result3 = convertDrawableToBase64(drawable)
|
|
46
|
+
|
|
47
|
+
assertEquals("Multiple calls should return identical base64", result1, result2)
|
|
48
|
+
assertEquals("Multiple calls should return identical base64", result2, result3)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `convertDrawableToBase64 returns null for invalid drawable`() =
|
|
53
|
+
runTest {
|
|
54
|
+
// Drawable with 0 intrinsic size (never set bounds)
|
|
55
|
+
val drawable = ColorDrawable(Color.RED)
|
|
56
|
+
|
|
57
|
+
val result = convertDrawableToBase64(drawable)
|
|
58
|
+
|
|
59
|
+
assertNull("Base64 should be null for invalid drawable", result)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
fun `convertDrawableToBase64 result is valid base64 format`() =
|
|
64
|
+
runTest {
|
|
65
|
+
val drawable =
|
|
66
|
+
ColorDrawable(Color.GREEN).apply {
|
|
67
|
+
setBounds(0, 0, 50, 50)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
val result = convertDrawableToBase64(drawable)
|
|
71
|
+
|
|
72
|
+
assertNotNull(result)
|
|
73
|
+
// Valid base64 should match pattern: [A-Za-z0-9+/=]+
|
|
74
|
+
assertTrue(
|
|
75
|
+
"Result should be valid base64",
|
|
76
|
+
result!!.matches(Regex("^[A-Za-z0-9+/=\\n]+$")),
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun `convertDrawableToBase64 result size is reasonable`() =
|
|
82
|
+
runTest {
|
|
83
|
+
val drawable =
|
|
84
|
+
ColorDrawable(Color.YELLOW).apply {
|
|
85
|
+
setBounds(0, 0, 100, 100)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
val result = convertDrawableToBase64(drawable)
|
|
89
|
+
|
|
90
|
+
assertNotNull(result)
|
|
91
|
+
// Should be larger than tiny placeholder (134 bytes)
|
|
92
|
+
// but smaller than unreasonably large
|
|
93
|
+
assertTrue(
|
|
94
|
+
"Base64 should be larger than 200 chars (not a 1x1 placeholder)",
|
|
95
|
+
result!!.length > 200,
|
|
96
|
+
)
|
|
97
|
+
assertTrue(
|
|
98
|
+
"Base64 should be smaller than 100KB",
|
|
99
|
+
result.length < 100_000,
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ============================================
|
|
104
|
+
// getBitmapFromDrawable Tests
|
|
105
|
+
// ============================================
|
|
106
|
+
|
|
107
|
+
@Test
|
|
108
|
+
fun `getBitmapFromDrawable returns correct size bitmap`() {
|
|
109
|
+
val drawable =
|
|
110
|
+
ColorDrawable(Color.CYAN).apply {
|
|
111
|
+
setBounds(0, 0, 150, 100)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
val bitmap = getBitmapFromDrawable(drawable)
|
|
115
|
+
|
|
116
|
+
assertNotNull("Bitmap should not be null", bitmap)
|
|
117
|
+
assertEquals("Bitmap width should match drawable", 150, bitmap!!.width)
|
|
118
|
+
assertEquals("Bitmap height should match drawable", 100, bitmap.height)
|
|
119
|
+
assertEquals("Bitmap should use ARGB_8888", Bitmap.Config.ARGB_8888, bitmap.config)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `getBitmapFromDrawable returns null for zero-size drawable`() {
|
|
124
|
+
val drawable = ColorDrawable(Color.MAGENTA)
|
|
125
|
+
// Don't set bounds, intrinsic size will be -1
|
|
126
|
+
|
|
127
|
+
val bitmap = getBitmapFromDrawable(drawable)
|
|
128
|
+
|
|
129
|
+
assertNull("Bitmap should be null for invalid drawable", bitmap)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@Test
|
|
133
|
+
fun `getBitmapFromDrawable preserves drawable dimensions`() {
|
|
134
|
+
// Test with typical card icon dimensions
|
|
135
|
+
val drawable =
|
|
136
|
+
ColorDrawable(Color.RED).apply {
|
|
137
|
+
setBounds(0, 0, 147, 105)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
val bitmap = getBitmapFromDrawable(drawable)
|
|
141
|
+
|
|
142
|
+
assertNotNull(bitmap)
|
|
143
|
+
assertEquals("Width should be preserved", 147, bitmap!!.width)
|
|
144
|
+
assertEquals("Height should be preserved", 105, bitmap.height)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
package com.reactnativestripesdk
|
|
2
|
+
|
|
3
|
+
import android.graphics.Canvas
|
|
4
|
+
import android.graphics.Color
|
|
5
|
+
import android.graphics.ColorFilter
|
|
6
|
+
import android.graphics.PixelFormat
|
|
7
|
+
import android.graphics.drawable.Drawable
|
|
8
|
+
import kotlinx.coroutines.delay
|
|
9
|
+
import kotlinx.coroutines.launch
|
|
10
|
+
import kotlinx.coroutines.test.runTest
|
|
11
|
+
import org.junit.Assert.assertNotNull
|
|
12
|
+
import org.junit.Assert.assertTrue
|
|
13
|
+
import org.junit.Test
|
|
14
|
+
import org.junit.runner.RunWith
|
|
15
|
+
import org.robolectric.RobolectricTestRunner
|
|
16
|
+
|
|
17
|
+
@RunWith(RobolectricTestRunner::class)
|
|
18
|
+
class DrawableLoadingTest {
|
|
19
|
+
/**
|
|
20
|
+
* Mock drawable that simulates DelegateDrawable's async loading behavior
|
|
21
|
+
*/
|
|
22
|
+
class MockAsyncDrawable : Drawable() {
|
|
23
|
+
private var loaded = false
|
|
24
|
+
private var width = 1
|
|
25
|
+
private var height = 1
|
|
26
|
+
|
|
27
|
+
override fun getIntrinsicWidth(): Int = width
|
|
28
|
+
|
|
29
|
+
override fun getIntrinsicHeight(): Int = height
|
|
30
|
+
|
|
31
|
+
suspend fun simulateLoading(delayMs: Long = 100) {
|
|
32
|
+
delay(delayMs)
|
|
33
|
+
width = 150
|
|
34
|
+
height = 100
|
|
35
|
+
loaded = true
|
|
36
|
+
invalidateSelf() // Trigger callback
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override fun draw(canvas: Canvas) {
|
|
40
|
+
// Draw a simple colored rectangle
|
|
41
|
+
canvas.drawColor(Color.RED)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun setAlpha(alpha: Int) {}
|
|
45
|
+
|
|
46
|
+
override fun setColorFilter(colorFilter: ColorFilter?) {}
|
|
47
|
+
|
|
48
|
+
override fun getOpacity(): Int = PixelFormat.OPAQUE
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `waitForDrawableToLoad returns immediately for already loaded drawable`() =
|
|
53
|
+
runTest {
|
|
54
|
+
val drawable = MockAsyncDrawable()
|
|
55
|
+
// Pre-load the drawable
|
|
56
|
+
drawable.simulateLoading(0)
|
|
57
|
+
|
|
58
|
+
val startTime = System.currentTimeMillis()
|
|
59
|
+
val result = waitForDrawableToLoad(drawable, timeoutMs = 3000)
|
|
60
|
+
val elapsed = System.currentTimeMillis() - startTime
|
|
61
|
+
|
|
62
|
+
assertNotNull(result)
|
|
63
|
+
assertTrue("Should return quickly (< 100ms)", elapsed < 100)
|
|
64
|
+
assertTrue("Drawable should be loaded", result.intrinsicWidth > 1)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@Test
|
|
68
|
+
fun `waitForDrawableToLoad waits for drawable to load`() =
|
|
69
|
+
runTest {
|
|
70
|
+
val drawable = MockAsyncDrawable()
|
|
71
|
+
|
|
72
|
+
// Start async loading
|
|
73
|
+
launch {
|
|
74
|
+
drawable.simulateLoading(100)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
val result = waitForDrawableToLoad(drawable, timeoutMs = 3000)
|
|
78
|
+
|
|
79
|
+
assertNotNull(result)
|
|
80
|
+
assertTrue("Drawable should be loaded after waiting", result.intrinsicWidth > 1)
|
|
81
|
+
assertTrue("Drawable should be loaded after waiting", result.intrinsicHeight > 1)
|
|
82
|
+
assertTrue("Width should be 150", result.intrinsicWidth == 150)
|
|
83
|
+
assertTrue("Height should be 100", result.intrinsicHeight == 100)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Test
|
|
87
|
+
fun `waitForDrawableToLoad times out gracefully for drawable that never loads`() =
|
|
88
|
+
runTest {
|
|
89
|
+
val drawable = MockAsyncDrawable() // Never call simulateLoading()
|
|
90
|
+
|
|
91
|
+
val result = waitForDrawableToLoad(drawable, timeoutMs = 500)
|
|
92
|
+
|
|
93
|
+
// Should return the drawable even if timeout (best effort)
|
|
94
|
+
assertNotNull(result)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
fun `convertDrawableToBase64 with async drawable returns consistent results`() =
|
|
99
|
+
runTest {
|
|
100
|
+
val drawable1 = MockAsyncDrawable()
|
|
101
|
+
val drawable2 = MockAsyncDrawable()
|
|
102
|
+
|
|
103
|
+
// Simulate both loading
|
|
104
|
+
launch { drawable1.simulateLoading(50) }
|
|
105
|
+
launch { drawable2.simulateLoading(100) }
|
|
106
|
+
|
|
107
|
+
val result1 = convertDrawableToBase64(drawable1)
|
|
108
|
+
val result2 = convertDrawableToBase64(drawable2)
|
|
109
|
+
|
|
110
|
+
assertNotNull("Result1 should not be null", result1)
|
|
111
|
+
assertNotNull("Result2 should not be null", result2)
|
|
112
|
+
|
|
113
|
+
// Both should be non-empty and similar length (same dimensions)
|
|
114
|
+
assertTrue("Both results should be non-empty", result1!!.isNotEmpty())
|
|
115
|
+
assertTrue("Both results should be non-empty", result2!!.isNotEmpty())
|
|
116
|
+
|
|
117
|
+
// They should be similar length since both are 150x100
|
|
118
|
+
val lengthDiff = kotlin.math.abs(result1.length - result2.length)
|
|
119
|
+
assertTrue(
|
|
120
|
+
"Results should have similar length (both 150x100), diff: $lengthDiff",
|
|
121
|
+
lengthDiff < result1.length * 0.1, // Within 10%
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@Test
|
|
126
|
+
fun `waitForDrawableToLoad handles multiple concurrent calls`() =
|
|
127
|
+
runTest {
|
|
128
|
+
val drawable1 = MockAsyncDrawable()
|
|
129
|
+
val drawable2 = MockAsyncDrawable()
|
|
130
|
+
val drawable3 = MockAsyncDrawable()
|
|
131
|
+
|
|
132
|
+
// Start all loading at the same time
|
|
133
|
+
launch { drawable1.simulateLoading(50) }
|
|
134
|
+
launch { drawable2.simulateLoading(75) }
|
|
135
|
+
launch { drawable3.simulateLoading(100) }
|
|
136
|
+
|
|
137
|
+
// Wait for all concurrently
|
|
138
|
+
val result1 = waitForDrawableToLoad(drawable1, timeoutMs = 3000)
|
|
139
|
+
val result2 = waitForDrawableToLoad(drawable2, timeoutMs = 3000)
|
|
140
|
+
val result3 = waitForDrawableToLoad(drawable3, timeoutMs = 3000)
|
|
141
|
+
|
|
142
|
+
assertNotNull(result1)
|
|
143
|
+
assertNotNull(result2)
|
|
144
|
+
assertNotNull(result3)
|
|
145
|
+
|
|
146
|
+
assertTrue("All drawables should be loaded", result1.intrinsicWidth > 1)
|
|
147
|
+
assertTrue("All drawables should be loaded", result2.intrinsicWidth > 1)
|
|
148
|
+
assertTrue("All drawables should be loaded", result3.intrinsicWidth > 1)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -4,16 +4,18 @@ import com.reactnativestripesdk.utils.PaymentSheetException
|
|
|
4
4
|
import com.reactnativestripesdk.utils.readableArrayOf
|
|
5
5
|
import com.reactnativestripesdk.utils.readableMapOf
|
|
6
6
|
import com.stripe.android.paymentelement.PaymentMethodOptionsSetupFutureUsagePreview
|
|
7
|
+
import com.stripe.android.paymentsheet.CardFundingFilteringPrivatePreview
|
|
7
8
|
import com.stripe.android.paymentsheet.PaymentSheet
|
|
8
9
|
import org.junit.Assert.assertEquals
|
|
9
10
|
import org.junit.Assert.assertNotNull
|
|
10
11
|
import org.junit.Assert.assertNull
|
|
12
|
+
import org.junit.Assert.assertTrue
|
|
11
13
|
import org.junit.Test
|
|
12
14
|
import org.junit.runner.RunWith
|
|
13
15
|
import org.robolectric.RobolectricTestRunner
|
|
14
16
|
|
|
15
17
|
@RunWith(RobolectricTestRunner::class)
|
|
16
|
-
@OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class)
|
|
18
|
+
@OptIn(PaymentMethodOptionsSetupFutureUsagePreview::class, CardFundingFilteringPrivatePreview::class)
|
|
17
19
|
class PaymentElementConfigTest {
|
|
18
20
|
// ============================================
|
|
19
21
|
// buildIntentConfiguration Tests
|
|
@@ -973,4 +975,139 @@ class PaymentElementConfigTest {
|
|
|
973
975
|
val result = buildBillingDetailsCollectionConfiguration(params)
|
|
974
976
|
assertNotNull(result)
|
|
975
977
|
}
|
|
978
|
+
|
|
979
|
+
// ============================================
|
|
980
|
+
// mapToAllowedCardFundingTypes Tests
|
|
981
|
+
// ============================================
|
|
982
|
+
|
|
983
|
+
@Test
|
|
984
|
+
fun mapToAllowedCardFundingTypes_NullParams_ReturnsNull() {
|
|
985
|
+
val result = mapToAllowedCardFundingTypes(null)
|
|
986
|
+
assertNull(result)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
@Test
|
|
990
|
+
fun mapToAllowedCardFundingTypes_NoCardFundingFiltering_ReturnsNull() {
|
|
991
|
+
val params =
|
|
992
|
+
readableMapOf(
|
|
993
|
+
"someOtherKey" to "value",
|
|
994
|
+
)
|
|
995
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
996
|
+
assertNull(result)
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
@Test
|
|
1000
|
+
fun mapToAllowedCardFundingTypes_DebitOnly_ReturnsList() {
|
|
1001
|
+
val params =
|
|
1002
|
+
readableMapOf(
|
|
1003
|
+
"cardFundingFiltering" to
|
|
1004
|
+
readableMapOf(
|
|
1005
|
+
"allowedCardFundingTypes" to readableArrayOf("debit"),
|
|
1006
|
+
),
|
|
1007
|
+
)
|
|
1008
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1009
|
+
assertNotNull(result)
|
|
1010
|
+
assertEquals(1, result?.size)
|
|
1011
|
+
assertEquals(PaymentSheet.CardFundingType.Debit, result?.get(0))
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
@Test
|
|
1015
|
+
fun mapToAllowedCardFundingTypes_CreditOnly_ReturnsList() {
|
|
1016
|
+
val params =
|
|
1017
|
+
readableMapOf(
|
|
1018
|
+
"cardFundingFiltering" to
|
|
1019
|
+
readableMapOf(
|
|
1020
|
+
"allowedCardFundingTypes" to readableArrayOf("credit"),
|
|
1021
|
+
),
|
|
1022
|
+
)
|
|
1023
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1024
|
+
assertNotNull(result)
|
|
1025
|
+
assertEquals(1, result?.size)
|
|
1026
|
+
assertEquals(PaymentSheet.CardFundingType.Credit, result?.get(0))
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
@Test
|
|
1030
|
+
fun mapToAllowedCardFundingTypes_MultipleTypes_ReturnsList() {
|
|
1031
|
+
val params =
|
|
1032
|
+
readableMapOf(
|
|
1033
|
+
"cardFundingFiltering" to
|
|
1034
|
+
readableMapOf(
|
|
1035
|
+
"allowedCardFundingTypes" to readableArrayOf("debit", "credit", "prepaid"),
|
|
1036
|
+
),
|
|
1037
|
+
)
|
|
1038
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1039
|
+
assertNotNull(result)
|
|
1040
|
+
assertEquals(3, result?.size)
|
|
1041
|
+
assertTrue(result!!.contains(PaymentSheet.CardFundingType.Debit))
|
|
1042
|
+
assertTrue(result.contains(PaymentSheet.CardFundingType.Credit))
|
|
1043
|
+
assertTrue(result.contains(PaymentSheet.CardFundingType.Prepaid))
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
@Test
|
|
1047
|
+
fun mapToAllowedCardFundingTypes_AllFourTypes_ReturnsList() {
|
|
1048
|
+
val params =
|
|
1049
|
+
readableMapOf(
|
|
1050
|
+
"cardFundingFiltering" to
|
|
1051
|
+
readableMapOf(
|
|
1052
|
+
"allowedCardFundingTypes" to readableArrayOf("debit", "credit", "prepaid", "unknown"),
|
|
1053
|
+
),
|
|
1054
|
+
)
|
|
1055
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1056
|
+
assertNotNull(result)
|
|
1057
|
+
assertEquals(4, result?.size)
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
@Test
|
|
1061
|
+
fun mapToAllowedCardFundingTypes_EmptyArray_ReturnsNull() {
|
|
1062
|
+
val params =
|
|
1063
|
+
readableMapOf(
|
|
1064
|
+
"cardFundingFiltering" to
|
|
1065
|
+
readableMapOf(
|
|
1066
|
+
"allowedCardFundingTypes" to readableArrayOf(),
|
|
1067
|
+
),
|
|
1068
|
+
)
|
|
1069
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1070
|
+
assertNull(result)
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
@Test
|
|
1074
|
+
fun mapToAllowedCardFundingTypes_InvalidTypes_Filtered() {
|
|
1075
|
+
val params =
|
|
1076
|
+
readableMapOf(
|
|
1077
|
+
"cardFundingFiltering" to
|
|
1078
|
+
readableMapOf(
|
|
1079
|
+
"allowedCardFundingTypes" to readableArrayOf("invalid", "debit", "not_a_type"),
|
|
1080
|
+
),
|
|
1081
|
+
)
|
|
1082
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1083
|
+
assertNotNull(result)
|
|
1084
|
+
assertEquals(1, result?.size)
|
|
1085
|
+
assertEquals(PaymentSheet.CardFundingType.Debit, result?.get(0))
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
@Test
|
|
1089
|
+
fun mapToAllowedCardFundingTypes_OnlyInvalidTypes_ReturnsNull() {
|
|
1090
|
+
val params =
|
|
1091
|
+
readableMapOf(
|
|
1092
|
+
"cardFundingFiltering" to
|
|
1093
|
+
readableMapOf(
|
|
1094
|
+
"allowedCardFundingTypes" to readableArrayOf("invalid", "not_valid"),
|
|
1095
|
+
),
|
|
1096
|
+
)
|
|
1097
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1098
|
+
assertNull(result)
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
@Test
|
|
1102
|
+
fun mapToAllowedCardFundingTypes_MissingAllowedCardFundingTypes_ReturnsNull() {
|
|
1103
|
+
val params =
|
|
1104
|
+
readableMapOf(
|
|
1105
|
+
"cardFundingFiltering" to
|
|
1106
|
+
readableMapOf(
|
|
1107
|
+
"someOtherKey" to "value",
|
|
1108
|
+
),
|
|
1109
|
+
)
|
|
1110
|
+
val result = mapToAllowedCardFundingTypes(params)
|
|
1111
|
+
assertNull(result)
|
|
1112
|
+
}
|
|
976
1113
|
}
|