Cover all auth tests

This commit is contained in:
Alex Gabor 2022-04-01 16:06:35 +03:00
parent d948d06378
commit d9725e31e6
4 changed files with 110 additions and 13 deletions

View file

@ -2,6 +2,7 @@ package org.fnives.test.showcase.ui
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import org.fnives.test.showcase.R
import org.fnives.test.showcase.compose.ComposeActivity import org.fnives.test.showcase.compose.ComposeActivity
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
@ -25,7 +26,7 @@ class AuthComposeInstrumentedTest : KoinTest {
private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
private val mainDispatcherTestRule = ComposeMainDispatcherTestRule() private val mainDispatcherTestRule = ComposeMainDispatcherTestRule()
private lateinit var robot: ComposeLoginRobot private lateinit var robot: ComposeLoginRobot
private lateinit var screenRobot: ComposeScreenRobot private lateinit var navigationRobot: ComposeNavigationRobot
@Rule @Rule
@JvmField @JvmField
@ -36,7 +37,7 @@ class AuthComposeInstrumentedTest : KoinTest {
@Before @Before
fun setup() { fun setup() {
robot = ComposeLoginRobot(composeTestRule) robot = ComposeLoginRobot(composeTestRule)
screenRobot = ComposeScreenRobot(composeTestRule) navigationRobot = ComposeNavigationRobot(composeTestRule)
} }
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */ /** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
@ -47,9 +48,8 @@ class AuthComposeInstrumentedTest : KoinTest {
) )
composeTestRule.mainClock.advanceTimeBy(500L) composeTestRule.mainClock.advanceTimeBy(500L)
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() } composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
screenRobot.assertAuthScreen() navigationRobot.assertAuthScreen()
robot robot.setPassword("alma")
.setPassword("alma")
.setUsername("banan") .setUsername("banan")
.assertUsername("banan") .assertUsername("banan")
.assertPassword("alma") .assertPassword("alma")
@ -61,7 +61,93 @@ class AuthComposeInstrumentedTest : KoinTest {
composeTestRule.mainClock.autoAdvance = true composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() } composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
screenRobot.assertHomeScreen() navigationRobot.assertHomeScreen()
} }
/** GIVEN empty password and username WHEN signIn THEN error password is shown */
@Test
fun emptyPasswordShowsProperErrorMessage() {
composeTestRule.mainClock.advanceTimeBy(500L)
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot.setUsername("banan")
.assertUsername("banan")
.clickOnLogin()
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
robot.assertErrorIsShown(R.string.password_is_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
}
/** GIVEN password and empty username WHEN signIn THEN error username is shown */
@Test
fun emptyUserNameShowsProperErrorMessage() {
composeTestRule.mainClock.advanceTimeBy(500L)
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot
.setPassword("banan")
.assertPassword("banan")
.clickOnLogin()
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
robot.assertErrorIsShown(R.string.username_is_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
}
/** GIVEN password and username and invalid credentials response WHEN signIn THEN error invalid credentials is shown */
@Test
fun invalidCredentialsGivenShowsProperErrorMessage() {
mockServerScenarioSetup.setScenario(
AuthScenario.InvalidCredentials(password = "alma", username = "banan")
)
composeTestRule.mainClock.advanceTimeBy(500L)
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot.setUsername("alma")
.setPassword("banan")
.assertUsername("alma")
.assertPassword("banan")
composeTestRule.mainClock.autoAdvance = false
robot.clickOnLogin()
composeTestRule.mainClock.advanceTimeByFrame()
robot.assertLoading()
composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
robot.assertErrorIsShown(R.string.credentials_invalid)
.assertNotLoading()
navigationRobot.assertAuthScreen()
}
/** GIVEN password and username and error response WHEN signIn THEN error invalid credentials is shown */
@Test
fun networkErrorShowsProperErrorMessage() {
mockServerScenarioSetup.setScenario(
AuthScenario.GenericError(username = "alma", password = "banan")
)
composeTestRule.mainClock.advanceTimeBy(500L)
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
navigationRobot.assertAuthScreen()
robot.setUsername("alma")
.setPassword("banan")
.assertUsername("alma")
.assertPassword("banan")
composeTestRule.mainClock.autoAdvance = false
robot.clickOnLogin()
composeTestRule.mainClock.advanceTimeByFrame()
robot.assertLoading()
composeTestRule.mainClock.autoAdvance = true
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
robot.assertErrorIsShown(R.string.something_went_wrong)
.assertNotLoading()
navigationRobot.assertAuthScreen()
}
} }

View file

@ -1,7 +1,9 @@
package org.fnives.test.showcase.ui package org.fnives.test.showcase.ui
import android.content.Context
import androidx.compose.ui.test.* import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.test.core.app.ApplicationProvider
import org.fnives.test.showcase.compose.screen.auth.AuthScreenTag import org.fnives.test.showcase.compose.screen.auth.AuthScreenTag
class ComposeLoginRobot( class ComposeLoginRobot(
@ -34,4 +36,13 @@ class ComposeLoginRobot(
fun assertLoading(): ComposeLoginRobot = apply { fun assertLoading(): ComposeLoginRobot = apply {
composeTestRule.onNodeWithTag(AuthScreenTag.LoadingIndicator).assertIsDisplayed() composeTestRule.onNodeWithTag(AuthScreenTag.LoadingIndicator).assertIsDisplayed()
} }
fun assertNotLoading(): ComposeLoginRobot = apply {
composeTestRule.onAllNodesWithTag(AuthScreenTag.LoadingIndicator).assertCountEquals(0)
}
fun assertErrorIsShown(stringId: Int): ComposeLoginRobot = apply {
composeTestRule.onNodeWithTag(AuthScreenTag.LoginError)
.assertTextContains(ApplicationProvider.getApplicationContext<Context>().resources.getString(stringId))
}
} }

View file

@ -1,19 +1,18 @@
package org.fnives.test.showcase.ui package org.fnives.test.showcase.ui
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import org.fnives.test.showcase.compose.screen.AppNavigationTag import org.fnives.test.showcase.compose.screen.AppNavigationTag
class ComposeScreenRobot( class ComposeNavigationRobot(
private val composeTestRule: ComposeTestRule, private val composeTestRule: ComposeTestRule,
) { ) {
fun assertHomeScreen(): ComposeScreenRobot = apply { fun assertHomeScreen(): ComposeNavigationRobot = apply {
composeTestRule.onNodeWithTag(AppNavigationTag.HomeScreen).assertIsDisplayed() composeTestRule.onNodeWithTag(AppNavigationTag.HomeScreen).assertExists()
} }
fun assertAuthScreen(): ComposeScreenRobot = apply { fun assertAuthScreen(): ComposeNavigationRobot = apply {
composeTestRule.onNodeWithTag(AppNavigationTag.AuthScreen).assertIsDisplayed() composeTestRule.onNodeWithTag(AppNavigationTag.AuthScreen).assertExists()
} }
} }

View file

@ -135,7 +135,7 @@ private fun Snackbar(authScreenState: AuthScreenState, modifier: Modifier = Modi
val stringId = error?.stringResId() val stringId = error?.stringResId()
if (stringId != null) { if (stringId != null) {
Snackbar(modifier = Modifier.padding(horizontal = 16.dp)) { Snackbar(modifier = Modifier.padding(horizontal = 16.dp)) {
Text(text = stringResource(stringId)) Text(text = stringResource(stringId), Modifier.testTag(AuthScreenTag.LoginError))
} }
} }
} }
@ -176,5 +176,6 @@ object AuthScreenTag {
const val PasswordInput = "AuthScreenTag.PasswordInput" const val PasswordInput = "AuthScreenTag.PasswordInput"
const val LoadingIndicator = "AuthScreenTag.LoadingIndicator" const val LoadingIndicator = "AuthScreenTag.LoadingIndicator"
const val LoginButton = "AuthScreenTag.LoginButton" const val LoginButton = "AuthScreenTag.LoginButton"
const val LoginError = "AuthScreenTag.LoginError"
const val PasswordVisibilityToggle = "AuthScreenTag.PasswordVisibilityToggle" const val PasswordVisibilityToggle = "AuthScreenTag.PasswordVisibilityToggle"
} }