Skip to content

Commit

Permalink
Merge pull request #35 from azrael8576/feat/home
Browse files Browse the repository at this point in the history
  • Loading branch information
azrael8576 authored Nov 17, 2023
2 parents 0e98330 + 07da8c6 commit a5a70cf
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ class AtAppStateTest {
}

assertThat(state.topLevelDestinations).hasSize(3)
assertThat(state.topLevelDestinations[0].name).ignoringCase().contains("schedule")
assertThat(state.topLevelDestinations[1].name).ignoringCase().contains("home")
assertThat(state.topLevelDestinations[0].name).ignoringCase().contains("home")
assertThat(state.topLevelDestinations[1].name).ignoringCase().contains("schedule")
assertThat(state.topLevelDestinations[2].name).ignoringCase().contains("contact_me")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal open class NavigationRobot(
composeTestRule.waitForIdle()
}

private fun clickNavHome() {
internal fun clickNavHome() {
navHome.performClick()
// 等待任何動畫完成
composeTestRule.waitForIdle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ class NavigationTest {
}

@Test
fun isScheduleScreen_afterLogin() {
fun isHomeScreen_afterLogin() {
welcomeEndToEndRobot(composeTestRule) {
} getStartedClick {
} login {
verifyScheduleTopAppBarDisplayed()
verifyMenuButtonDisplayed()
}
}

Expand All @@ -77,11 +77,11 @@ class NavigationTest {
}

/*
* When pressing back from any top level destination except "Schedule", the app navigates back
* to the "Schedule" destination, no matter which destinations you visited in between.
* When pressing back from any top level destination except "Home", the app navigates back
* to the "Home" destination, no matter which destinations you visited in between.
*/
@Test
fun navigationBar_backFromAnyDestination_returnsToSchedule() {
fun navigationBar_backFromAnyDestination_returnsToHome() {
welcomeEndToEndRobot(composeTestRule) {
} getStartedClick {
} login {
Expand All @@ -91,7 +91,7 @@ class NavigationTest {
// WHEN the user uses the system button/gesture to go back
Espresso.pressBack()
}
verifyScheduleTopAppBarDisplayed()
verifyMenuButtonDisplayed()
}
}

Expand All @@ -106,8 +106,8 @@ class NavigationTest {
navigationRobot(composeTestRule) {
// GIVEN the user navigates to the Contact Me destination
clickNavContactMe()
// and then navigates to the Schedule destination
clickNavSchedule()
// and then navigates to the Home destination
clickNavHome()
// WHEN the user uses the system button/gesture to go back
Espresso.pressBack()
// THEN the app quits
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.wei.amazingtalker.ui.robot

import androidx.annotation.StringRes
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.wei.amazingtalker.MainActivity
import kotlin.properties.ReadOnlyProperty
import com.wei.amazingtalker.feature.home.R as FeatureHomeR

/**
* Screen Robot for End To End Test.
*
* 遵循此模型,找到測試使用者介面元素、檢查其屬性、和透過測試規則執行動作:
* composeTestRule{.finder}{.assertion}{.action}
*
* Testing cheatsheet:
* https://developer.android.com/jetpack/compose/testing-cheatsheet
*/
internal fun homeEndToEndRobot(
composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>,
func: HomeEndToEndRobot.() -> Unit,
) = HomeEndToEndRobot(composeTestRule).apply(func)

internal open class HomeEndToEndRobot(
private val composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<MainActivity>, MainActivity>,
) {
private fun AndroidComposeTestRule<*, *>.stringResource(@StringRes resId: Int) =
ReadOnlyProperty<Any?, String> { _, _ -> activity.getString(resId) }

private fun withRole(role: Role) = SemanticsMatcher("${SemanticsProperties.Role.name} contains '$role'") {
val roleProperty = it.config.getOrNull(SemanticsProperties.Role) ?: false
roleProperty == role
}

// The strings used for matching in these tests
private val menuDescription by composeTestRule.stringResource(FeatureHomeR.string.menu)

private val menuButton by lazy {
composeTestRule.onNode(
withRole(Role.Button)
.and(hasContentDescription(menuDescription)),
)
}

fun verifyMenuButtonDisplayed() {
menuButton.assertExists().assertIsDisplayed()
}

fun isMenuButtonDisplayed(): Boolean {
return try {
verifyMenuButtonDisplayed()
true
} catch (e: AssertionError) {
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ internal open class LoginEndToEndRobotRobot(
}
}

infix fun login(func: ScheduleEndToEndRobot.() -> Unit): ScheduleEndToEndRobot {
infix fun login(func: HomeEndToEndRobot.() -> Unit): HomeEndToEndRobot {
loginButton.performClick()
return scheduleEndToEndRobot(composeTestRule) {
return homeEndToEndRobot(composeTestRule) {
// 等待任何動畫完成
composeTestRule.waitUntil(3_000) { isScheduleTopAppBarDisplayed() }
composeTestRule.waitUntil(3_000) { isMenuButtonDisplayed() }
func()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import androidx.window.layout.DisplayFeature
import com.wei.amazingtalker.core.designsystem.ui.DeviceOrientation
import com.wei.amazingtalker.feature.contactme.contactme.navigation.contactMeScreen
import com.wei.amazingtalker.feature.home.home.navigation.homeGraph
import com.wei.amazingtalker.feature.home.home.navigation.homeRoute
import com.wei.amazingtalker.feature.login.login.navigation.loginScreen
import com.wei.amazingtalker.feature.login.welcome.navigation.welcomeGraph
import com.wei.amazingtalker.feature.login.welcome.navigation.welcomeRoute
import com.wei.amazingtalker.feature.teacherschedule.schedule.navigation.scheduleGraph
import com.wei.amazingtalker.feature.teacherschedule.schedule.navigation.scheduleRoute
import com.wei.amazingtalker.feature.teacherschedule.scheduledetail.navigation.scheduleDetailScreen
import com.wei.amazingtalker.ui.AtAppState

Expand All @@ -28,7 +28,7 @@ fun AtNavHost(
appState: AtAppState,
isTokenValid: Boolean,
displayFeatures: List<DisplayFeature>,
startDestination: String = if (isTokenValid) scheduleRoute else welcomeRoute,
startDestination: String = if (isTokenValid) homeRoute else welcomeRoute,
) {
val navController = appState.navController
val navigationType = appState.navigationType
Expand All @@ -49,14 +49,17 @@ fun AtNavHost(
)
},
)
homeGraph(
navController = navController,
tokenInvalidNavigate = { appState.tokenInvalidNavigate() },
)
scheduleGraph(
navController = navController,
tokenInvalidNavigate = { appState.tokenInvalidNavigate() },
nestedGraphs = {
scheduleDetailScreen(navController = navController)
},
)
homeGraph()
contactMeScreen(
navController = navController,
contentType = contentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ enum class TopLevelDestination(
val iconTextId: Int,
val titleTextId: Int,
) {
SCHEDULE(
selectedIcon = AtIcons.Schedule,
unselectedIcon = AtIcons.ScheduleBorder,
iconTextId = R.string.schedule,
titleTextId = R.string.schedule,
),
HOME(
selectedIcon = AtIcons.Home,
unselectedIcon = AtIcons.HomeBorder,
iconTextId = R.string.home,
titleTextId = R.string.home,
),
SCHEDULE(
selectedIcon = AtIcons.Schedule,
unselectedIcon = AtIcons.ScheduleBorder,
iconTextId = R.string.schedule,
titleTextId = R.string.schedule,
),
CONTACT_ME(
selectedIcon = AtIcons.ContactMe,
unselectedIcon = AtIcons.ContactMeBorder,
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/java/com/wei/amazingtalker/ui/AtAppState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ class AtAppState(

val currentTopLevelDestination: TopLevelDestination?
@Composable get() = when (currentDestination?.route) {
homeRoute -> TopLevelDestination.HOME
scheduleRoute -> TopLevelDestination.SCHEDULE
contactMeRoute -> TopLevelDestination.CONTACT_ME
homeRoute -> TopLevelDestination.HOME
else -> null
}

Expand Down Expand Up @@ -205,11 +205,11 @@ class AtAppState(
}

when (topLevelDestination) {
TopLevelDestination.SCHEDULE -> navController.navigateToSchedule(
TopLevelDestination.HOME -> navController.navigateToHome(
topLevelNavOptions,
)

TopLevelDestination.HOME -> navController.navigateToHome(
TopLevelDestination.SCHEDULE -> navController.navigateToSchedule(
topLevelNavOptions,
)

Expand All @@ -224,7 +224,7 @@ class AtAppState(

fun loginNavigate() {
navController.popBackStack()
navController.navigateToSchedule()
navController.navigateToHome()
}

fun tokenInvalidNavigate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import coil.ImageLoader
import coil.compose.rememberAsyncImagePainter
import coil.decode.SvgDecoder
Expand Down Expand Up @@ -88,7 +89,10 @@ import com.wei.amazingtalker.feature.home.home.ui.StatusCard
*
*/
@Composable
internal fun HomeRoute() {
internal fun HomeRoute(
navController: NavController,
tokenInvalidNavigate: () -> Unit,
) {
HomeScreen()
}

Expand Down Expand Up @@ -310,12 +314,14 @@ fun loadImageUsingCoil(resId: Int, isPreview: Boolean): Painter {
private fun MenuButton(
onMenuClick: () -> Unit,
) {
val menu = stringResource(R.string.menu)

IconButton(
onClick = onMenuClick,
modifier = Modifier
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceVariant)
.semantics { contentDescription = "" },
.semantics { contentDescription = menu },
) {
Icon(
imageVector = AtIcons.Menu,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ fun NavController.navigateToHome(navOptions: NavOptions? = null) {
this.navigate(homeRoute, navOptions)
}

fun NavGraphBuilder.homeGraph() {
fun NavGraphBuilder.homeGraph(
navController: NavController,
tokenInvalidNavigate: () -> Unit,
) {
composable(route = homeRoute) {
HomeRoute()
HomeRoute(
navController = navController,
tokenInvalidNavigate = tokenInvalidNavigate,
)
}
}
2 changes: 2 additions & 0 deletions feature/home/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 首頁 -->
<string name="profile_picture">%s 的大頭貼</string>
<string name="menu">選單</string>
</resources>
2 changes: 2 additions & 0 deletions feature/home/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 首頁 -->
<string name="profile_picture">%s\'s profile picture</string>
<string name="menu">Menu</string>
</resources>

0 comments on commit a5a70cf

Please sign in to comment.