Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package to.bitkit.ui.components

import android.content.res.Configuration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import kotlinx.collections.immutable.persistentListOf
import org.junit.Rule
import org.junit.Test
import to.bitkit.repositories.MethodId
import to.bitkit.test.annotations.ComposeUi
import to.bitkit.ui.theme.AppThemeSurface
import java.util.Locale
import kotlin.test.assertEquals
import kotlin.test.assertTrue

@ComposeUi
class PaykitAcceptedMethodsTest {
@get:Rule
val composeTestRule = createComposeRule()

@Test
fun largeLocalizedMethodsWrapAndRemainReachable() {
val clicked = mutableListOf<PaykitPaymentOption>()
val methods = persistentListOf(
MethodId.P2wpkh.rawValue,
MethodId.Bolt11.rawValue,
MethodId.UsdtArbitrum.rawValue,
)
composeTestRule.setContent {
val configuration = Configuration(LocalConfiguration.current).apply { setLocale(Locale.GERMANY) }
val localizedContext = LocalContext.current.createConfigurationContext(configuration)
CompositionLocalProvider(
LocalContext provides localizedContext,
LocalConfiguration provides configuration,
LocalDensity provides Density(LocalDensity.current.density, fontScale = 1.5f),
) {
AppThemeSurface {
Box(modifier = Modifier.width(280.dp).testTag("MethodsBounds")) {
PaykitAcceptedMethods(
title = "USDT",
availableMethods = methods,
selectedMethods = methods,
testTagPrefix = "PaymentRequest",
onClickMethod = { clicked += it },
)
}
}
}
}

val bounds = composeTestRule.onNodeWithTag("MethodsBounds").fetchSemanticsNode().boundsInRoot
val chips = PaykitPaymentOption.entries.map { option ->
composeTestRule.onNodeWithTag("PaymentRequestAccept-${option.id}").also {
it.assertIsDisplayed().performClick()
}.fetchSemanticsNode().boundsInRoot
}
chips.forEach { chip ->
assertTrue(chip.left >= bounds.left && chip.right <= bounds.right)
}
assertTrue(chips.last().top > chips.first().top)
assertEquals(PaykitPaymentOption.entries.toList(), clicked)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class CreatePaymentRequestScreenTest {
}

@Test
fun acceptedAssetsGroupsBitcoinRailsAndRequiresASelection() {
fun acceptedMethodsCanBeRemovedIndependentlyAndRequireASelection() {
var submitted: PaykitPaymentRequestDraft? = null
composeTestRule.setContent {
AppThemeSurface {
Expand All @@ -94,20 +94,29 @@ class CreatePaymentRequestScreenTest {
)
}
}
composeTestRule.onNodeWithTag("PaymentRequestAccept-bitcoin")
composeTestRule.onNodeWithTag("PaymentRequestAccept-savings")
.performScrollTo().assertIsSelected().performClick()
composeTestRule.onNodeWithTag("PaymentRequestAccept-spending").assertIsSelected()
composeTestRule.onNodeWithTag("PaymentRequestSend").performClick()
assertEquals(
listOf(MethodId.Bolt11.rawValue, MethodId.UsdtArbitrum.rawValue),
submitted?.acceptedPaymentEndpointIdentifiers
)
composeTestRule.onNodeWithTag("PaymentRequestAccept-spending").performClick().assertIsNotSelected()
composeTestRule.onNodeWithTag("PaymentRequestAccept-usdt").assertIsSelected()
SystemClock.sleep(600)
composeTestRule.onNodeWithTag("PaymentRequestSend").performClick()
assertEquals(listOf(MethodId.UsdtArbitrum.rawValue), submitted?.acceptedPaymentEndpointIdentifiers)
composeTestRule.onNodeWithTag("PaymentRequestAccept-usdt").performClick().assertIsNotSelected()
composeTestRule.onNodeWithTag("PaymentRequestSend").assertIsNotEnabled()
// Chip clicks use a 500 ms real-clock debounce, independent of the Compose test clock.
SystemClock.sleep(600)
composeTestRule.onNodeWithTag("PaymentRequestAccept-bitcoin").performClick().assertIsSelected()
composeTestRule.onNodeWithTag("PaymentRequestAccept-savings").performClick().assertIsSelected()
SystemClock.sleep(600)
composeTestRule.onNodeWithTag("PaymentRequestSend").assertIsEnabled().performClick()
assertEquals(
setOf(MethodId.P2wpkh.rawValue, MethodId.Bolt11.rawValue),
submitted?.acceptedPaymentEndpointIdentifiers?.toSet()
listOf(MethodId.P2wpkh.rawValue),
submitted?.acceptedPaymentEndpointIdentifiers
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
@file:OptIn(ExperimentalTime::class)

package to.bitkit.ui.screens.paymentrequests

import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import kotlinx.collections.immutable.persistentListOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import to.bitkit.models.PaykitAmount
import to.bitkit.models.PaykitAsset
import to.bitkit.repositories.AmountInputHandler
import to.bitkit.repositories.PaykitPaymentRequestDraft
import to.bitkit.test.annotations.ComposeUi
import to.bitkit.ui.theme.AppThemeSurface
import to.bitkit.viewmodels.AmountInputViewModel
import java.util.Locale
import kotlin.test.assertEquals
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

@ComposeUi
class PaymentRequestAmountLocaleTest {
private val originalLocale = Locale.getDefault()

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun setup() {
Locale.setDefault(Locale.GERMANY)
}

@After
fun teardown() {
Locale.setDefault(originalLocale)
}

@Test
fun commaDecimalEntryPreservesExactCentsAndTrailingZeros() {
var submitted: PaykitPaymentRequestDraft? = null
val amountInput = AmountInputViewModel(AmountInputHandler.stub())
composeTestRule.setContent {
AppThemeSurface {
CompositionLocalProvider(LocalInspectionMode provides true) {
PaymentRequestAmountContent(
amountInputViewModel = amountInput,
initialDraft = PaykitPaymentRequestDraft(
amount = PaykitAmount(PaykitAsset.USD, 0uL),
note = "",
expiresAt = Instant.fromEpochSeconds(2_000_000_000),
),
contact = null,
onBack = {},
onContinue = { submitted = it },
allowedAssets = persistentListOf(PaykitAsset.USD),
)
}
}
}

composeTestRule.onNodeWithText(",", useUnmergedTree = true).assertIsDisplayed()
listOf("N1", "N5", "N0", "NDecimal").forEach {
composeTestRule.onNodeWithTag(it).performClick()
}
composeTestRule.onNodeWithText("150,", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithTag("PaymentRequestAmountContinue").assertIsEnabled()
composeTestRule.onNodeWithTag("N5").performClick()
composeTestRule.onNodeWithTag("N0").performClick()
composeTestRule.onNodeWithText("150,50", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithTag("PaymentRequestAmountContinue").performClick()

assertEquals(PaykitAmount(PaykitAsset.USD, 15_050uL), submitted?.amount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import to.bitkit.repositories.PaykitSubscriptionMetadata
import to.bitkit.repositories.PaykitSubscriptionRecurrence
import to.bitkit.repositories.PaykitSubscriptionRole
import to.bitkit.test.annotations.ComposeUi
import to.bitkit.ui.components.PaykitPaymentOption
import to.bitkit.ui.screens.paymentrequests.PaymentRequestExpiration
import to.bitkit.ui.theme.AppThemeSurface
import kotlin.test.assertEquals
Expand All @@ -55,15 +56,17 @@ class CreateSubscriptionScreenTest {
@Test
fun detailsRequiresAmountNameAndCompletedIconLoad() {
var amount by mutableStateOf(0uL)
var asset by mutableStateOf(PaykitAsset.BTC)
var name by mutableStateOf("")
var frequency by mutableStateOf(PaykitRecurrenceUnit.Month)
var isLoadingIcon by mutableStateOf(false)
var choseRecipient = false
var clickedMethod: PaykitPaymentOption? = null
var acceptedMethods by mutableStateOf(persistentListOf(MethodId.P2wpkh.rawValue))
composeTestRule.setContent {
AppThemeSurface {
CreateSubscriptionDetails(
amount = PaykitAmount(PaykitAsset.BTC, amount),
amount = PaykitAmount(asset, amount),
availableMethods = persistentListOf(MethodId.P2wpkh.rawValue, MethodId.UsdtArbitrum.rawValue),
acceptedMethods = acceptedMethods,
name = name,
Expand All @@ -72,7 +75,7 @@ class CreateSubscriptionScreenTest {
selectedIconUri = null,
isLoadingIcon = isLoadingIcon,
onAmountClick = {},
onClickAsset = {},
onClickMethod = { clickedMethod = it },
onNameChange = { name = it },
onDescriptionChange = {},
onFrequencyChange = { frequency = it },
Expand All @@ -97,9 +100,19 @@ class CreateSubscriptionScreenTest {
composeTestRule.onNodeWithTag("Tab-${option.name.lowercase()}").performClick()
assertEquals(option, frequency)
}
composeTestRule.onNodeWithTag("SubscriptionAccept-savings").performClick()
assertEquals(PaykitPaymentOption.Savings, clickedMethod)
composeTestRule.runOnIdle { acceptedMethods = persistentListOf() }
composeTestRule.onNodeWithTag("SubscriptionChooseRecipient").assertIsNotEnabled()
composeTestRule.runOnIdle { acceptedMethods = persistentListOf(MethodId.P2wpkh.rawValue) }
composeTestRule.onNodeWithTag("SubscriptionAccept-savings").assertIsNotSelected()
composeTestRule.onNodeWithTag("SubscriptionAccept-usdt").performClick()
assertEquals(PaykitPaymentOption.Usdt, clickedMethod)
composeTestRule.runOnIdle {
asset = PaykitAsset.USD
acceptedMethods = persistentListOf(MethodId.UsdtArbitrum.rawValue)
}
composeTestRule.onNodeWithTag("SubscriptionAccept-usdt").assertIsSelected()
composeTestRule.onNodeWithTag("SubscriptionChooseRecipient").assertIsEnabled()
composeTestRule.runOnIdle { isLoadingIcon = true }
composeTestRule.onNodeWithTag("SubscriptionChooseRecipient").assertIsNotEnabled()
composeTestRule.runOnIdle { isLoadingIcon = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.assertIsNotEnabled
Expand All @@ -19,6 +21,8 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.test.platform.app.InstrumentationRegistry
import com.synonym.bitkitcore.UsdtDeposit
import com.synonym.bitkitcore.UsdtDepositDetail
Expand Down Expand Up @@ -152,7 +156,7 @@ class UsdtBridgeDetailsTest {
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("USDT address on BSC").performScrollTo().assertIsDisplayed()
composeTestRule.onNodeWithTag("UsdtDepositRefund").assertIsNotEnabled()
assertHeight("UsdtRefundAddress", 52f)
assertHeight("UsdtRefundAddress", 56f)
screenshot("receiving-details")
val refundField = composeTestRule.onNodeWithTag("UsdtRefundAddress")
refundField.performTextInput(address.take(20))
Expand All @@ -171,11 +175,13 @@ class UsdtBridgeDetailsTest {
screenshot("refund")
composeTestRule.onNodeWithTag("UsdtRefundCancel").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag("UsdtDepositDetails").performClick()
waitForClickDebounce()
composeTestRule.onNodeWithTag("UsdtDepositDetails").performScrollTo().performClick()
composeTestRule.waitForIdle()
screenshot("receiving-summary-after-refund")
composeTestRule.onNodeWithText("Needs attention").assertIsDisplayed()
waitForClickDebounce()
composeTestRule.onNodeWithTag("UsdtDepositDetails").performClick()
composeTestRule.onNodeWithTag("UsdtDepositDetails").performScrollTo().performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText(address).performScrollTo().assertIsDisplayed()
composeTestRule.onNodeWithTag("UsdtDepositHistory").performClick()
Expand All @@ -198,6 +204,29 @@ class UsdtBridgeDetailsTest {
}
}

@Test
fun refundAddressExpandsForLargeText() {
val enteredAddress = mutableStateOf("")
composeTestRule.setContent {
CompositionLocalProvider(LocalDensity provides Density(LocalDensity.current.density, fontScale = 2f)) {
Frame {
UsdtDepositsContent(
state = DepositUiState(detail = UsdtDepositDetail(deposit, null)),
address = enteredAddress.value, hideBalance = false,
onBack = {}, onAddress = { enteredAddress.value = it },
onDetail = {}, onHistory = {}, onMore = {}, onRefresh = {}, onRefund = {},
)
}
}
}
composeTestRule.onNodeWithTag("UsdtDepositDetails").performScrollTo().performClick()
val field = composeTestRule.onNodeWithTag("UsdtRefundAddress")
field.performScrollTo().performTextInput(address)
field.assertTextContains(address)
screenshot("refund-large-font")
field.assertHeightIsAtLeast(64.dp)
}

@Composable
private fun Frame(content: @Composable () -> Unit) {
AppThemeSurface {
Expand Down
Loading
Loading