package cash.z.ecc.ui.screen.restore.view import android.content.Context import android.view.inputmethod.InputMethodManager import androidx.compose.ui.test.assertIsFocused 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.performTextInput import androidx.test.core.app.ApplicationProvider import androidx.test.filters.MediumTest import cash.z.ecc.android.bip39.Mnemonics import cash.z.ecc.sdk.fixture.SeedPhraseFixture import cash.z.ecc.sdk.model.SeedPhrase import cash.z.ecc.ui.R import cash.z.ecc.ui.screen.common.CommonTag import cash.z.ecc.ui.screen.restore.RestoreTag import cash.z.ecc.ui.screen.restore.state.WordList import cash.z.ecc.ui.test.getStringResource import cash.z.ecc.ui.theme.ZcashTheme import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import java.util.Locale class RestoreViewTest { @get:Rule val composeTestRule = createComposeRule() @Test @MediumTest fun keyboard_appears_on_launch() { newTestSetup(emptyList()) composeTestRule.waitForIdle() composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.assertIsFocused() } val inputMethodManager = ApplicationProvider.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager assertTrue(inputMethodManager.isAcceptingText) } @Test @MediumTest fun autocomplete_suggestions_appear() { newTestSetup(emptyList()) composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.performTextInput("ab") // Make sure text isn't cleared it.assertTextContains("ab") } composeTestRule.onNode(hasText("abandon") and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)).also { it.assertExists() } composeTestRule.onNode(hasText("able") and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)).also { it.assertExists() } } @Test @MediumTest fun choose_autocomplete() { newTestSetup(emptyList()) composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.performTextInput("ab") } composeTestRule.onNode(hasText("abandon") and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)).also { it.performClick() } composeTestRule.onNodeWithTag(RestoreTag.AUTOCOMPLETE_LAYOUT).also { it.assertDoesNotExist() } composeTestRule.onNode(hasText("abandon") and hasTestTag(CommonTag.CHIP), useUnmergedTree = true).also { it.assertExists() } composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.assertTextEquals("") } } @Test @MediumTest fun type_full_word() { newTestSetup(emptyList()) composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.performTextInput("abandon") } composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.assertTextEquals("") } composeTestRule.onNodeWithTag(RestoreTag.AUTOCOMPLETE_LAYOUT).also { it.assertDoesNotExist() } composeTestRule.onNode(hasText("abandon") and hasTestTag(CommonTag.CHIP), useUnmergedTree = true).also { it.assertExists() } composeTestRule.onNodeWithTag(RestoreTag.SEED_WORD_TEXT_FIELD).also { it.assertTextEquals("") } } @Test @MediumTest fun invalid_phrase_does_not_progress() { newTestSetup(generateSequence { "abandon" }.take(SeedPhrase.SEED_PHRASE_SIZE).toList()) composeTestRule.onNodeWithText(getStringResource(R.string.restore_complete_header)).also { it.assertDoesNotExist() } } @Test @MediumTest fun finish_appears_after_24_words() { newTestSetup(SeedPhraseFixture.new().split) composeTestRule.onNodeWithText(getStringResource(R.string.restore_complete_header)).also { it.assertExists() } } @Test @MediumTest fun click_take_to_wallet() { val testSetup = newTestSetup(SeedPhraseFixture.new().split) assertEquals(0, testSetup.getOnFinishedCount()) composeTestRule.onNodeWithText(getStringResource(R.string.restore_button_see_wallet)).also { it.performClick() } assertEquals(1, testSetup.getOnFinishedCount()) } @Test @MediumTest fun back() { val testSetup = newTestSetup() assertEquals(0, testSetup.getOnBackCount()) composeTestRule.onNodeWithContentDescription(getStringResource(R.string.restore_back_content_description)).also { it.performClick() } assertEquals(1, testSetup.getOnBackCount()) } @Test @MediumTest fun clear() { newTestSetup(listOf("abandon")) composeTestRule.onNode(hasText("abandon") and hasTestTag(CommonTag.CHIP), useUnmergedTree = true).also { it.assertExists() } composeTestRule.onNodeWithText(getStringResource(R.string.restore_button_clear)).also { it.performClick() } composeTestRule.onNode(hasText("abandon") and hasTestTag(CommonTag.CHIP), useUnmergedTree = true).also { it.assertDoesNotExist() } } private fun newTestSetup(initialState: List = emptyList()) = TestSetup(composeTestRule, initialState) private class TestSetup(private val composeTestRule: ComposeContentTestRule, initialState: List) { private val state = WordList(initialState) private var onBackCount = 0 private var onFinishedCount = 0 fun getUserInputWords(): List { composeTestRule.waitForIdle() return state.current.value } fun getOnBackCount(): Int { composeTestRule.waitForIdle() return onBackCount } fun getOnFinishedCount(): Int { composeTestRule.waitForIdle() return onFinishedCount } init { composeTestRule.setContent { ZcashTheme { RestoreWallet( Mnemonics.getCachedWords(Locale.ENGLISH.language).toSortedSet(), state, onBack = { onBackCount++ }, paste = { "" }, onFinished = { onFinishedCount++ }, ) } } } } }