From a01ec0e609486d8e32405ceb6612fbda91270306 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sat, 2 Dec 2023 19:00:40 +0900 Subject: [PATCH 1/9] :sparkles: Add AccountSettingsScreenTest --- .../speechbuddy/AccountSettingsScreenTest.kt | 117 ++++++++++++++++++ .../speechbuddy/DisplaySettingsScreenTest.kt | 4 + .../example/speechbuddy/LoginScreenTest.kt | 5 +- .../speechbuddy/TextToSpeechScreenTest.kt | 2 +- .../com/example/speechbuddy/BaseActivity.kt | 4 + .../com/example/speechbuddy/HomeActivity.kt | 3 +- .../speechbuddy/repository/UserRepository.kt | 16 ++- .../viewmodel/AccountSettingsViewModel.kt | 1 + 8 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt new file mode 100644 index 00000000..04d291e0 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt @@ -0,0 +1,117 @@ +package com.example.speechbuddy + +import android.content.Context +import android.content.Intent +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.SemanticsNodeInteraction +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.captureToImage +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.example.speechbuddy.compose.settings.AccountSettings +import com.example.speechbuddy.ui.SpeechBuddyTheme +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +class AccountSettingsScreenTest { + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeTestRule = createAndroidIntentComposeRule { + Intent(it, HomeActivity::class.java).apply { + putExtra("isTest", true) + } + } + + @Before + fun setUp() { + hiltRule.inject() + val fakeId = 1 + val fakeEmail = "email@email.com" + val fakeNickname = "nickname" + composeTestRule.activity.sessionManager.setUserId(fakeId) + composeTestRule.activity.userRepository.setMyInfo(fakeId, fakeEmail, fakeNickname) + composeTestRule.activity.setContent { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { + AccountSettings( + paddingValues = PaddingValues() + ) + } + } + } + + @Test + fun should_display_all_elements_when_account_settings_screen_appears() { + + composeTestRule.onNodeWithText(ACCOUNT).assertIsDisplayed() + composeTestRule.onNodeWithText(EMAIL).assertIsDisplayed() + composeTestRule.onNodeWithText(NICKNAME).assertIsDisplayed() + composeTestRule.onNodeWithText(LOGOUT).assertIsDisplayed().assertHasClickAction().assertIsEnabled() + composeTestRule.onNodeWithText(WITHDRAW).assertIsDisplayed().assertHasClickAction().assertIsEnabled() + } + + companion object { + const val ACCOUNT = "계정" + const val EMAIL = "이메일" + const val NICKNAME = "닉네임" + const val LOGOUT = "로그아웃" + const val WITHDRAW = "회원탈퇴" + + val PRIMARY_COLOR = Color(0xFF0D6D35) + val SECONDARY_CONTAINER_COLOR = Color(0xFFD3E8D2) + + const val USER_EMAIL = "test@test.com" + const val USER_NICKNAME = "test" + + fun SemanticsNodeInteraction.assertBackgroundColor(expectedBackground: Color) { + val capturedName = captureToImage().colorSpace.name + Assert.assertEquals(expectedBackground.colorSpace.name, capturedName) + } + } + + /** + * Factory method to provide Android specific implementation of createComposeRule, for a given + * activity class type A that needs to be launched via an intent. + * + * @param intentFactory A lambda that provides a Context that can used to create an intent. A intent needs to be returned. + */ + inline fun createAndroidIntentComposeRule(intentFactory: (context: Context) -> Intent) : AndroidComposeTestRule, A> { + val context = ApplicationProvider.getApplicationContext() + val intent = intentFactory(context) + + return AndroidComposeTestRule( + activityRule = ActivityScenarioRule(intent), + activityProvider = { scenarioRule -> scenarioRule.getActivity() } + ) + } + + /** + * Gets the activity from a scenarioRule. + * + * https://androidx.tech/artifacts/compose.ui/ui-test-junit4/1.0.0-alpha11-source/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt.html + */ + fun ActivityScenarioRule.getActivity(): A { + var activity: A? = null + + scenario.onActivity { activity = it } + + return activity ?: throw IllegalStateException("Activity was not set in the ActivityScenarioRule!") + } +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt new file mode 100644 index 00000000..894f49e2 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt @@ -0,0 +1,4 @@ +package com.example.speechbuddy + +class DisplaySettingsScreenTest { +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/LoginScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/LoginScreenTest.kt index 16ee7158..47790675 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/LoginScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/LoginScreenTest.kt @@ -34,7 +34,10 @@ class LoginScreenTest { fun setUp() { hiltRule.inject() composeTestRule.activity.setContent { - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { LoginScreen( onResetPasswordClick = {}, onSignupClick = {} diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt index 14ee7187..3bd3f75b 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt @@ -35,7 +35,7 @@ class TextToSpeechScreenTest { hiltRule.inject() SpeechBuddyTheme { TextToSpeechScreen( - bottomPaddingValues = PaddingValues(16.dp) + paddingValues = PaddingValues() ) } } diff --git a/frontend/app/src/main/java/com/example/speechbuddy/BaseActivity.kt b/frontend/app/src/main/java/com/example/speechbuddy/BaseActivity.kt index 35fc7dfc..640780f9 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/BaseActivity.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/BaseActivity.kt @@ -3,6 +3,7 @@ package com.example.speechbuddy import androidx.appcompat.app.AppCompatActivity import com.example.speechbuddy.domain.SessionManager import com.example.speechbuddy.repository.SettingsRepository +import com.example.speechbuddy.repository.UserRepository import javax.inject.Inject abstract class BaseActivity : AppCompatActivity() { @@ -13,4 +14,7 @@ abstract class BaseActivity : AppCompatActivity() { @Inject lateinit var settingsRepository: SettingsRepository + @Inject + lateinit var userRepository: UserRepository + } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt b/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt index ddb01224..41cf0d11 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt @@ -21,7 +21,6 @@ class HomeActivity : BaseActivity() { @RequiresApi(Build.VERSION_CODES.O) override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) // force the database worker to build a new db // in order to check if the weight-db is empty or not and fill it @@ -41,7 +40,7 @@ class HomeActivity : BaseActivity() { private fun subscribeObservers() { sessionManager.isAuthorized.observe(this) { isAuthorized -> - if (!isAuthorized) navAuthActivity() + if (!isAuthorized && !intent.getBooleanExtra("isTest", false)) navAuthActivity() } } diff --git a/frontend/app/src/main/java/com/example/speechbuddy/repository/UserRepository.kt b/frontend/app/src/main/java/com/example/speechbuddy/repository/UserRepository.kt index 7cc050ba..70960643 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/repository/UserRepository.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/repository/UserRepository.kt @@ -2,6 +2,7 @@ package com.example.speechbuddy.repository import com.example.speechbuddy.data.local.UserDao import com.example.speechbuddy.data.local.UserIdPrefsManager +import com.example.speechbuddy.data.local.models.UserEntity import com.example.speechbuddy.data.local.models.UserMapper import com.example.speechbuddy.data.remote.UserRemoteSource import com.example.speechbuddy.data.remote.models.UserDtoMapper @@ -32,8 +33,12 @@ class UserRepository @Inject constructor( fun getMyInfo(): Flow> { return userDao.getUserById(sessionManager.userId.value!!).map { userEntity -> - if (userEntity != null) Resource.success(userMapper.mapToDomainModel(userEntity)) - else Resource.error("Unable to find user", null) + if (userEntity != null) { + Resource.success(userMapper.mapToDomainModel(userEntity)) + } + else { + Resource.error("Unable to find user", null) + } } } @@ -77,4 +82,11 @@ class UserRepository @Inject constructor( ) } + fun setMyInfo(id: Int, email: String, nickname: String) { + CoroutineScope(Dispatchers.IO).launch { + userIdPrefsManager.saveUserId(id) + userDao.insertUser(UserEntity(id, email, nickname)) + } + } + } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt index 63dd23d3..f10ec269 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt @@ -5,6 +5,7 @@ import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.speechbuddy.domain.SessionManager +import com.example.speechbuddy.domain.models.User import com.example.speechbuddy.repository.AuthRepository import com.example.speechbuddy.repository.SettingsRepository import com.example.speechbuddy.repository.SymbolRepository From 9f3ba62e06894d22203e8b5edf76edfc9b20cb75 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sat, 2 Dec 2023 21:05:06 +0900 Subject: [PATCH 2/9] :sparkles: Add AndroidTestUtil --- .../speechbuddy/AccountSettingsScreenTest.kt | 186 +++++++++++++----- .../example/speechbuddy/AndroidTestUtil.kt | 39 ++++ .../speechbuddy/DisplaySettingsScreenTest.kt | 65 ++++++ .../EmailVerificationScreenForResetPWTest.kt | 5 +- .../EmailVerificationScreenForSignupTest.kt | 5 +- .../example/speechbuddy/LandingScreenTest.kt | 5 +- .../speechbuddy/ResetPasswordScreenTest.kt | 5 +- .../example/speechbuddy/SignupScreenTest.kt | 5 +- .../speechbuddy/TextToSpeechScreenTest.kt | 5 +- .../java/com/example/speechbuddy/ui/Theme.kt | 74 +++---- .../viewmodel/AccountSettingsViewModel.kt | 1 - 11 files changed, 290 insertions(+), 105 deletions(-) create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/AndroidTestUtil.kt diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt index 04d291e0..0fc89ed9 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt @@ -1,25 +1,20 @@ package com.example.speechbuddy -import android.content.Context import android.content.Intent -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.captureToImage -import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.rules.ActivityScenarioRule +import androidx.compose.ui.test.performClick import com.example.speechbuddy.compose.settings.AccountSettings import com.example.speechbuddy.ui.SpeechBuddyTheme import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import org.junit.Assert +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -27,11 +22,13 @@ import org.junit.Test @HiltAndroidTest class AccountSettingsScreenTest { + private val androidTestUtil = AndroidTestUtil() + @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) - val composeTestRule = createAndroidIntentComposeRule { + val composeTestRule = androidTestUtil.createAndroidIntentComposeRule { Intent(it, HomeActivity::class.java).apply { putExtra("isTest", true) } @@ -59,59 +56,152 @@ class AccountSettingsScreenTest { @Test fun should_display_all_elements_when_account_settings_screen_appears() { - composeTestRule.onNodeWithText(ACCOUNT).assertIsDisplayed() composeTestRule.onNodeWithText(EMAIL).assertIsDisplayed() composeTestRule.onNodeWithText(NICKNAME).assertIsDisplayed() composeTestRule.onNodeWithText(LOGOUT).assertIsDisplayed().assertHasClickAction().assertIsEnabled() composeTestRule.onNodeWithText(WITHDRAW).assertIsDisplayed().assertHasClickAction().assertIsEnabled() + composeTestRule.onNodeWithText(FAKE_EMAIL).assertIsDisplayed() + composeTestRule.onNodeWithText(FAKE_NICKNAME).assertIsDisplayed() } - companion object { - const val ACCOUNT = "계정" - const val EMAIL = "이메일" - const val NICKNAME = "닉네임" - const val LOGOUT = "로그아웃" - const val WITHDRAW = "회원탈퇴" + @Test + fun should_display_backup_dialog_when_clicking_backup_button() { + composeTestRule.onAllNodesWithText(LOGOUT)[0].performClick() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onNodeWithText(WITHDRAW).assertIsNotEnabled() + composeTestRule.onNodeWithText(BACKUP).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(BACKUP_DIALOG).assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertIsDisplayed().assertHasClickAction() + } - val PRIMARY_COLOR = Color(0xFF0D6D35) - val SECONDARY_CONTAINER_COLOR = Color(0xFFD3E8D2) + @Test + fun should_display_loading_indicator_when_clicking_backup_button_in_backup_dialog() { + composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.onNodeWithText(BACKUP).performClick() + composeTestRule.onNodeWithText(BACKUP_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(BACKUP).assertDoesNotExist() + composeTestRule.onNodeWithText(LOGOUT).assertIsNotEnabled() + composeTestRule.onNodeWithText(WITHDRAW).assertIsNotEnabled() + } - const val USER_EMAIL = "test@test.com" - const val USER_NICKNAME = "test" + @Test + fun should_display_logout_dialog_when_clicking_logout_button_in_backup_dialog() { + composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed() + composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onNodeWithText(WITHDRAW).assertIsNotEnabled() + } - fun SemanticsNodeInteraction.assertBackgroundColor(expectedBackground: Color) { - val capturedName = captureToImage().colorSpace.name - Assert.assertEquals(expectedBackground.colorSpace.name, capturedName) - } + @Test + fun should_display_loading_indicator_when_clicking_logout_button_in_logout_dialog() { + composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() } - /** - * Factory method to provide Android specific implementation of createComposeRule, for a given - * activity class type A that needs to be launched via an intent. - * - * @param intentFactory A lambda that provides a Context that can used to create an intent. A intent needs to be returned. - */ - inline fun createAndroidIntentComposeRule(intentFactory: (context: Context) -> Intent) : AndroidComposeTestRule, A> { - val context = ApplicationProvider.getApplicationContext() - val intent = intentFactory(context) - - return AndroidComposeTestRule( - activityRule = ActivityScenarioRule(intent), - activityProvider = { scenarioRule -> scenarioRule.getActivity() } - ) + @Test + fun should_display_account_settings_screen_when_clicking_cancel_button_in_logout_dialog() { + composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() } - /** - * Gets the activity from a scenarioRule. - * - * https://androidx.tech/artifacts/compose.ui/ui-test-junit4/1.0.0-alpha11-source/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt.html - */ - fun ActivityScenarioRule.getActivity(): A { - var activity: A? = null + @Test + fun should_display_first_withdraw_dialog_when_clicking_withdraw_button() { + composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.onNodeWithText(FIRST_WITHDRAW_DIALOG).assertIsDisplayed() + composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(PROCEED).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(LOGOUT).assertIsNotEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[1].assertIsEnabled() + } - scenario.onActivity { activity = it } + @Test + fun should_display_second_withdraw_dialog_when_clicking_progress_button_in_first_withdraw_dialog() { + composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertIsDisplayed() + composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(LOGOUT).assertIsNotEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(WITHDRAW)[2].assertHasClickAction().assertIsEnabled() + } + + @Test + fun should_display_account_settings_screen_when_clicking_cancel_button_in_first_withdraw_dialog() { + composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.onNodeWithText(FIRST_WITHDRAW_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() + composeTestRule.onNodeWithText(LOGOUT).assertIsEnabled() + composeTestRule.onNodeWithText(WITHDRAW).assertIsEnabled() + } + + @Test + fun should_display_loading_indicator_when_clicking_withdraw_button_in_second_withdraw_dialog() { + composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.onAllNodesWithText(WITHDRAW)[0].assertIsNotEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[1].assertIsDisplayed() + composeTestRule.onAllNodesWithText(WITHDRAW)[2].assertHasClickAction().assertIsEnabled() + composeTestRule.onAllNodesWithText(WITHDRAW)[2].performClick() + composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() + } + + @Test + fun should_display_account_settings_screen_when_clicking_cancel_button_in_second_withdraw_dialog() { + composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertDoesNotExist() + composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() + } - return activity ?: throw IllegalStateException("Activity was not set in the ActivityScenarioRule!") + @After + fun tearDown() { + composeTestRule.activityRule.scenario.close() } + + companion object { + const val ACCOUNT = "계정" + const val EMAIL = "이메일" + const val NICKNAME = "닉네임" + const val LOGOUT = "로그아웃" + const val WITHDRAW = "회원탈퇴" + const val FAKE_EMAIL = "email@email.com" + const val FAKE_NICKNAME = "nickname" + const val BACKUP = "백업" + const val BACKUP_DIALOG = "로그아웃하기 전 백업하시겠습니까? 변경사항을 백업하지 않으면 이용 기록이 손실될 수 있습니다." + const val LOGOUT_DIALOG = "정말로 로그아웃하시겠습니까?" + const val CANCEL = "취소" + const val PROCEED = "진행" + const val FIRST_WITHDRAW_DIALOG = "회원탈퇴를 하면 지금까지의 이용 기록이 모두 사라지며 복구할 수 없습니다. SpeechBuddy를 다시 이용하시려면 게스트 모드를 이용하거나 회원가입을 새로 해야 합니다. 정말로 탈퇴하시겠습니까?" + const val SECOND_WITHDRAW_DIALOG = "정말로 탈퇴하시겠습니까? 이 동작은 취소할 수 없습니다." + } + } \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/AndroidTestUtil.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/AndroidTestUtil.kt new file mode 100644 index 00000000..f9741f24 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/AndroidTestUtil.kt @@ -0,0 +1,39 @@ +package com.example.speechbuddy + +import android.content.Context +import android.content.Intent +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.rules.ActivityScenarioRule + +class AndroidTestUtil { + /** + * Factory method to provide Android specific implementation of createComposeRule, for a given + * activity class type A that needs to be launched via an intent. + * + * @param intentFactory A lambda that provides a Context that can used to create an intent. A intent needs to be returned. + */ + inline fun createAndroidIntentComposeRule(intentFactory: (context: Context) -> Intent) : AndroidComposeTestRule, A> { + val context = ApplicationProvider.getApplicationContext() + val intent = intentFactory(context) + + return AndroidComposeTestRule( + activityRule = ActivityScenarioRule(intent), + activityProvider = { scenarioRule -> scenarioRule.getActivity() } + ) + } + + /** + * Gets the activity from a scenarioRule. + * + * https://androidx.tech/artifacts/compose.ui/ui-test-junit4/1.0.0-alpha11-source/androidx/compose/ui/test/junit4/AndroidComposeTestRule.kt.html + */ + fun ActivityScenarioRule.getActivity(): A { + var activity: A? = null + + scenario.onActivity { activity = it } + + return activity ?: throw IllegalStateException("Activity was not set in the ActivityScenarioRule!") + } +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt index 894f49e2..18ed24aa 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt @@ -1,4 +1,69 @@ package com.example.speechbuddy +import android.content.Intent +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import com.example.speechbuddy.compose.settings.DisplaySettings +import com.example.speechbuddy.ui.SpeechBuddyTheme +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest class DisplaySettingsScreenTest { + + private val androidTestUtil = AndroidTestUtil() + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeTestRule = androidTestUtil.createAndroidIntentComposeRule { + Intent(it, HomeActivity::class.java).apply { + putExtra("isTest", true) + } + } + + @Before + fun setUp() { + hiltRule.inject() + composeTestRule.activity.setContent { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { + DisplaySettings( + paddingValues = PaddingValues() + ) + } + } + } + + @Test + fun should_display_all_elements_when_account_settings_screen_appears() { + composeTestRule.onNodeWithText(DISPLAY).assertIsDisplayed() + composeTestRule.onNodeWithText(DARK_MODE).assertIsDisplayed() + composeTestRule.onNodeWithText(INITIAL_PAGE).assertIsDisplayed() + composeTestRule.onNodeWithText(SYMBOL).assertIsDisplayed() + composeTestRule.onNodeWithText(TTS).assertIsDisplayed() + } + + @Test + fun should_change_dark_mode_when_clicking_dark_mode() { + composeTestRule.onNodeWithTag("SwitchTag").performClick() + } + + companion object { + const val DISPLAY = "디스플레이 설정" + const val DARK_MODE = "다크 모드" + const val INITIAL_PAGE = "시작 페이지" + const val SYMBOL = "상징으로 말하기" + const val TTS = "음성으로 말하기" + } } \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForResetPWTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForResetPWTest.kt index 5393c639..2ebc0d62 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForResetPWTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForResetPWTest.kt @@ -31,7 +31,10 @@ class EmailVerificationScreenForResetPWTest { hiltRule.inject() composeTestRule.activity.setContent { val fakeSource = "reset_password" - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { EmailVerificationScreen( source = fakeSource, navigateCallback = {} diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForSignupTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForSignupTest.kt index a543e2c8..2e943bbe 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForSignupTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/EmailVerificationScreenForSignupTest.kt @@ -31,7 +31,10 @@ class EmailVerificationScreenForSignupTest { hiltRule.inject() composeTestRule.activity.setContent { val fakeSource = "signup" - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { EmailVerificationScreen( source = fakeSource, navigateCallback = {} diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/LandingScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/LandingScreenTest.kt index 5865f1e8..567b65d6 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/LandingScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/LandingScreenTest.kt @@ -26,7 +26,10 @@ class LandingScreenTest { fun setUp() { hiltRule.inject() composeTestRule.activity.setContent { - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { LandingScreen( onLoginClick = {}, isBackup = false) } } diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/ResetPasswordScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/ResetPasswordScreenTest.kt index 42a9b8e8..a077a537 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/ResetPasswordScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/ResetPasswordScreenTest.kt @@ -29,7 +29,10 @@ class ResetPasswordScreenTest { fun setUp() { hiltRule.inject() composeTestRule.activity.setContent { - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { ResetPasswordScreen( navigateToLogin = {} ) diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/SignupScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/SignupScreenTest.kt index 8b44608c..0cde1c25 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/SignupScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/SignupScreenTest.kt @@ -31,7 +31,10 @@ class SignupScreenTest { hiltRule.inject() composeTestRule.activity.setContent { val fakeEmail = "test@example.com" - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { SignupScreen( email = fakeEmail, navigateToLogin = {} diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt index 3bd3f75b..3f877e97 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/TextToSpeechScreenTest.kt @@ -33,7 +33,10 @@ class TextToSpeechScreenTest { fun setUp() { composeTestRule.activity.setContent { hiltRule.inject() - SpeechBuddyTheme { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { TextToSpeechScreen( paddingValues = PaddingValues() ) diff --git a/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt b/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt index dd631673..eecc2648 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt @@ -84,65 +84,39 @@ private val _darkColorScheme = darkColorScheme( @Composable fun SpeechBuddyTheme( - settingsRepository: SettingsRepository? = null, - initialDarkMode: Boolean? = false, + settingsRepository: SettingsRepository, + initialDarkMode: Boolean, darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = false, content: @Composable () -> Unit ) { - if (settingsRepository != null) { - val darkModeState by settingsRepository.getDarkModeForChange() - .collectAsState(initialDarkMode) + val darkModeState by settingsRepository.getDarkModeForChange() + .collectAsState(initialDarkMode) - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkModeState == true -> _darkColorScheme - else -> _lightColorScheme - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = - darkTheme - } + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) - } else { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - else -> _lightColorScheme - } - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() - WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = - darkTheme - } + darkModeState == true -> _darkColorScheme + else -> _lightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = + darkTheme } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) } diff --git a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt index f10ec269..63dd23d3 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/AccountSettingsViewModel.kt @@ -5,7 +5,6 @@ import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.speechbuddy.domain.SessionManager -import com.example.speechbuddy.domain.models.User import com.example.speechbuddy.repository.AuthRepository import com.example.speechbuddy.repository.SettingsRepository import com.example.speechbuddy.repository.SymbolRepository From 050cfa930f72fcc9241c9af8ed11b3a15721be72 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sat, 2 Dec 2023 21:07:04 +0900 Subject: [PATCH 3/9] :fire: Remove redundant expression --- frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt b/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt index eecc2648..0943450e 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/ui/Theme.kt @@ -100,7 +100,7 @@ fun SpeechBuddyTheme( if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } - darkModeState == true -> _darkColorScheme + darkModeState -> _darkColorScheme else -> _lightColorScheme } val view = LocalView.current From d16e3c0cd190ca154a929f6c1c484546d34539e1 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sat, 2 Dec 2023 21:21:04 +0900 Subject: [PATCH 4/9] :white_check_mark: Implement DisplaySettingsScreenTest --- .../speechbuddy/DisplaySettingsScreenTest.kt | 35 ++++++++++++++++++- .../compose/settings/DisplaySettings.kt | 7 ++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt index 18ed24aa..806f0a3b 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt @@ -3,14 +3,21 @@ package com.example.speechbuddy import android.content.Intent import androidx.activity.compose.setContent import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.graphics.Color import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotSelected +import androidx.compose.ui.test.assertIsOff +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import com.example.speechbuddy.LoginScreenTest.Companion.assertBackgroundColor import com.example.speechbuddy.compose.settings.DisplaySettings import com.example.speechbuddy.ui.SpeechBuddyTheme import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test @@ -56,7 +63,30 @@ class DisplaySettingsScreenTest { @Test fun should_change_dark_mode_when_clicking_dark_mode() { - composeTestRule.onNodeWithTag("SwitchTag").performClick() + composeTestRule.onNodeWithTag("dark_mode").assertIsOff() + composeTestRule.onNodeWithTag("dark_mode").performClick() + composeTestRule.onNodeWithTag("dark_mode").assertIsOn() + composeTestRule.onNodeWithText(DISPLAY).assertBackgroundColor(DARK_COLOR) + composeTestRule.onNodeWithTag("dark_mode").performClick() + composeTestRule.onNodeWithTag("dark_mode").assertIsOff() + composeTestRule.onNodeWithText(DISPLAY).assertBackgroundColor(LIGHT_COLOR) + } + + @Test + fun should_change_initial_page_when_clicking_initial_page() { + composeTestRule.onNodeWithTag("initial_page_symbol").assertIsSelected() + composeTestRule.onNodeWithTag("initial_page_tts").assertIsNotSelected() + composeTestRule.onNodeWithTag("initial_page_tts").performClick() + composeTestRule.onNodeWithTag("initial_page_symbol").assertIsNotSelected() + composeTestRule.onNodeWithTag("initial_page_tts").assertIsSelected() + composeTestRule.onNodeWithTag("initial_page_symbol").performClick() + composeTestRule.onNodeWithTag("initial_page_symbol").assertIsSelected() + composeTestRule.onNodeWithTag("initial_page_tts").assertIsNotSelected() + } + + @After + fun tearDown() { + composeTestRule.activityRule.scenario.close() } companion object { @@ -65,5 +95,8 @@ class DisplaySettingsScreenTest { const val INITIAL_PAGE = "시작 페이지" const val SYMBOL = "상징으로 말하기" const val TTS = "음성으로 말하기" + + val LIGHT_COLOR = Color(0xFF000000) + val DARK_COLOR = Color(0xFFFFFFFF) } } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/DisplaySettings.kt b/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/DisplaySettings.kt index def6abee..ef3dd848 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/DisplaySettings.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/DisplaySettings.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -61,7 +62,7 @@ fun DisplaySettings( Switch( checked = uiState.isDarkModeEnabled, onCheckedChange = { viewModel.setDarkMode(it) }, - modifier = Modifier.heightIn(max = 32.dp) + modifier = Modifier.heightIn(max = 32.dp).testTag("dark_mode") ) } ) @@ -101,7 +102,7 @@ fun InitialPageColumn( RadioButton( selected = initialPage == InitialPage.SYMBOL_SELECTION, onClick = { onSelectInitialPage(InitialPage.SYMBOL_SELECTION) }, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(20.dp).testTag("initial_page_symbol") ) } } @@ -117,7 +118,7 @@ fun InitialPageColumn( RadioButton( selected = initialPage == InitialPage.TEXT_TO_SPEECH, onClick = { onSelectInitialPage(InitialPage.TEXT_TO_SPEECH) }, - modifier = Modifier.size(20.dp) + modifier = Modifier.size(20.dp).testTag("initial_page_tts") ) } } From 58e5b886c885352c06819268912dfb76102797e6 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sat, 2 Dec 2023 21:38:01 +0900 Subject: [PATCH 5/9] :white_check_mark: Implement BackupSettigsScreenTest --- .../speechbuddy/AccountSettingsScreenTest.kt | 2 +- .../speechbuddy/BackupSettingsScreenTest.kt | 86 +++++++++++++++++++ .../speechbuddy/DisplaySettingsScreenTest.kt | 4 + .../compose/settings/BackupSettings.kt | 4 +- 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt index 0fc89ed9..b5baf740 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt @@ -77,7 +77,7 @@ class AccountSettingsScreenTest { } @Test - fun should_display_loading_indicator_when_clicking_backup_button_in_backup_dialog() { + fun should_remove_dialog_when_clicking_backup_button_in_backup_dialog() { composeTestRule.onNodeWithText(LOGOUT).performClick() composeTestRule.onNodeWithText(BACKUP).performClick() composeTestRule.onNodeWithText(BACKUP_DIALOG).assertDoesNotExist() diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt new file mode 100644 index 00000000..86f18d59 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt @@ -0,0 +1,86 @@ +package com.example.speechbuddy + +import android.content.Intent +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsOff +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import com.example.speechbuddy.compose.settings.BackupSettings +import com.example.speechbuddy.ui.SpeechBuddyTheme +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +class BackupSettingsScreenTest { + private val androidTestUtil = AndroidTestUtil() + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeTestRule = androidTestUtil.createAndroidIntentComposeRule { + Intent(it, HomeActivity::class.java).apply { + putExtra("isTest", true) + } + } + + @Before + fun setUp() { + hiltRule.inject() + composeTestRule.activity.setContent { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { + BackupSettings( + paddingValues = PaddingValues() + ) + } + } + } + + @Test + fun should_display_all_elements_when_account_settings_screen_appears() { + composeTestRule.onNodeWithText(BACKUP_TO_SERVER).assertIsDisplayed() + composeTestRule.onNodeWithText(LAST_BACKUP_DATE).assertIsDisplayed() + composeTestRule.onNodeWithText(ENABLE_AUTO_BACKUP).assertIsDisplayed() + composeTestRule.onNodeWithText(BACKUP_NOW).assertIsDisplayed().assertIsEnabled().assertHasClickAction() + composeTestRule.onNodeWithTag("auto_backup").assertIsDisplayed().assertIsEnabled() + } + + @Test + fun should_change_auto_backup_when_switch_is_clicked() { + composeTestRule.onNodeWithTag("auto_backup").assertIsOn() + composeTestRule.onNodeWithTag("auto_backup").performClick() + composeTestRule.onNodeWithTag("auto_backup").assertIsOff() + composeTestRule.onNodeWithTag("auto_backup").performClick() + composeTestRule.onNodeWithTag("auto_backup").assertIsOn() + } + + @Test + fun should_show_loading_indicator_when_backup_is_clicked() { + composeTestRule.onNodeWithText(BACKUP_NOW).performClick() + composeTestRule.onNodeWithTag("backup_loading").assertIsDisplayed() + } + + companion object { + const val BACKUP_TO_SERVER = "서버에 백업하기" + const val LAST_BACKUP_DATE = "마지막 백업 날짜" + const val ENABLE_AUTO_BACKUP = "자동 백업 활성화" + const val BACKUP_NOW = "지금 백업하기" + + val LIGHT_COLOR = Color(0xFF000000) + val DARK_COLOR = Color(0xFFFFFFFF) + } + +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt index 806f0a3b..177c0a28 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/DisplaySettingsScreenTest.kt @@ -5,6 +5,7 @@ import androidx.activity.compose.setContent import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.graphics.Color import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotSelected import androidx.compose.ui.test.assertIsOff import androidx.compose.ui.test.assertIsOn @@ -59,6 +60,9 @@ class DisplaySettingsScreenTest { composeTestRule.onNodeWithText(INITIAL_PAGE).assertIsDisplayed() composeTestRule.onNodeWithText(SYMBOL).assertIsDisplayed() composeTestRule.onNodeWithText(TTS).assertIsDisplayed() + composeTestRule.onNodeWithTag("dark_mode").assertIsDisplayed().assertIsEnabled() + composeTestRule.onNodeWithTag("initial_page_symbol").assertIsDisplayed().assertIsEnabled() + composeTestRule.onNodeWithTag("initial_page_tts").assertIsDisplayed().assertIsEnabled() } @Test diff --git a/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/BackupSettings.kt b/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/BackupSettings.kt index 57a7b20a..1dabdcc7 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/BackupSettings.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/compose/settings/BackupSettings.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -70,7 +71,7 @@ fun BackupSettings( Switch( checked = uiState.isAutoBackupEnabled, onCheckedChange = { viewModel.setAutoBackup(it) }, - modifier = Modifier.heightIn(max = 32.dp), + modifier = Modifier.heightIn(max = 32.dp).testTag("auto_backup"), enabled = uiState.buttonEnabled ) } @@ -117,6 +118,7 @@ fun BackupSettings( modifier = Modifier .fillMaxSize() .wrapContentSize() + .testTag("backup_loading") ) } } From b37b61ac31d3720406cd76c2b7773e91bcc4789a Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sun, 3 Dec 2023 12:00:30 +0900 Subject: [PATCH 6/9] Update frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 이석찬 / Lee Sukchan --- .../java/com/example/speechbuddy/BackupSettingsScreenTest.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt index 86f18d59..02d999cc 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/BackupSettingsScreenTest.kt @@ -78,9 +78,6 @@ class BackupSettingsScreenTest { const val LAST_BACKUP_DATE = "마지막 백업 날짜" const val ENABLE_AUTO_BACKUP = "자동 백업 활성화" const val BACKUP_NOW = "지금 백업하기" - - val LIGHT_COLOR = Color(0xFF000000) - val DARK_COLOR = Color(0xFFFFFFFF) } } \ No newline at end of file From 78c88e6ef623cbe2681d95b061d0dce06df56708 Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sun, 3 Dec 2023 12:26:54 +0900 Subject: [PATCH 7/9] :white_check_mark: Implement GuestSettingsScreenTest, MainSettingsScreenTest --- .../speechbuddy/GuestSettingsScreenTest.kt | 70 ++++++++++++++++ .../speechbuddy/MainSettingsScreenTest.kt | 81 +++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/GuestSettingsScreenTest.kt create mode 100644 frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/GuestSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/GuestSettingsScreenTest.kt new file mode 100644 index 00000000..03205887 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/GuestSettingsScreenTest.kt @@ -0,0 +1,70 @@ +package com.example.speechbuddy + +import android.content.Intent +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.onNodeWithText +import com.example.speechbuddy.compose.settings.GuestSettings +import com.example.speechbuddy.ui.SpeechBuddyTheme +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +class GuestSettingsScreenTest { + private val androidTestUtil = AndroidTestUtil() + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeTestRule = androidTestUtil.createAndroidIntentComposeRule { + Intent(it, HomeActivity::class.java).apply { + putExtra("isTest", true) + } + } + + @Before + fun setUp() { + hiltRule.inject() + val guestId = -1 + val fakeEmail = "guest" + val fakeNickname = "guest" + composeTestRule.activity.sessionManager.setUserId(guestId) + composeTestRule.activity.userRepository.setMyInfo(guestId, fakeEmail, fakeNickname) + composeTestRule.activity.setContent { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { + GuestSettings( + paddingValues = PaddingValues() + ) + } + } + } + + @Test + fun should_display_all_elements_when_account_settings_screen_appears() { + composeTestRule.onNodeWithText(ACCOUNT).assertIsDisplayed() + composeTestRule.onNodeWithText(ACCOUNT_GUEST_MODE).assertIsDisplayed() + composeTestRule.onNodeWithText(EXIT).assertIsDisplayed().assertIsEnabled().assertHasClickAction() + } + + @After + fun tearDown() { + composeTestRule.activityRule.scenario.close() + } + + companion object { + const val ACCOUNT = "계정" + const val ACCOUNT_GUEST_MODE = "현재 게스트 모드를 사용 중입니다." + const val EXIT = "게스트 모드에서 나가기" + } +} \ No newline at end of file diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt new file mode 100644 index 00000000..4f15ec96 --- /dev/null +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt @@ -0,0 +1,81 @@ +package com.example.speechbuddy + +import android.content.Intent +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.onNodeWithText +import androidx.navigation.NavHostController +import com.example.speechbuddy.compose.settings.MainSettings +import com.example.speechbuddy.ui.SpeechBuddyTheme +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +class MainSettingsScreenTest { + private val androidTestUtil = AndroidTestUtil() + + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeTestRule = androidTestUtil.createAndroidIntentComposeRule { + Intent(it, HomeActivity::class.java).apply { + putExtra("isTest", true) + } + } + + @Before + fun setUp() { + hiltRule.inject() + val fakeId = 0 + val fakeEmail = "email" + val fakeNickname = "nickname" + composeTestRule.activity.sessionManager.setUserId(fakeId) + composeTestRule.activity.userRepository.setMyInfo(fakeId, fakeEmail, fakeNickname) + composeTestRule.activity.setContent { + SpeechBuddyTheme( + settingsRepository = composeTestRule.activity.settingsRepository, + initialDarkMode = false + ) { + MainSettings( + paddingValues = PaddingValues(), + navController = NavHostController(composeTestRule.activity.applicationContext) + ) + } + } + } + + @Test + fun should_display_all_elements_when_account_settings_screen_appears_in_guest_mode() { + composeTestRule.onNodeWithText(ACCOUNT).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(DISPLAY).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(MY_SETTINGS).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(BACKUP).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(VERSION).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(DEVELOPER).assertIsDisplayed().assertHasClickAction() + composeTestRule.onNodeWithText(COPYRIGHT).assertIsDisplayed().assertHasClickAction() + } + + @After + fun tearDown() { + composeTestRule.activityRule.scenario.close() + } + + companion object { + const val ACCOUNT = "계정" + const val DISPLAY = "디스플레이" + const val MY_SETTINGS = "상징 목록 관리" + const val BACKUP = "서버에 백업하기" + const val VERSION = "버전 정보" + const val DEVELOPER = "개발자 정보" + const val COPYRIGHT = "저작권 정보" + } + +} \ No newline at end of file From d51c7b5809674183e4327c14b3ec6a61269be4ab Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sun, 3 Dec 2023 12:29:10 +0900 Subject: [PATCH 8/9] :white_check_mark: Implement GuestSettingsScreenTest, MainSettingsScreenTest --- .../java/com/example/speechbuddy/MainSettingsScreenTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt index 4f15ec96..3d76c7e4 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/MainSettingsScreenTest.kt @@ -5,7 +5,6 @@ import androidx.activity.compose.setContent import androidx.compose.foundation.layout.PaddingValues import androidx.compose.ui.test.assertHasClickAction import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.onNodeWithText import androidx.navigation.NavHostController import com.example.speechbuddy.compose.settings.MainSettings From dee666287fa827e200ad962d0a613e0ffcac3cac Mon Sep 17 00:00:00 2001 From: Minyeong Lee <13619@snu.ac.kr> Date: Sun, 3 Dec 2023 14:51:33 +0900 Subject: [PATCH 9/9] :bug: Fix settings UI tests --- .../speechbuddy/AccountSettingsScreenTest.kt | 103 +++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt index b5baf740..9eb17f08 100644 --- a/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt +++ b/frontend/app/src/androidTest/java/com/example/speechbuddy/AccountSettingsScreenTest.kt @@ -14,6 +14,7 @@ import com.example.speechbuddy.compose.settings.AccountSettings import com.example.speechbuddy.ui.SpeechBuddyTheme import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.DelicateCoroutinesApi import org.junit.After import org.junit.Before import org.junit.Rule @@ -56,6 +57,9 @@ class AccountSettingsScreenTest { @Test fun should_display_all_elements_when_account_settings_screen_appears() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(ACCOUNT).assertIsDisplayed() composeTestRule.onNodeWithText(EMAIL).assertIsDisplayed() composeTestRule.onNodeWithText(NICKNAME).assertIsDisplayed() @@ -67,7 +71,13 @@ class AccountSettingsScreenTest { @Test fun should_display_backup_dialog_when_clicking_backup_button() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() composeTestRule.onNodeWithText(WITHDRAW).assertIsNotEnabled() composeTestRule.onNodeWithText(BACKUP).assertIsDisplayed().assertHasClickAction() @@ -78,8 +88,17 @@ class AccountSettingsScreenTest { @Test fun should_remove_dialog_when_clicking_backup_button_in_backup_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(BACKUP).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(BACKUP_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(BACKUP).assertDoesNotExist() composeTestRule.onNodeWithText(LOGOUT).assertIsNotEnabled() @@ -88,11 +107,20 @@ class AccountSettingsScreenTest { @Test fun should_display_logout_dialog_when_clicking_logout_button_in_backup_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.waitUntil { + composeTestRule.onAllNodesWithText(LOGOUT_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed() composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertIsDisplayed() composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() @@ -102,35 +130,62 @@ class AccountSettingsScreenTest { } @Test - fun should_display_loading_indicator_when_clicking_logout_button_in_logout_dialog() { + fun should_remove_dialog_when_clicking_logout_button_in_logout_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.waitUntil { + composeTestRule.onAllNodesWithText(LOGOUT_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() + composeTestRule.waitUntil { + composeTestRule.onAllNodesWithText(LOGOUT_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() } @Test fun should_display_account_settings_screen_when_clicking_cancel_button_in_logout_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(LOGOUT).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(LOGOUT).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(BACKUP_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(LOGOUT)[0].assertIsNotEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[1].assertIsDisplayed() composeTestRule.onAllNodesWithText(LOGOUT)[2].assertHasClickAction().assertIsEnabled() composeTestRule.onAllNodesWithText(LOGOUT)[2].performClick() composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.waitUntil { + composeTestRule.onAllNodesWithText(LOGOUT_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(LOGOUT_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() } @Test fun should_display_first_withdraw_dialog_when_clicking_withdraw_button() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(WITHDRAW).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(FIRST_WITHDRAW_DIALOG).assertIsDisplayed() composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed().assertHasClickAction() composeTestRule.onNodeWithText(PROCEED).assertIsDisplayed().assertHasClickAction() @@ -141,8 +196,17 @@ class AccountSettingsScreenTest { @Test fun should_display_second_withdraw_dialog_when_clicking_progress_button_in_first_withdraw_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(WITHDRAW).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(SECOND_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertIsDisplayed() composeTestRule.onNodeWithText(CANCEL).assertIsDisplayed().assertHasClickAction() composeTestRule.onNodeWithText(LOGOUT).assertIsNotEnabled() @@ -152,9 +216,18 @@ class AccountSettingsScreenTest { } @Test - fun should_display_account_settings_screen_when_clicking_cancel_button_in_first_withdraw_dialog() { + fun should_remove_dialog_when_clicking_cancel_button_in_first_withdraw_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(WITHDRAW).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(FIRST_WITHDRAW_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() composeTestRule.onNodeWithText(LOGOUT).assertIsEnabled() @@ -162,22 +235,46 @@ class AccountSettingsScreenTest { } @Test - fun should_display_loading_indicator_when_clicking_withdraw_button_in_second_withdraw_dialog() { + fun should_remove_dialog_when_clicking_withdraw_button_in_second_withdraw_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(WITHDRAW).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(SECOND_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onAllNodesWithText(WITHDRAW)[0].assertIsNotEnabled() composeTestRule.onAllNodesWithText(WITHDRAW)[1].assertIsDisplayed() composeTestRule.onAllNodesWithText(WITHDRAW)[2].assertHasClickAction().assertIsEnabled() composeTestRule.onAllNodesWithText(WITHDRAW)[2].performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(SECOND_WITHDRAW_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() } @Test fun should_display_account_settings_screen_when_clicking_cancel_button_in_second_withdraw_dialog() { + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(WITHDRAW).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(WITHDRAW).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(FIRST_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(PROCEED).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(SECOND_WITHDRAW_DIALOG).fetchSemanticsNodes().size == 1 + } composeTestRule.onNodeWithText(CANCEL).performClick() + composeTestRule.waitUntil{ + composeTestRule.onAllNodesWithText(SECOND_WITHDRAW_DIALOG).fetchSemanticsNodes().isEmpty() + } composeTestRule.onNodeWithText(SECOND_WITHDRAW_DIALOG).assertDoesNotExist() composeTestRule.onNodeWithText(CANCEL).assertDoesNotExist() }