Updated on 2026-08-14
This commit is contained in:
commit
fce19374d4
185 changed files with 7959 additions and 1134 deletions
|
|
@ -6,14 +6,25 @@ import org.json.JSONObject
|
|||
import com.tangem.utils.logging.TangemLogger
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
// WC URIs embed a session symKey that lets anyone join/hijack the session — strip it before logging.
|
||||
private val WC_SECRET_REGEX = Regex("(symKey(?:=|%3D))[^&\\s\"']+", RegexOption.IGNORE_CASE)
|
||||
|
||||
private fun redactWcSecrets(text: String): String =
|
||||
WC_SECRET_REGEX.replace(text) { "${it.groupValues[1]}<redacted>" }
|
||||
|
||||
/**
|
||||
* Requests a WalletConnect URI from the qa-tools service.
|
||||
*
|
||||
* Response shape (see qa-tools `/wc_uri` swagger):
|
||||
* - 200: { success: true, wcUri: "wc:...", network, wallet, tangemDeepLink, timestamp, processingTime }
|
||||
* - 5xx: { error, network, timestamp, errorType }
|
||||
*/
|
||||
fun getWcUri(
|
||||
network: String = "ethereum",
|
||||
baseUrl: String = "[REDACTED_ENV_URL]"
|
||||
): String? {
|
||||
TangemLogger.i("Getting WC URI for network: $network")
|
||||
val url = "$baseUrl/wc_uri?network=$network"
|
||||
TangemLogger.i("getWcUri: requesting $url")
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
|
|
@ -23,37 +34,94 @@ fun getWcUri(
|
|||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$baseUrl/wc_uri?network=$network")
|
||||
.url(url)
|
||||
.header("Accept", "application/json")
|
||||
.get()
|
||||
.build()
|
||||
|
||||
return try {
|
||||
client.newCall(request).execute().use { response ->
|
||||
TangemLogger.i("Response code: ${response.code}")
|
||||
val body = response.body?.string().orEmpty()
|
||||
val contentType = response.header("Content-Type") ?: "<missing>"
|
||||
TangemLogger.i(
|
||||
"getWcUri: HTTP ${response.code} ${response.message}, " +
|
||||
"Content-Type=$contentType, body.length=${body.length}"
|
||||
)
|
||||
TangemLogger.i("getWcUri: raw body=${redactWcSecrets(body)}")
|
||||
|
||||
if (response.isSuccessful) {
|
||||
val body = response.body?.string() ?: ""
|
||||
TangemLogger.i("Response body: $body")
|
||||
|
||||
val jsonObject = JSONObject(body)
|
||||
|
||||
if (jsonObject.getBoolean("success")) {
|
||||
val wcUri = jsonObject.getString("wcUri")
|
||||
TangemLogger.i("Got WC URI successfully: $wcUri")
|
||||
|
||||
wcUri
|
||||
} else {
|
||||
TangemLogger.e("API returned error: ${jsonObject.optString("error", "Unknown")}")
|
||||
null
|
||||
}
|
||||
} else {
|
||||
val errorBody = response.body?.string() ?: "No error body"
|
||||
TangemLogger.e("Request failed: ${response.code}, body: $errorBody")
|
||||
null
|
||||
if (!response.isSuccessful) {
|
||||
TangemLogger.e("getWcUri: non-2xx response (${response.code}), body=${redactWcSecrets(body)}")
|
||||
return@use null
|
||||
}
|
||||
|
||||
if (body.isBlank()) {
|
||||
TangemLogger.e("getWcUri: response body is empty")
|
||||
return@use null
|
||||
}
|
||||
|
||||
if (contentType.contains("text/html", ignoreCase = true) ||
|
||||
body.trimStart().startsWith("<")
|
||||
) {
|
||||
val server = response.header("Server").orEmpty()
|
||||
val wwwAuth = response.header("WWW-Authenticate").orEmpty()
|
||||
val isCloudflareAccess = server.contains("cloudflare", ignoreCase = true) ||
|
||||
wwwAuth.contains("Cloudflare-Access", ignoreCase = true) ||
|
||||
body.contains("cloudflareaccess.com", ignoreCase = true)
|
||||
if (isCloudflareAccess) {
|
||||
TangemLogger.e(
|
||||
"getWcUri: blocked by Cloudflare Access. The test runner is not " +
|
||||
"authorized to reach $url — connect to the corporate VPN or " +
|
||||
"configure a Cloudflare Access service token (CF-Access-Client-Id / " +
|
||||
"CF-Access-Client-Secret headers) on the device."
|
||||
)
|
||||
} else {
|
||||
TangemLogger.e(
|
||||
"getWcUri: server returned HTML instead of JSON for $url. " +
|
||||
"Verify [REDACTED_ENV_URL] and the current API in /docs."
|
||||
)
|
||||
}
|
||||
return@use null
|
||||
}
|
||||
|
||||
val jsonObject = try {
|
||||
JSONObject(body)
|
||||
} catch (e: Exception) {
|
||||
TangemLogger.e("getWcUri: failed to parse body as JSON: ${redactWcSecrets(body)}", e)
|
||||
return@use null
|
||||
}
|
||||
|
||||
TangemLogger.i("getWcUri: response keys=${jsonObject.keys().asSequence().toList()}")
|
||||
|
||||
val success = jsonObject.optBoolean("success", false)
|
||||
if (!success) {
|
||||
val error = jsonObject.optString("error", "<no error field>")
|
||||
val errorType = jsonObject.optString("errorType", "<no errorType field>")
|
||||
TangemLogger.e(
|
||||
"getWcUri: API success=false, error=$error, errorType=$errorType, body=${redactWcSecrets(body)}"
|
||||
)
|
||||
return@use null
|
||||
}
|
||||
|
||||
val wcUri = jsonObject.optString("wcUri", "")
|
||||
val tangemDeepLink = jsonObject.optString("tangemDeepLink", "")
|
||||
val processingTime = jsonObject.optString("processingTime", "")
|
||||
TangemLogger.i(
|
||||
"getWcUri: parsed wcUri=${redactWcSecrets(wcUri)}, " +
|
||||
"tangemDeepLink=${redactWcSecrets(tangemDeepLink)}, processingTime=$processingTime"
|
||||
)
|
||||
|
||||
if (wcUri.isBlank() || !wcUri.startsWith("wc:")) {
|
||||
TangemLogger.e(
|
||||
"getWcUri: wcUri is missing or has unexpected format. " +
|
||||
"wcUri='${redactWcSecrets(wcUri)}', body=${redactWcSecrets(body)}"
|
||||
)
|
||||
return@use null
|
||||
}
|
||||
|
||||
wcUri
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
TangemLogger.e("Error getting WC URI", e)
|
||||
TangemLogger.e("getWcUri: exception while requesting $url", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ import androidx.test.core.app.ApplicationProvider
|
|||
import io.github.kakaocup.kakao.intent.KIntent
|
||||
|
||||
fun openAppByDeepLink(deepLinkUri: String?) {
|
||||
val deeplinkScheme = "tangem://wc?uri="
|
||||
requireNotNull(deepLinkUri) { "openAppByDeepLink: deepLinkUri is null" }
|
||||
val finalUri = Uri.parse(deepLinkUri)
|
||||
val context = ApplicationProvider.getApplicationContext<android.content.Context>()
|
||||
val intent = Intent(ACTION_VIEW, Uri.parse(deeplinkScheme + deepLinkUri)).apply {
|
||||
val intent = Intent(ACTION_VIEW, finalUri).apply {
|
||||
addFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,15 +140,6 @@ fun BaseTestCase.checkWalletConnectDetailsBottomSheet(dAppName: String) {
|
|||
step("Assert app URL is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { appUrl.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet icon is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletIcon.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet title is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletTitle.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert wallet name is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { walletName.assertIsDisplayed() }
|
||||
}
|
||||
step("Assert 'Connected networks' title is displayed") {
|
||||
onWalletConnectDetailsBottomSheet { connectedNetworksTitle.assertIsDisplayed() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,36 @@
|
|||
package com.tangem.tests
|
||||
package com.tangem.tests.walletConnect
|
||||
|
||||
import android.Manifest
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants.WAIT_UNTIL_TIMEOUT
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.getWcUri
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.scenarios.*
|
||||
import com.tangem.scenarios.checkWalletConnectBottomSheet
|
||||
import com.tangem.scenarios.checkWalletConnectDetailsBottomSheet
|
||||
import com.tangem.scenarios.checkWalletConnectScreen
|
||||
import com.tangem.scenarios.openAppByDeepLink
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.openWalletConnectScreen
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.onScanQrScreen
|
||||
import com.tangem.screens.onWalletConnectBottomSheet
|
||||
import com.tangem.screens.onWalletConnectDetailsBottomSheet
|
||||
import com.tangem.screens.onScanQrScreen
|
||||
import com.tangem.screens.onWalletConnectScreen
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class WalletConnectTest : BaseTestCase() {
|
||||
class EthereumWalletConnectTest : BaseTestCase() {
|
||||
|
||||
@AllureId("3958")
|
||||
@DisplayName("WC (React App): open session from deeplink on main screen")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionOnMainScreenTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
setupHooks().run {
|
||||
|
|
@ -36,11 +40,11 @@ class WalletConnectTest : BaseTestCase() {
|
|||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +63,7 @@ class WalletConnectTest : BaseTestCase() {
|
|||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -80,10 +84,9 @@ class WalletConnectTest : BaseTestCase() {
|
|||
|
||||
@AllureId("3959")
|
||||
@DisplayName("WC (React App): open session from deeplink not on main screen")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionNotOnMainScreenTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
setupHooks().run {
|
||||
|
|
@ -97,11 +100,11 @@ class WalletConnectTest : BaseTestCase() {
|
|||
openWalletConnectScreen()
|
||||
checkWalletConnectScreen(false)
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +117,7 @@ class WalletConnectTest : BaseTestCase() {
|
|||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +125,7 @@ class WalletConnectTest : BaseTestCase() {
|
|||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
}
|
||||
|
|
@ -136,11 +139,10 @@ class WalletConnectTest : BaseTestCase() {
|
|||
}
|
||||
|
||||
@AllureId("3957")
|
||||
@DisplayName("WC (React App): open session from deeplink ")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@DisplayName("WC (React App): open session from deeplink")
|
||||
@Test
|
||||
fun openWalletConnectSessionTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val deepLinkUri = getWcUri()
|
||||
|
||||
|
|
@ -154,11 +156,11 @@ class WalletConnectTest : BaseTestCase() {
|
|||
step("Kill app") {
|
||||
device.apps.kill(packageName)
|
||||
}
|
||||
step("Create WC session buy deeplink") {
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
|
|
@ -191,10 +193,9 @@ class WalletConnectTest : BaseTestCase() {
|
|||
|
||||
@AllureId("887")
|
||||
@DisplayName("WC: open session by 'Paste from clipboard' button")
|
||||
@Ignore("TODO [REDACTED_JIRA] React app deeplink doesn't work")
|
||||
@Test
|
||||
fun openWalletConnectSessionByClipboardLinkTest() {
|
||||
val dAppName = "React App"
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val context = device.context
|
||||
val deepLinkUri = getWcUri()
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
|
|
@ -225,7 +226,7 @@ class WalletConnectTest : BaseTestCase() {
|
|||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
waitForIdle()
|
||||
flakySafely(WAIT_UNTIL_TIMEOUT) {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,301 @@
|
|||
package com.tangem.tests.walletConnect
|
||||
|
||||
import android.Manifest
|
||||
import com.tangem.common.BaseTestCase
|
||||
import com.tangem.common.constants.TestConstants
|
||||
import com.tangem.common.constants.TestConstants.USER_TOKENS_API_SCENARIO
|
||||
import com.tangem.common.extensions.clickWithAssertion
|
||||
import com.tangem.common.utils.getWcUri
|
||||
import com.tangem.common.utils.resetWireMockScenarioState
|
||||
import com.tangem.common.utils.setClipboardText
|
||||
import com.tangem.common.utils.setWireMockScenarioState
|
||||
import com.tangem.scenarios.checkWalletConnectBottomSheet
|
||||
import com.tangem.scenarios.checkWalletConnectDetailsBottomSheet
|
||||
import com.tangem.scenarios.checkWalletConnectScreen
|
||||
import com.tangem.scenarios.openAppByDeepLink
|
||||
import com.tangem.scenarios.openMainScreen
|
||||
import com.tangem.scenarios.openWalletConnectScreen
|
||||
import com.tangem.scenarios.synchronizeAddresses
|
||||
import com.tangem.screens.onScanQrScreen
|
||||
import com.tangem.screens.onWalletConnectBottomSheet
|
||||
import com.tangem.screens.onWalletConnectDetailsBottomSheet
|
||||
import com.tangem.screens.onWalletConnectScreen
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import io.qameta.allure.kotlin.AllureId
|
||||
import io.qameta.allure.kotlin.junit4.DisplayName
|
||||
import org.junit.Test
|
||||
|
||||
@HiltAndroidTest
|
||||
class SolanaWalletConnectTest : BaseTestCase() {
|
||||
|
||||
@AllureId("4023")
|
||||
@DisplayName("WC (Raydium): open session from deeplink on main screen")
|
||||
@Test
|
||||
fun openWalletConnectSessionOnMainScreenTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Assert 'Connect' button is enabled") {
|
||||
onWalletConnectBottomSheet { connectButton.assertIsEnabled() }
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4024")
|
||||
@DisplayName("WC (Raydium): open session from deeplink not on main screen")
|
||||
@Test
|
||||
fun openWalletConnectSessionNotOnMainScreenTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
checkWalletConnectScreen(false)
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4025")
|
||||
@DisplayName("WC (Raydium): open session from deeplink")
|
||||
@Test
|
||||
fun openWalletConnectSessionTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
|
||||
setupHooks(
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Kill app") {
|
||||
device.apps.kill(packageName)
|
||||
}
|
||||
step("Create WC session by deeplink") {
|
||||
openAppByDeepLink(deepLinkUri)
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AllureId("4026")
|
||||
@DisplayName("WC: open session by 'Paste from clipboard' button")
|
||||
@Test
|
||||
fun openWalletConnectSessionByClipboardLinkTest() {
|
||||
val dAppName = "Tangem QA Tools"
|
||||
val context = device.context
|
||||
val deepLinkUri = getWcUri("solana")
|
||||
val scenarioState = "Solana"
|
||||
val packageName = BuildConfig.APPLICATION_ID
|
||||
val permissionName = Manifest.permission.CAMERA
|
||||
|
||||
setupHooks(
|
||||
additionalBeforeSection = {
|
||||
device.uiDevice.executeShellCommand("pm grant $packageName $permissionName")
|
||||
|
||||
},
|
||||
additionalAfterSection = {
|
||||
resetWireMockScenarioState(USER_TOKENS_API_SCENARIO)
|
||||
}
|
||||
).run {
|
||||
|
||||
step("Set WireMock scenario: '$USER_TOKENS_API_SCENARIO' to state: '$scenarioState'") {
|
||||
setWireMockScenarioState(USER_TOKENS_API_SCENARIO, scenarioState)
|
||||
}
|
||||
|
||||
step("Set URI to clipboard") {
|
||||
setClipboardText(context, deepLinkUri)
|
||||
}
|
||||
step("Open 'Main Screen'") {
|
||||
openMainScreen()
|
||||
}
|
||||
step("Synchronize addresses") {
|
||||
synchronizeAddresses()
|
||||
}
|
||||
step("Open 'Wallet Connect' screen") {
|
||||
openWalletConnectScreen()
|
||||
}
|
||||
step("Click 'New connection' button") {
|
||||
onWalletConnectScreen { newConnectionButton.performClick() }
|
||||
}
|
||||
step("CLick 'Paste from clipboard' button") {
|
||||
onScanQrScreen { pasteFromClipboardButton.clickWithAssertion() }
|
||||
}
|
||||
step("Check 'Wallet Connect' bottom sheet") {
|
||||
waitForIdle()
|
||||
flakySafely(TestConstants.WAIT_UNTIL_TIMEOUT) {
|
||||
checkWalletConnectBottomSheet()
|
||||
}
|
||||
}
|
||||
step("Click on 'Connect' button") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.performClick() }
|
||||
}
|
||||
step("Assert 'Connect' button is not displayed") {
|
||||
waitForIdle()
|
||||
onWalletConnectBottomSheet { connectButton.assertIsNotDisplayed() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen with connections") {
|
||||
checkWalletConnectScreen(withConnections = true)
|
||||
}
|
||||
step("Click on app icon") {
|
||||
onWalletConnectScreen { appIcon.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' details bottom sheet") {
|
||||
checkWalletConnectDetailsBottomSheet(dAppName)
|
||||
}
|
||||
step("Click on 'Disconnect' button") {
|
||||
onWalletConnectDetailsBottomSheet { disconnectButton.performClick() }
|
||||
}
|
||||
step("Check 'Wallet Connect' screen without connections") {
|
||||
checkWalletConnectScreen(withConnections = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,12 +66,6 @@ sealed class AnalyticsParam {
|
|||
data object BlockchainSdk : Error("Blockchain Sdk Error")
|
||||
}
|
||||
|
||||
sealed class WalletCreationType(val value: String) {
|
||||
data object PrivateKey : WalletCreationType(value = "Private Key")
|
||||
data object NewSeed : WalletCreationType(value = "New Seed")
|
||||
data object SeedImport : WalletCreationType(value = "Seed Import")
|
||||
}
|
||||
|
||||
sealed class AppTheme(val value: String) {
|
||||
data object System : AppTheme("System")
|
||||
data object Dark : AppTheme("Dark")
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class Onboarding(
|
||||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class Finished : Onboarding("Onboarding", "Onboarding Finished")
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
|
||||
class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In")
|
||||
class ButtonCardSignIn : SignIn(event = "Button - Card Sign In")
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import arrow.core.right
|
|||
import com.tangem.common.*
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
|
|
@ -19,10 +20,7 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository.LockMethod
|
||||
import com.tangem.domain.common.wallets.error.*
|
||||
import com.tangem.domain.hotwallet.repository.HotWalletRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.*
|
||||
import com.tangem.domain.wallets.R
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
|
|
@ -261,7 +259,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
when (unlockMethod) {
|
||||
UserWalletsListRepository.UnlockMethod.Biometric -> {
|
||||
unlockAllWallets().bind()
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Biometric)
|
||||
select(userWalletId)
|
||||
}
|
||||
UserWalletsListRepository.UnlockMethod.AccessCode -> {
|
||||
|
|
@ -292,7 +290,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
sensitiveInformationRepository.getAll(listOf(encryptionKey))
|
||||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { it?.updateWith(sensitiveInfo) }
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.AccessCode)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.AccessCode)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -333,7 +331,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
walletIdToDerivedKeys = mapOf(userWallet.walletId to scanResponse.derivedKeys),
|
||||
)
|
||||
}
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Card)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Card)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -376,7 +374,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { wallets -> wallets?.updateWith(sensitiveInfo) }
|
||||
selectedUserWallet.value?.let {
|
||||
trackSignInEvent(it, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(it, AnalyticsParam.SignInType.Biometric)
|
||||
}
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
|
|
@ -605,18 +603,14 @@ internal class DefaultUserWalletsListRepository(
|
|||
return lastOrNull()
|
||||
}
|
||||
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: Basic.SignedIn.SignInType) {
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: AnalyticsParam.SignInType) {
|
||||
trackingContextProxy.addContext(userWallet)
|
||||
val isBackedUp = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> userWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = type,
|
||||
walletsCount = userWallets.value?.size ?: 0,
|
||||
isImported = userWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = userWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ import com.tangem.common.routing.entity.InitScreenLaunchMode
|
|||
import com.tangem.common.ui.notifications.NotificationId
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
|
|
@ -30,7 +32,7 @@ import com.tangem.core.ui.message.SnackbarMessage
|
|||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isBackedUpForAnalytics
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
|
|
@ -47,7 +49,6 @@ import com.tangem.hot.sdk.TangemHotSdk
|
|||
import com.tangem.hot.sdk.android.create
|
||||
import com.tangem.sdk.api.BackupServiceHolder
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.analytics.events.Onboarding
|
||||
import com.tangem.tap.features.hot.TangemHotSDKProxy
|
||||
import com.tangem.tap.features.root.RootDetectedWarningComponent
|
||||
import com.tangem.tap.features.scanfails.ScanFailsComponent
|
||||
|
|
@ -352,7 +353,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
val unfinishedBackup = onboardingRepository.getUnfinishedFinalizeOnboarding() ?: return@launch
|
||||
cardRepository.finishCardActivation(unfinishedBackup.card.cardId)
|
||||
onboardingRepository.clearUnfinishedFinalizeOnboarding()
|
||||
analyticsEventHandler.send(Onboarding.Finished())
|
||||
analyticsEventHandler.send(OnboardingAnalyticsEvent.Onboarding.Finished())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -360,16 +361,12 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
val userWallets = userWalletsListRepository.userWalletsSync()
|
||||
val selectedWallet = userWalletsListRepository.selectedUserWalletSync() ?: return
|
||||
trackingContextProxy.addContext(selectedWallet)
|
||||
val isBackedUp = when (selectedWallet) {
|
||||
is UserWallet.Cold -> selectedWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> selectedWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = Basic.SignedIn.SignInType.NoSecurity,
|
||||
signInType = AnalyticsParam.SignInType.NoSecurity,
|
||||
walletsCount = userWallets.size,
|
||||
isImported = selectedWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = selectedWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -649,10 +649,7 @@ internal class ChildFactory @Inject constructor(
|
|||
is AppRoute.TangemPayDetails -> {
|
||||
createComponentChild(
|
||||
context = context,
|
||||
params = TangemPayDetailsContainerComponent.Params(
|
||||
userWalletId = route.userWalletId,
|
||||
config = route.config,
|
||||
),
|
||||
params = TangemPayDetailsContainerComponent.Params(initialStatus = route.status),
|
||||
componentFactory = tangemPayDetailsContainerComponentFactory,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue