Fix checkout tests hitting chat connection calls.

This commit is contained in:
Cody Henthorne
2026-07-20 13:31:35 -04:00
committed by Greyson Parrelli
parent 1ae5e884ec
commit 8b71bde890
6 changed files with 70 additions and 0 deletions
@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.settings.app.subscription.permits.DonationPermits
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.testing.DisableAnimationsRule
import org.thoughtcrime.securesms.testing.GooglePayTestRule
import org.thoughtcrime.securesms.testing.InAppPaymentsRule
import org.thoughtcrime.securesms.testing.RxTestSchedulerRule
@@ -54,6 +55,9 @@ class CheckoutFlowActivityTest__OneTimeDonations {
@get:Rule
val composeRule = createEmptyComposeRule()
@get:Rule
val animationsRule = DisableAnimationsRule()
private val intent = CheckoutFlowActivity.createIntent(InstrumentationRegistry.getInstrumentation().targetContext, InAppPaymentType.ONE_TIME_DONATION)
@Before
@@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
import org.thoughtcrime.securesms.database.model.databaseprotos.InAppPaymentData
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.testing.DisableAnimationsRule
import org.thoughtcrime.securesms.testing.GooglePayTestRule
import org.thoughtcrime.securesms.testing.InAppPaymentsRule
import org.thoughtcrime.securesms.testing.RxTestSchedulerRule
@@ -59,6 +60,9 @@ class CheckoutFlowActivityTest__RecurringDonations {
@get:Rule
val composeRule = createEmptyComposeRule()
@get:Rule
val animationsRule = DisableAnimationsRule()
private val intent = CheckoutFlowActivity.createIntent(InstrumentationRegistry.getInstrumentation().targetContext, InAppPaymentType.RECURRING_DONATION)
@Before
@@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.database.DonationReceiptTable
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.testing.DisableAnimationsRule
import org.thoughtcrime.securesms.testing.GooglePayTestRule
import org.thoughtcrime.securesms.testing.InAppPaymentsRule
import org.thoughtcrime.securesms.testing.RxTestSchedulerRule
@@ -59,6 +60,9 @@ class CheckoutFlowActivityTest__Redemption {
@get:Rule
val composeRule = createEmptyComposeRule()
@get:Rule
val animationsRule = DisableAnimationsRule()
private val intent = CheckoutFlowActivity.createIntent(InstrumentationRegistry.getInstrumentation().targetContext, InAppPaymentType.ONE_TIME_DONATION)
@Before
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.database.DonationReceiptTable
import org.thoughtcrime.securesms.database.InAppPaymentTable
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.database.model.InAppPaymentReceiptRecord
import org.thoughtcrime.securesms.testing.DisableAnimationsRule
import org.thoughtcrime.securesms.testing.GooglePayTestRule
import org.thoughtcrime.securesms.testing.InAppPaymentsRule
import org.thoughtcrime.securesms.testing.RawFlag
@@ -69,6 +70,9 @@ class DonationsCheckoutSpecTest(private val spec: DonationsCheckoutTestSpec) {
@get:Rule
val composeRule = createEmptyComposeRule()
@get:Rule
val animationsRule = DisableAnimationsRule()
@Before
fun setUp() {
SignalDatabase.inAppPayments.writableDatabase.deleteAll(InAppPaymentTable.TABLE_NAME)
@@ -0,0 +1,49 @@
/*
* Copyright 2026 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.testing
import android.provider.Settings
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.rules.ExternalResource
import java.io.FileInputStream
/**
* Disables system animation scales for the duration of a test and restores them afterward.
*
* Espresso and [android.app.Instrumentation.waitForIdleSync] only make progress once the main looper is idle. An
* on-screen indeterminate animation (e.g. the checkout's `CircularProgressIndicator`) posts frame callbacks forever, so
* the looper never idles and any idle-gated wait hangs indefinitely rather than timing out. Forcing the scales to 0
* stops those animations so the looper can idle.
*
* Writes go through the shell (`UiAutomation`), which holds `WRITE_SECURE_SETTINGS`; the app process does not.
*/
class DisableAnimationsRule : ExternalResource() {
private val scales = listOf(
Settings.Global.WINDOW_ANIMATION_SCALE,
Settings.Global.TRANSITION_ANIMATION_SCALE,
Settings.Global.ANIMATOR_DURATION_SCALE
)
private lateinit var previous: Map<String, Float>
override fun before() {
val resolver = InstrumentationRegistry.getInstrumentation().targetContext.contentResolver
previous = scales.associateWith { Settings.Global.getFloat(resolver, it, 1f) }
scales.forEach { putScale(it, 0f) }
}
override fun after() {
if (::previous.isInitialized) {
previous.forEach { (key, value) -> putScale(key, value) }
}
}
private fun putScale(key: String, value: Float) {
val stream = InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand("settings put global $key $value")
FileInputStream(stream.fileDescriptor).use { it.readBytes() }
}
}
@@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.testing.endpoints
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.subjects.BehaviorSubject
import org.signal.libsignal.net.ChatConnection
import org.signal.network.websocket.WebSocketRequestMessage
import org.signal.network.websocket.WebSocketResponseMessage
import org.signal.network.websocket.WebsocketResponse
@@ -99,6 +100,10 @@ class ResponderWebSocketConnection(
override fun sendResponse(response: WebSocketResponseMessage) = Unit
override suspend fun <T> runWithChatConnection(callback: (ChatConnection) -> T): T {
throw IOException("Responder does not provide a libsignal chat connection")
}
private fun parseHeaders(headers: List<String>): Map<String, String> {
return headers.mapNotNull { header ->
val separator = header.indexOf(':')