package com.tangem.common import android.Manifest import androidx.compose.ui.test.isRoot import androidx.compose.ui.test.junit4.createEmptyComposeRule import androidx.compose.ui.test.printToLog import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.rule.GrantPermissionRule import com.kaspersky.components.alluresupport.interceptors.step.ScreenshotStepInterceptor import com.kaspersky.components.alluresupport.withForcedAllureSupport import com.kaspersky.components.composesupport.config.addComposeSupport import com.kaspersky.kaspresso.kaspresso.Kaspresso import com.kaspersky.kaspresso.testcases.api.testcase.TestCase import com.tangem.common.allure.FailedStepScreenshotInterceptor import com.tangem.common.constants.TestConstants.ALLURE_LABEL_NAME import com.tangem.common.constants.TestConstants.ALLURE_LABEL_VALUE import com.tangem.common.rules.ApiEnvironmentRule import com.tangem.datasource.api.common.config.managers.ApiConfigsManager import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.domain.promo.PromoRepository import com.tangem.domain.promo.models.PromoId import com.tangem.tap.MainActivity import dagger.hilt.android.testing.HiltAndroidRule import io.qameta.allure.kotlin.Allure import kotlinx.coroutines.runBlocking import org.junit.Rule import org.junit.rules.RuleChain import org.junit.rules.TestRule import javax.inject.Inject abstract class BaseTestCase : TestCase( kaspressoBuilder = Kaspresso.Builder.withForcedAllureSupport( shouldRecordVideo = false ).apply { stepWatcherInterceptors = stepWatcherInterceptors.filter { it !is ScreenshotStepInterceptor }.toMutableList() stepWatcherInterceptors.addAll( listOf( FailedStepScreenshotInterceptor(screenshots) ) ) }.addComposeSupport() ) { @Inject lateinit var apiConfigsManager: ApiConfigsManager @Inject lateinit var appPreferencesStore: AppPreferencesStore @Inject lateinit var promoRepository: PromoRepository private val hiltRule = HiltAndroidRule(this) private val apiEnvironmentRule = ApiEnvironmentRule() private val permissionRule = GrantPermissionRule.grant( Manifest.permission.POST_NOTIFICATIONS, ) /** * It is important to use `ComposeRule` without specifying an activity to ensure that the initialization order of * all test rules is fully controlled. */ val composeTestRule = createEmptyComposeRule() @Rule @JvmField val ruleChain: TestRule = RuleChain .outerRule(hiltRule) .around(applicationInjectionRule()) .around(permissionRule) .around(apiEnvironmentRule) .around(composeTestRule) /** * Initialization order is important: * – DI dependencies must be injected first, * – then the API environment should be set up, * – and only after that the activity should be launched. */ protected fun setupHooks( additionalBeforeSection: () -> Unit = {}, additionalAfterSection: () -> Unit = {}, ) = before { Allure.label(ALLURE_LABEL_NAME, ALLURE_LABEL_VALUE) hiltRule.inject() runBlocking { appPreferencesStore.editData { mutablePreferences -> mutablePreferences.set( key = PreferencesKeys.NOTIFICATIONS_USER_ALLOW_SEND_ADDRESSES_KEY, value = false ) } appPreferencesStore.editData { mutablePreferences -> mutablePreferences.set( key = PreferencesKeys.getShouldShowNotificationKey("EnablePushesReminderNotification"), value = false ) } promoRepository.setNeverToShowWalletPromo(PromoId.Sepa) } apiEnvironmentRule.setup(apiConfigsManager) ActivityScenario.launch(MainActivity::class.java) Intents.init() additionalBeforeSection() }.after { additionalAfterSection() Intents.release() } /** * Prints the Compose semantics tree to logcat for debugging UI tests. * * @param rootIndex Use rootIndex > 0, if you need to print semantics tree for bottom sheet. * Default: 0. * @param useUnmergedTree When true, shows unmerged tree with all individual nodes. * Use for accessing inner elements of compound components. * Default: false (merged tree - accessibility view). * @param tag Log tag for filtering in logcat. Default: "SEMANTIC_TREE". * @param maxDepth Maximum nesting level to print. Use to avoid log overflow. * Default: Int.MAX_VALUE (unlimited depth). */ fun printSemanticTree( rootIndex: Int = 0, useUnmergedTree: Boolean = false, tag: String = "SEMANTIC_TREE", maxDepth: Int = Int.MAX_VALUE ) { composeTestRule.onAllNodes(isRoot(), useUnmergedTree = useUnmergedTree)[rootIndex] .printToLog(tag, maxDepth) } fun waitForIdle() = composeTestRule.waitForIdle() private fun applicationInjectionRule(): ApplicationInjectionExecutionRule { return ApplicationInjectionExecutionRule( toggleStates = mapOf( "NEW_TOKEN_RECEIVE_ENABLED" to true, "SWAP_REDESIGN_ENABLED" to false, "NEW_ONRAMP_MAIN_ENABLED" to true, "HOT_WALLET_ENABLED" to true, "YIELD_SUPPLY_FEATURE_ENABLED" to true, "ACCOUNTS_FEATURE_ENABLED" to true ) ) } }