secant-android-wallet/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewTest.kt

410 lines
13 KiB
Kotlin
Raw Normal View History

package co.electriccoin.zcash.ui.screen.restore.view
2021-12-09 12:21:30 -08:00
2023-03-21 12:04:16 -07:00
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
2021-12-09 12:21:30 -08:00
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
2021-12-09 12:21:30 -08:00
import androidx.compose.ui.test.performTextInput
import androidx.test.filters.MediumTest
import cash.z.ecc.android.bip39.Mnemonics
2023-03-21 12:04:16 -07:00
import cash.z.ecc.android.sdk.model.BlockHeight
2023-02-17 03:05:23 -08:00
import cash.z.ecc.android.sdk.model.SeedPhrase
2023-03-21 12:04:16 -07:00
import cash.z.ecc.android.sdk.model.ZcashNetwork
2021-12-09 12:21:30 -08:00
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.CommonTag
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.restore.RestoreTag
2023-03-21 12:04:16 -07:00
import co.electriccoin.zcash.ui.screen.restore.model.RestoreStage
import co.electriccoin.zcash.ui.screen.restore.state.RestoreState
import co.electriccoin.zcash.ui.screen.restore.state.WordList
import co.electriccoin.zcash.ui.test.getStringResource
import kotlinx.collections.immutable.toPersistentSet
2023-03-21 12:04:16 -07:00
import kotlinx.coroutines.flow.MutableStateFlow
2021-12-09 12:21:30 -08:00
import org.junit.Assert.assertEquals
2023-03-22 12:05:19 -07:00
import org.junit.Before
2021-12-09 12:21:30 -08:00
import org.junit.Rule
import org.junit.Test
import java.util.Locale
import java.util.concurrent.atomic.AtomicInteger
2023-03-21 12:04:16 -07:00
import kotlin.test.assertNull
2021-12-09 12:21:30 -08:00
class RestoreViewTest : UiTestPrerequisites() {
2021-12-09 12:21:30 -08:00
@get:Rule
val composeTestRule = createComposeRule()
2023-03-22 12:05:19 -07:00
@Before
fun setup() {
composeTestRule.mainClock.autoAdvance = true
}
2021-12-09 12:21:30 -08:00
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_autocomplete_suggestions_appear() {
newTestSetup()
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also {
it.performTextInput("ab")
// Make sure text isn't cleared
it.assertTextContains("ab")
}
composeTestRule.onNode(
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
).also {
2021-12-09 12:21:30 -08:00
it.assertExists()
}
composeTestRule.onNode(
matcher = hasText("able", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
).also {
2021-12-09 12:21:30 -08:00
it.assertExists()
}
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_choose_autocomplete() {
newTestSetup()
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also {
it.performTextInput("ab")
}
composeTestRule.onNode(
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
).also {
2021-12-09 12:21:30 -08:00
it.performClick()
}
composeTestRule.onNodeWithTag(RestoreTag.AUTOCOMPLETE_LAYOUT).also {
it.assertDoesNotExist()
}
composeTestRule.onNode(matcher = hasText("abandon", substring = true)).also {
2021-12-09 12:21:30 -08:00
it.assertExists()
}
composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also {
it.assertTextEquals("abandon ", includeEditableText = true)
2021-12-09 12:21:30 -08:00
}
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_type_full_word() {
newTestSetup()
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also {
it.performTextInput("abandon")
}
composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also {
it.assertTextEquals("abandon ", includeEditableText = true)
2021-12-09 12:21:30 -08:00
}
composeTestRule.onNodeWithTag(RestoreTag.AUTOCOMPLETE_LAYOUT).also {
it.assertDoesNotExist()
}
composeTestRule.onNode(matcher = hasText(text = "abandon", substring = true))
.also {
it.assertExists()
}
2021-12-09 12:21:30 -08:00
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_invalid_phrase_does_not_progress() {
newTestSetup(initialWordsList = generateSequence { "abandon" }.take(SeedPhrase.SEED_PHRASE_SIZE).toList())
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_seed_button_next),
ignoreCase = true
).also {
2023-03-21 12:04:16 -07:00
it.assertIsNotEnabled()
2021-12-09 12:21:30 -08:00
}
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_finish_appears_after_24_words() {
2023-03-22 12:05:19 -07:00
// There appears to be a bug introduced in Compose 1.4.0 which makes this necessary
composeTestRule.mainClock.autoAdvance = false
2023-03-21 12:04:16 -07:00
newTestSetup(initialWordsList = SeedPhraseFixture.new().split)
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_seed_button_next),
ignoreCase = true
).also {
2021-12-09 12:21:30 -08:00
it.assertExists()
}
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun seed_clear() {
newTestSetup(initialWordsList = listOf("abandon"))
composeTestRule.onNode(
matcher = hasText(text = "abandon", substring = true),
useUnmergedTree = true
).also {
2023-03-21 12:04:16 -07:00
it.assertExists()
}
composeTestRule.onNodeWithText(getStringResource(R.string.restore_button_clear)).also {
it.performClick()
}
composeTestRule.onNode(
matcher = hasText("abandon", substring = true) and hasTestTag(CommonTag.CHIP),
useUnmergedTree = true
).also {
2023-03-21 12:04:16 -07:00
it.assertDoesNotExist()
}
}
@Test
@MediumTest
fun height_skip() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
initialWordsList = SeedPhraseFixture.new().split
)
2023-03-21 12:04:16 -07:00
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
it.performScrollTo()
it.assertIsEnabled()
2023-03-21 12:04:16 -07:00
it.performClick()
}
assertEquals(testSetup.getRestoreHeight(), null)
assertEquals(1, testSetup.getOnFinishedCount())
2023-03-21 12:04:16 -07:00
}
@Test
@MediumTest
fun height_set_valid() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
initialWordsList = SeedPhraseFixture.new().split
)
composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also {
2023-03-21 12:04:16 -07:00
it.performTextInput(ZcashNetwork.Mainnet.saplingActivationHeight.value.toString())
}
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
2023-03-21 12:04:16 -07:00
it.assertIsEnabled()
it.performClick()
}
assertEquals(testSetup.getRestoreHeight(), ZcashNetwork.Mainnet.saplingActivationHeight)
assertEquals(1, testSetup.getOnFinishedCount())
2023-03-21 12:04:16 -07:00
}
@Test
@MediumTest
fun height_set_invalid_too_small() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
initialWordsList = SeedPhraseFixture.new().split
)
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
it.assertIsEnabled()
2023-03-21 12:04:16 -07:00
}
composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also {
2023-03-21 12:04:16 -07:00
it.performTextInput((ZcashNetwork.Mainnet.saplingActivationHeight.value - 1L).toString())
}
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
2023-03-21 12:04:16 -07:00
it.assertIsNotEnabled()
it.performClick()
}
assertNull(testSetup.getRestoreHeight())
assertEquals(0, testSetup.getOnFinishedCount())
2023-03-21 12:04:16 -07:00
}
@Test
@MediumTest
fun height_set_invalid_non_digit() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
initialWordsList = SeedPhraseFixture.new().split
)
composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also {
2023-03-21 12:04:16 -07:00
it.performTextInput("1.2")
}
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
2023-03-21 12:04:16 -07:00
it.assertIsNotEnabled()
it.performClick()
}
assertNull(testSetup.getRestoreHeight())
assertEquals(0, testSetup.getOnFinishedCount())
2023-03-21 12:04:16 -07:00
}
@Test
@MediumTest
fun complete_click_take_to_wallet() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
2023-03-21 12:04:16 -07:00
initialWordsList = SeedPhraseFixture.new().split
)
2021-12-09 12:21:30 -08:00
assertEquals(0, testSetup.getOnFinishedCount())
composeTestRule.onNodeWithText(
text = getStringResource(R.string.restore_birthday_button_restore),
ignoreCase = true
).also {
2021-12-09 12:21:30 -08:00
it.performClick()
}
assertEquals(1, testSetup.getOnFinishedCount())
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun back_from_seed() {
2021-12-09 12:21:30 -08:00
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnBackCount())
composeTestRule.onNodeWithContentDescription(
getStringResource(R.string.restore_back_content_description)
).also {
2021-12-09 12:21:30 -08:00
it.performClick()
}
assertEquals(1, testSetup.getOnBackCount())
}
@Test
@MediumTest
2023-03-21 12:04:16 -07:00
fun back_from_birthday() {
val testSetup = newTestSetup(
initialStage = RestoreStage.Birthday,
initialWordsList = SeedPhraseFixture.new().split
)
2021-12-09 12:21:30 -08:00
2023-03-21 12:04:16 -07:00
assertEquals(0, testSetup.getOnBackCount())
2021-12-09 12:21:30 -08:00
composeTestRule.onNodeWithContentDescription(
getStringResource(R.string.restore_back_content_description)
).also {
2021-12-09 12:21:30 -08:00
it.performClick()
}
2023-03-22 12:05:19 -07:00
// There appears to be a bug introduced in Compose 1.4.0 which makes this necessary
composeTestRule.mainClock.autoAdvance = false
2023-03-21 12:04:16 -07:00
assertEquals(testSetup.getStage(), RestoreStage.Seed)
assertEquals(0, testSetup.getOnBackCount())
}
private fun newTestSetup(
initialStage: RestoreStage = RestoreStage.Seed,
initialWordsList: List<String> = emptyList()
) = TestSetup(composeTestRule, initialStage, initialWordsList)
internal class TestSetup(
private val composeTestRule: ComposeContentTestRule,
initialStage: RestoreStage,
initialWordsList: List<String>
) {
private val state = RestoreState(initialStage)
2021-12-09 12:21:30 -08:00
2023-03-21 12:04:16 -07:00
private val wordList = WordList(initialWordsList)
2021-12-09 12:21:30 -08:00
private val onBackCount = AtomicInteger(0)
2021-12-09 12:21:30 -08:00
private val onFinishedCount = AtomicInteger(0)
2021-12-09 12:21:30 -08:00
2023-03-21 12:04:16 -07:00
private val restoreHeight = MutableStateFlow<BlockHeight?>(null)
2021-12-09 12:21:30 -08:00
fun getUserInputWords(): List<String> {
2023-03-21 12:04:16 -07:00
composeTestRule.waitForIdle()
return wordList.current.value
}
fun getStage(): RestoreStage {
2021-12-09 12:21:30 -08:00
composeTestRule.waitForIdle()
return state.current.value
}
2023-03-21 12:04:16 -07:00
fun getRestoreHeight(): BlockHeight? {
composeTestRule.waitForIdle()
return restoreHeight.value
}
2021-12-09 12:21:30 -08:00
fun getOnBackCount(): Int {
composeTestRule.waitForIdle()
return onBackCount.get()
2021-12-09 12:21:30 -08:00
}
fun getOnFinishedCount(): Int {
composeTestRule.waitForIdle()
return onFinishedCount.get()
2021-12-09 12:21:30 -08:00
}
init {
composeTestRule.setContent {
ZcashTheme {
RestoreWallet(
2023-03-21 12:04:16 -07:00
ZcashNetwork.Mainnet,
2021-12-09 12:21:30 -08:00
state,
2023-03-21 12:04:16 -07:00
Mnemonics.getCachedWords(Locale.ENGLISH.language).toPersistentSet(),
wordList,
restoreHeight = restoreHeight.collectAsState().value,
setRestoreHeight = {
restoreHeight.value = it
},
2021-12-09 12:21:30 -08:00
onBack = {
onBackCount.incrementAndGet()
2021-12-09 12:21:30 -08:00
},
paste = { "" },
onFinished = {
onFinishedCount.incrementAndGet()
2022-06-22 02:48:19 -07:00
}
2021-12-09 12:21:30 -08:00
)
}
}
}
}
}