diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/StaffScreenRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/StaffScreenRobot.kt index 650d36dfd..874b8b2e5 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/StaffScreenRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/StaffScreenRobot.kt @@ -1,12 +1,21 @@ package io.github.droidkaigi.confsched.testing.robot +import androidx.compose.ui.test.assertContentDescriptionEquals import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.hasTestTag -import androidx.compose.ui.test.onChildren -import androidx.compose.ui.test.onFirst +import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.performScrollToIndex import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.model.Staff +import io.github.droidkaigi.confsched.model.fakes +import io.github.droidkaigi.confsched.staff.StaffItemTestTagPrefix import io.github.droidkaigi.confsched.staff.StaffScreen import io.github.droidkaigi.confsched.staff.StaffScreenLazyColumnTestTag +import io.github.droidkaigi.confsched.staff.component.StaffItemImageTestTag +import io.github.droidkaigi.confsched.staff.component.StaffItemUserNameTextTestTag +import io.github.droidkaigi.confsched.testing.utils.assertCountAtLeast +import io.github.droidkaigi.confsched.testing.utils.hasTestTag import javax.inject.Inject class StaffScreenRobot @Inject constructor( @@ -26,20 +35,81 @@ class StaffScreenRobot @Inject constructor( waitUntilIdle() } - fun checkExistsStaffItem() { + fun scrollToIndex10() { composeTestRule .onNode(hasTestTag(StaffScreenLazyColumnTestTag)) - .onChildren() - .onFirst() - .assertExists() - .assertIsDisplayed() + .performScrollToIndex(10) } - fun checkDoesNotExistsStaffItem() { + fun checkShowFirstAndSecondStaffs() { + checkRangeStaffItemsDisplayed( + fromTo = 0..2, + ) + } + + private fun checkRangeStaffItemsDisplayed( + fromTo: IntRange, + ) { + val staffList = Staff.fakes().subList(fromTo.first, fromTo.last) + staffList.forEach { staff -> + composeTestRule + .onNode(hasTestTag(StaffItemTestTagPrefix.plus(staff.id))) + .assertExists() + .assertIsDisplayed() + + composeTestRule + .onNode( + matcher = hasTestTag(StaffItemImageTestTag.plus(staff.username)), + useUnmergedTree = true, + ) + .assertExists() + .assertIsDisplayed() + .assertContentDescriptionEquals(staff.username) + + composeTestRule + .onNode( + matcher = hasTestTag(StaffItemUserNameTextTestTag.plus(staff.username)), + useUnmergedTree = true, + ) + .assertExists() + .assertIsDisplayed() + .assertTextEquals(staff.username) + } + } + + fun checkStaffItemsDisplayed() { + // Check there are two staffs composeTestRule - .onNode(hasTestTag(StaffScreenLazyColumnTestTag)) - .onChildren() - .onFirst() + .onAllNodes(hasTestTag(StaffItemTestTagPrefix, substring = true)) + .assertCountAtLeast(2) + } + + fun checkDoesNotFirstStaffItemDisplayed() { + val staff = Staff.fakes().first() + composeTestRule + .onNode(hasTestTag(StaffItemTestTagPrefix.plus(staff.id))) + .assertDoesNotExist() + + composeTestRule + .onNode( + matcher = hasTestTag(StaffItemImageTestTag.plus(staff.username)), + useUnmergedTree = true, + ) .assertDoesNotExist() + + composeTestRule + .onNode( + matcher = hasTestTag(StaffItemUserNameTextTestTag.plus(staff.username)), + useUnmergedTree = true, + ) + .assertDoesNotExist() + } + + fun checkErrorSnackbarDisplayed() { + composeTestRule + .onNode( + hasText("Fake IO Exception"), + useUnmergedTree = true, + ).assertIsDisplayed() } } diff --git a/feature/staff/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/staff/StaffScreenTest.kt b/feature/staff/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/staff/StaffScreenTest.kt index dbe5d789e..90229dab6 100644 --- a/feature/staff/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/staff/StaffScreenTest.kt +++ b/feature/staff/src/androidUnitTest/kotlin/io/github/droidkaigi/confsched/staff/StaffScreenTest.kt @@ -6,8 +6,7 @@ import io.github.droidkaigi.confsched.testing.DescribedBehavior import io.github.droidkaigi.confsched.testing.describeBehaviors import io.github.droidkaigi.confsched.testing.execute import io.github.droidkaigi.confsched.testing.robot.StaffScreenRobot -import io.github.droidkaigi.confsched.testing.robot.StaffServerRobot.ServerStatus.Error -import io.github.droidkaigi.confsched.testing.robot.StaffServerRobot.ServerStatus.Operational +import io.github.droidkaigi.confsched.testing.robot.StaffServerRobot import io.github.droidkaigi.confsched.testing.robot.runRobot import io.github.droidkaigi.confsched.testing.rules.RobotTestRule import org.junit.Rule @@ -41,36 +40,46 @@ class StaffScreenTest( return describeBehaviors(name = "StaffScreen") { describe("when server is operational") { run { - setupStaffServer(Operational) + setupStaffServer(StaffServerRobot.ServerStatus.Operational) } describe("when launch") { run { setupScreenContent() } - itShould("show staff screen") { - captureScreenWithChecks( - checks = { - checkExistsStaffItem() - }, - ) + itShould("show first and second staffs") { + captureScreenWithChecks { + checkShowFirstAndSecondStaffs() + } } - } - } - describe("when server is down") { - run { - setupStaffServer(Error) + describe("when scroll to index 10") { + run { + scrollToIndex10() + } + itShould("show staffs") { + captureScreenWithChecks { + checkStaffItemsDisplayed() + } + } + } } - describe("when launch") { + + describe("when server is down") { run { - setupScreenContent() + setupStaffServer(StaffServerRobot.ServerStatus.Error) } - itShould("show snackbar") { - captureScreenWithChecks( - checks = { - checkDoesNotExistsStaffItem() - }, - ) + describe("when launch") { + run { + setupScreenContent() + } + itShould("does not show staff and show snackbar") { + captureScreenWithChecks( + checks = { + checkDoesNotFirstStaffItemDisplayed() + checkErrorSnackbarDisplayed() + }, + ) + } } } } diff --git a/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/StaffScreen.kt b/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/StaffScreen.kt index eeee900dc..e6ea5e6fd 100644 --- a/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/StaffScreen.kt +++ b/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/StaffScreen.kt @@ -36,6 +36,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview const val staffScreenRoute = "staff" const val StaffScreenTestTag = "StaffScreenTestTag" const val StaffScreenLazyColumnTestTag = "StaffScreenLazyColumnTestTag" +const val StaffItemTestTagPrefix = "StaffItemTestTag:" fun NavGraphBuilder.staffScreens( onNavigationIconClick: () -> Unit, @@ -121,21 +122,23 @@ fun StaffScreen( modifier = Modifier .fillMaxSize() .padding(top = padding.calculateTopPadding()) - .testTag(StaffScreenLazyColumnTestTag) .let { if (scrollBehavior != null) { it.nestedScroll(scrollBehavior.nestedScrollConnection) } else { it } - }, + } + .testTag(StaffScreenLazyColumnTestTag), contentPadding = PaddingValues(bottom = padding.calculateBottomPadding()), ) { items(uiState.staff) { staff -> StaffItem( staff = staff, onStaffItemClick = onStaffItemClick, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .testTag(StaffItemTestTagPrefix.plus(staff.id)), ) } } diff --git a/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/component/StaffItem.kt b/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/component/StaffItem.kt index 02e9a444d..24bcfd60f 100644 --- a/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/component/StaffItem.kt +++ b/feature/staff/src/commonMain/kotlin/io/github/droidkaigi/confsched/staff/component/StaffItem.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme @@ -29,6 +30,9 @@ import org.jetbrains.compose.ui.tooling.preview.Preview private val staffIconShape = CircleShape +const val StaffItemImageTestTag = "StaffItemImageTestTag:" +const val StaffItemUserNameTextTestTag = "StaffItemUserNameTextTestTag:" + @Composable fun StaffItem( staff: Staff, @@ -48,7 +52,7 @@ fun StaffItem( painter = previewOverride(previewPainter = { rememberVectorPainter(image = Icons.Default.Person) }) { rememberAsyncImagePainter(staff.iconUrl) }, - contentDescription = null, + contentDescription = staff.username, modifier = Modifier .size(52.dp) .clip(staffIconShape) @@ -56,13 +60,15 @@ fun StaffItem( width = 1.dp, color = MaterialTheme.colorScheme.outline, shape = staffIconShape, - ), + ) + .testTag(StaffItemImageTestTag.plus(staff.username)), ) Text( text = staff.username, style = MaterialTheme.typography.bodyLarge, maxLines = 2, overflow = TextOverflow.Ellipsis, + modifier = Modifier.testTag(StaffItemUserNameTextTestTag.plus(staff.username)), ) } }