Assert navigation
This commit is contained in:
parent
225fbed849
commit
d948d06378
11 changed files with 167 additions and 18 deletions
|
|
@ -5,7 +5,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import org.fnives.test.showcase.compose.ComposeActivity
|
||||
import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario
|
||||
import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.ComposeMainDispatcherTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.ComposeNetworkSynchronizationTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.anyResourceIdling
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
|
|
@ -20,10 +21,11 @@ class AuthComposeInstrumentedTest : KoinTest {
|
|||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<ComposeActivity>()
|
||||
|
||||
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule()
|
||||
private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule(networkSynchronizationTestRule = ComposeNetworkSynchronizationTestRule(composeTestRule))
|
||||
private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup
|
||||
private val mainDispatcherTestRule = MainDispatcherTestRule()
|
||||
private val mainDispatcherTestRule = ComposeMainDispatcherTestRule()
|
||||
private lateinit var robot: ComposeLoginRobot
|
||||
private lateinit var screenRobot: ComposeScreenRobot
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
|
|
@ -34,6 +36,7 @@ class AuthComposeInstrumentedTest : KoinTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
robot = ComposeLoginRobot(composeTestRule)
|
||||
screenRobot = ComposeScreenRobot(composeTestRule)
|
||||
}
|
||||
|
||||
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
|
||||
|
|
@ -44,19 +47,21 @@ class AuthComposeInstrumentedTest : KoinTest {
|
|||
)
|
||||
composeTestRule.mainClock.advanceTimeBy(500L)
|
||||
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
|
||||
composeTestRule.waitForIdle()
|
||||
screenRobot.assertAuthScreen()
|
||||
robot
|
||||
.setPassword("alma")
|
||||
.setUsername("banan")
|
||||
.assertUsername("banan")
|
||||
.assertPassword("alma")
|
||||
|
||||
composeTestRule.mainClock.autoAdvance = false
|
||||
robot.clickOnLogin()
|
||||
composeTestRule.mainClock.advanceTimeByFrame()
|
||||
robot.assertLoading()
|
||||
composeTestRule.mainClock.autoAdvance = true
|
||||
|
||||
// mainDispatcherTestRule.advanceUntilIdleWithIdlingResources()
|
||||
// robot.assertNavigatedToHome()
|
||||
composeTestRule.mainClock.advanceTimeUntil { anyResourceIdling() }
|
||||
screenRobot.assertHomeScreen()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.fnives.test.showcase.ui
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.ComposeTestRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import org.fnives.test.showcase.compose.screen.AppNavigationTag
|
||||
|
||||
class ComposeScreenRobot(
|
||||
private val composeTestRule: ComposeTestRule,
|
||||
) {
|
||||
|
||||
fun assertHomeScreen(): ComposeScreenRobot = apply {
|
||||
composeTestRule.onNodeWithTag(AppNavigationTag.HomeScreen).assertIsDisplayed()
|
||||
}
|
||||
|
||||
fun assertAuthScreen(): ComposeScreenRobot = apply {
|
||||
composeTestRule.onNodeWithTag(AppNavigationTag.AuthScreen).assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
|
@ -5,16 +5,17 @@ import androidx.compose.material.MaterialTheme
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import kotlinx.coroutines.delay
|
||||
import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase
|
||||
import org.fnives.test.showcase.compose.screen.auth.AuthScreen
|
||||
import org.fnives.test.showcase.compose.screen.auth.rememberAuthScreenState
|
||||
import org.fnives.test.showcase.compose.screen.home.HomeScreen
|
||||
import org.fnives.test.showcase.compose.screen.home.rememberHomeScreenState
|
||||
import org.fnives.test.showcase.compose.screen.splash.SplashScreen
|
||||
import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase
|
||||
import org.koin.androidx.compose.get
|
||||
|
||||
@Composable
|
||||
|
|
@ -35,15 +36,22 @@ fun AppNavigation() {
|
|||
composable("Splash") { SplashScreen() }
|
||||
composable("Auth") {
|
||||
val authState = rememberAuthScreenState()
|
||||
AuthScreen(authState)
|
||||
AuthScreen(Modifier.testTag(AppNavigationTag.AuthScreen), authState)
|
||||
if (authState.navigateToHome?.consume() != null) {
|
||||
navController.navigate("Home")
|
||||
}
|
||||
}
|
||||
composable("Home") {
|
||||
HomeScreen(rememberHomeScreenState {
|
||||
navController.navigate("Auth")
|
||||
})
|
||||
HomeScreen(
|
||||
Modifier.testTag(AppNavigationTag.HomeScreen),
|
||||
homeScreenState = rememberHomeScreenState(
|
||||
onLogout = { navController.navigate("Auth") })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object AppNavigationTag {
|
||||
const val AuthScreen = "AppNavigationTag.AuthScreen"
|
||||
const val HomeScreen = "AppNavigationTag.HomeScreen"
|
||||
}
|
||||
|
|
@ -26,9 +26,10 @@ import org.fnives.test.showcase.R
|
|||
|
||||
@Composable
|
||||
fun AuthScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
authScreenState: AuthScreenState = rememberAuthScreenState()
|
||||
) {
|
||||
ConstraintLayout(Modifier.fillMaxSize()) {
|
||||
ConstraintLayout(modifier.fillMaxSize()) {
|
||||
val (title, credentials, snackbar, loading, login) = createRefs()
|
||||
Title(
|
||||
Modifier
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ class AuthScreenState(
|
|||
LoginStatus.INVALID_USERNAME -> error = ErrorType.UNSUPPORTED_USERNAME
|
||||
LoginStatus.INVALID_PASSWORD -> error = ErrorType.UNSUPPORTED_PASSWORD
|
||||
}
|
||||
println("asdasdasd: ${error.hashCode()}")
|
||||
}
|
||||
|
||||
fun dismissError() {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,10 @@ import org.fnives.test.showcase.model.content.FavouriteContent
|
|||
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
homeScreenState: HomeScreenState = rememberHomeScreenState()
|
||||
) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
Column(modifier.fillMaxSize()) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Title(Modifier.weight(1f))
|
||||
Image(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import org.koin.test.KoinTest
|
|||
*/
|
||||
class MockServerScenarioSetupResetingTestRule(
|
||||
private val reloadKoinModulesIfNecessaryTestRule: ReloadKoinModulesIfNecessaryTestRule = ReloadKoinModulesIfNecessaryTestRule(),
|
||||
private val networkSynchronizationTestRule: NetworkSynchronizationTestRule = NetworkSynchronizationTestRule()
|
||||
private val networkSynchronizationTestRule: TestRule = NetworkSynchronizationTestRule()
|
||||
) : TestRule, KoinTest {
|
||||
|
||||
lateinit var mockServerScenarioSetup: MockServerScenarioSetup
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import org.fnives.test.showcase.storage.database.DatabaseInitialization
|
||||
import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ComposeMainDispatcherTestRule : TestRule {
|
||||
|
||||
private lateinit var testDispatcher: TestDispatcher
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement =
|
||||
object : Statement() {
|
||||
@Throws(Throwable::class)
|
||||
override fun evaluate() {
|
||||
val dispatcher = StandardTestDispatcher()
|
||||
testDispatcher = dispatcher
|
||||
DatabaseInitialization.dispatcher = dispatcher
|
||||
base.evaluate()
|
||||
}
|
||||
}
|
||||
|
||||
fun advanceUntilIdleWithIdlingResources() = runOnUIAwaitOnCurrent {
|
||||
testDispatcher.advanceUntilIdleWithIdlingResources()
|
||||
}
|
||||
|
||||
fun advanceUntilIdle() = runOnUIAwaitOnCurrent {
|
||||
testDispatcher.scheduler.advanceUntilIdle()
|
||||
}
|
||||
|
||||
fun advanceTimeBy(delayInMillis: Long) = runOnUIAwaitOnCurrent {
|
||||
testDispatcher.scheduler.advanceTimeBy(delayInMillis)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun TestDispatcher.advanceUntilIdleWithIdlingResources() {
|
||||
scheduler.advanceUntilIdle() // advance until a request is sent
|
||||
while (anyResourceIdling()) { // check if any request is in progress
|
||||
awaitIdlingResources() // complete all requests and other idling resources
|
||||
scheduler.advanceUntilIdle() // run coroutines after request is finished
|
||||
}
|
||||
scheduler.advanceUntilIdle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.compose.ui.test.IdlingResource
|
||||
import androidx.compose.ui.test.junit4.ComposeTestRule
|
||||
import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
import org.koin.test.KoinTest
|
||||
|
||||
class ComposeNetworkSynchronizationTestRule(private val composeTestRule: ComposeTestRule) : TestRule, KoinTest {
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
override fun evaluate() {
|
||||
disposable = registerNetworkingSynchronization()
|
||||
try {
|
||||
base.evaluate()
|
||||
} finally {
|
||||
dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dispose() = disposable?.dispose()
|
||||
|
||||
@CheckResult
|
||||
private fun registerNetworkingSynchronization(): Disposable {
|
||||
val idlingResources = NetworkTestConfigurationHelper.getOkHttpClients()
|
||||
.associateBy(keySelector = { it.toString() })
|
||||
.map { (key, client) -> OkHttp3IdlingResource.create(key, client) }
|
||||
.map {
|
||||
ComposeIdlingResourceDisposable(composeTestRule, object : IdlingResource {
|
||||
override val isIdleNow: Boolean
|
||||
get() {
|
||||
return it.isIdleNow
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return CompositeDisposable(idlingResources)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ComposeIdlingResourceDisposable(
|
||||
private val composeTestRule: ComposeTestRule,
|
||||
private val idlingResource: IdlingResource
|
||||
) : Disposable {
|
||||
override var isDisposed: Boolean = false
|
||||
private set
|
||||
|
||||
init {
|
||||
composeTestRule.registerIdlingResource(idlingResource)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
if (isDisposed) return
|
||||
isDisposed = true
|
||||
composeTestRule.unregisterIdlingResource(idlingResource)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,13 +22,13 @@ class MainDispatcherTestRule : TestRule {
|
|||
@Throws(Throwable::class)
|
||||
override fun evaluate() {
|
||||
val dispatcher = StandardTestDispatcher()
|
||||
// Dispatchers.setMain(dispatcher)
|
||||
Dispatchers.setMain(dispatcher)
|
||||
testDispatcher = dispatcher
|
||||
DatabaseInitialization.dispatcher = dispatcher
|
||||
try {
|
||||
base.evaluate()
|
||||
} finally {
|
||||
// Dispatchers.resetMain()
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class NetworkSynchronizationTestRule : TestRule, KoinTest {
|
|||
|
||||
@CheckResult
|
||||
private fun registerNetworkingSynchronization(): Disposable {
|
||||
val idlingResources = NetworkTestConfigurationHelper.getOkHttpClients()//.filterIndexed { index, okHttpClient -> index == 0 }
|
||||
val idlingResources = NetworkTestConfigurationHelper.getOkHttpClients()
|
||||
.associateBy(keySelector = { it.toString() })
|
||||
.map { (key, client) -> client.asIdlingResource(key) }
|
||||
.map(::IdlingResourceDisposable)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue