Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

♻️ Enhanced ContributorsScreen testing. #503

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public data class Contributor(
public companion object
}

public fun Contributor.Companion.fakes(): PersistentList<Contributor> = (0..20)
public fun Contributor.Companion.fakes(): PersistentList<Contributor> = (1..20)
.map {
Contributor(
id = it,
username = it.toString(),
username = "username $it",
profileUrl = "https://developer.android.com/",
iconUrl = "https://placehold.jp/150x150.png",
)
Expand Down
1 change: 1 addition & 0 deletions core/testing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
implementation(projects.feature.sponsors)
implementation(projects.feature.favorites)
implementation(projects.feature.eventmap)
implementation(projects.feature.contributors)
implementation(libs.daggerHiltAndroidTesting)
implementation(libs.roborazzi)
implementation(libs.kermit)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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.hasText
import io.github.droidkaigi.confsched.contributors.ContributorsItemTestTag
import io.github.droidkaigi.confsched.contributors.ContributorsScreen
import io.github.droidkaigi.confsched.contributors.component.ContributorsItemImageTestTag
import io.github.droidkaigi.confsched.contributors.component.ContributorsUserNameTextTestTag
import io.github.droidkaigi.confsched.model.Contributor
import io.github.droidkaigi.confsched.model.fakes
import io.github.droidkaigi.confsched.ui.Inject

class ContributorsScreenRobot @Inject constructor(
screenRobot: DefaultScreenRobot,
contributorsServerRobot: DefaultContributorsServerRobot,
) : ScreenRobot by screenRobot,
ContributorsServerRobot by contributorsServerRobot {
fun setupScreenContent() {
robotTestRule.setContent {
ContributorsScreen(
onNavigationIconClick = { },
onContributorsItemClick = { },
)
}
}

fun checkExistsContributorItem(
fromTo: Pair<Int, Int>,
) {
val contributorsList = Contributor.fakes().subList(fromTo.first, fromTo.second)
contributorsList.forEach { contributor ->
composeTestRule
.onNode(hasTestTag(ContributorsItemTestTag.plus(contributor.id)))
.assertExists()
.assertIsDisplayed()

composeTestRule
.onNode(
matcher = hasTestTag(ContributorsItemImageTestTag.plus(contributor.username)),
useUnmergedTree = true,
)
.assertExists()
.assertIsDisplayed()
.assertContentDescriptionEquals(contributor.username)

composeTestRule
.onNode(
matcher = hasTestTag(ContributorsUserNameTextTestTag.plus(contributor.username)),
useUnmergedTree = true,
)
.assertExists()
.assertIsDisplayed()
.assertTextEquals(contributor.username)
}
}

fun checkDoesNotExistsContributorItem() {
val contributor = Contributor.fakes().first()
composeTestRule
.onNode(hasTestTag(ContributorsItemTestTag.plus(contributor.id)))
.assertDoesNotExist()

composeTestRule
.onNode(
matcher = hasTestTag(ContributorsItemImageTestTag.plus(contributor.username)),
useUnmergedTree = true,
)
.assertDoesNotExist()

composeTestRule
.onNode(
matcher = hasTestTag(ContributorsUserNameTextTestTag.plus(contributor.username)),
useUnmergedTree = true,
)
.assertDoesNotExist()
}

fun checkErrorSnackbarDisplayed() {
composeTestRule
.onNode(
hasText("Fake IO Exception"),
useUnmergedTree = true,
).assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
package io.github.droidkaigi.confsched.contributors

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidTest
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.ContributorsScreenRobot
import io.github.droidkaigi.confsched.testing.robot.ContributorsServerRobot
import io.github.droidkaigi.confsched.testing.robot.DefaultContributorsServerRobot
import io.github.droidkaigi.confsched.testing.robot.DefaultScreenRobot
import io.github.droidkaigi.confsched.testing.robot.ScreenRobot
import io.github.droidkaigi.confsched.testing.robot.runRobot
import io.github.droidkaigi.confsched.testing.rules.RobotTestRule
import org.junit.Rule
Expand Down Expand Up @@ -42,58 +37,43 @@ class ContributorsScreenTest(private val testCase: DescribedBehavior<Contributor
@ParameterizedRobolectricTestRunner.Parameters(name = "{0}")
fun behaviors(): List<DescribedBehavior<ContributorsScreenRobot>> {
return describeBehaviors<ContributorsScreenRobot>(name = "ContributorsScreen") {
describe("when launch") {
describe("when server is operational") {
run {
setupContributorServer(ContributorsServerRobot.ServerStatus.Operational)
setupScreenContent()
}
itShould("show contributors list") {
captureScreenWithChecks(
checks = { checkContributorsDisplayed() },
)
describe("when launch") {
run {
setupScreenContent()
}
itShould("show contributor two or more") {
captureScreenWithChecks {
checkExistsContributorItem(
fromTo = 0 to 2,
)
}
}
}
}
describe("when launch with error") {
run {
setupContributorServer(ContributorsServerRobot.ServerStatus.Error)
setupScreenContent()
}
itShould("show error message") {
captureScreenWithChecks(
checks = { checkErrorSnackbarDisplayed() },
)

describe("when server is down") {
run {
setupContributorServer(ContributorsServerRobot.ServerStatus.Error)
}
describe("when launch") {
run {
setupScreenContent()
}
itShould("does not show contributor, and show snackbar") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The , is here 👀

captureScreenWithChecks(
checks = {
checkDoesNotExistsContributorItem()
checkErrorSnackbarDisplayed()
},
)
}
}
}
}
}
}
}
}

class ContributorsScreenRobot @Inject constructor(
screenRobot: DefaultScreenRobot,
contributorsServerRobot: DefaultContributorsServerRobot,
) : ScreenRobot by screenRobot,
ContributorsServerRobot by contributorsServerRobot {
fun setupScreenContent() {
robotTestRule.setContent {
ContributorsScreen(
onNavigationIconClick = { },
onContributorsItemClick = { },
)
}
}

fun checkContributorsDisplayed() {
composeTestRule
.onNode(hasTestTag(ContributorsScreenTestTag))
.assertIsDisplayed()
}

fun checkErrorSnackbarDisplayed() {
composeTestRule
.onNode(
hasText("Fake IO Exception"),
useUnmergedTree = true,
).assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import kotlinx.collections.immutable.PersistentList

const val contributorsScreenRoute = "contributors"
const val ContributorsScreenTestTag = "ContributorsScreenTestTag"
const val ContributorsItemTestTag = "ContributorsItemTestTag:"

fun NavGraphBuilder.contributorsScreens(
onNavigationIconClick: () -> Unit,
Expand Down Expand Up @@ -156,7 +157,9 @@ private fun Contributors(
ContributorsItem(
contributor = it,
onClick = onContributorsItemClick,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier
.fillMaxWidth()
.testTag(ContributorsItemTestTag.plus(it.id)),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ 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.model.Contributor
import io.github.droidkaigi.confsched.ui.previewOverride
import io.github.droidkaigi.confsched.ui.rememberAsyncImagePainter

const val ContributorsItemImageTestTag = "ContributorsItemImageTestTag:"
const val ContributorsUserNameTextTestTag = "ContributorsUserNameTextTestTag:"

private val contributorIconShape = CircleShape

@Composable
Expand All @@ -44,21 +48,23 @@ fun ContributorsItem(
painter = previewOverride(previewPainter = { rememberVectorPainter(image = Icons.Default.Person) }) {
rememberAsyncImagePainter(contributor.iconUrl)
},
contentDescription = null,
contentDescription = contributor.username,
modifier = Modifier
.size(52.dp)
.clip(contributorIconShape)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.outline,
shape = contributorIconShape,
),
)
.testTag(ContributorsItemImageTestTag.plus(contributor.username)),
)
Text(
text = contributor.username,
style = MaterialTheme.typography.bodyLarge,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.testTag(ContributorsUserNameTextTestTag.plus(contributor.username)),
)
}
}
Loading