add robot for ForYou feature ui tests

pull/402/head
JanFidor 3 years ago
parent 326eb35cf8
commit 3f61d04b49

@ -0,0 +1,123 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.feature.foryou
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasScrollToNodeAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToNode
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.feature.foryou.R.string
internal class ForYouRobot(
private val composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>
) {
private val doneButtonMatcher by lazy {
hasText(composeTestRule.activity.resources.getString(string.done))
}
fun setContent(
isSyncing: Boolean,
interestsSelectionState: ForYouInterestsSelectionUiState,
feedState: NewsFeedUiState,
) {
composeTestRule.setContent {
BoxWithConstraints {
ForYouScreen(
isSyncing = isSyncing,
interestsSelectionState = interestsSelectionState,
feedState = feedState,
onTopicCheckedChanged = { _, _ -> },
onAuthorCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
}
}
fun loadingIndicatorExists() {
composeTestRule
.onNodeWithContentDescription(
composeTestRule.activity.resources.getString(string.for_you_loading)
)
.assertExists()
}
fun clickableAuthorExists(author: FollowableAuthor) {
composeTestRule
.onNodeWithText(author.author.name)
.assertExists()
.assertHasClickAction()
}
fun clickableTopicExists(topic: FollowableTopic) {
composeTestRule
.onNodeWithText(topic.topic.name)
.assertExists()
.assertHasClickAction()
}
fun scrollToDoneButton() {
composeTestRule
.onAllNodes(hasScrollToNodeAction())
.onFirst()
.performScrollToNode(doneButtonMatcher)
}
fun clickableDoneButtonExists(isEnabled: Boolean) {
composeTestRule
.onNode(doneButtonMatcher)
.assertExists()
.assertHasClickAction()
.apply {
if (isEnabled) assertIsEnabled() else assertIsNotEnabled()
}
}
fun clickableNewsResourceExists(newsResource: NewsResource) {
composeTestRule
.onNodeWithText(
newsResource.title,
substring = true
)
.assertExists()
.assertHasClickAction()
}
fun scrollToNewsResource(newsResource: NewsResource) {
composeTestRule.onNode(hasScrollToNodeAction())
.performScrollToNode(
hasText(
newsResource.title,
substring = true
)
)
}
}

@ -17,17 +17,9 @@
package com.google.samples.apps.nowinandroid.feature.foryou package com.google.samples.apps.nowinandroid.feature.foryou
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.hasScrollToNodeAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onFirst import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToNode
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
@ -42,114 +34,60 @@ class ForYouScreenTest {
@get:Rule @get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>() val composeTestRule = createAndroidComposeRule<ComponentActivity>()
private val doneButtonMatcher by lazy {
hasText(
composeTestRule.activity.resources.getString(R.string.done)
)
}
@Test @Test
fun circularProgressIndicator_whenScreenIsLoading_exists() { fun circularProgressIndicator_whenScreenIsLoading_exists() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = OnboardingUiState.Loading, onboardingState = OnboardingUiState.Loading,
feedState = NewsFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> }, ) {
onAuthorCheckedChanged = { _, _ -> }, loadingIndicatorExists()
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
} }
} }
composeTestRule
.onNodeWithContentDescription(
composeTestRule.activity.resources.getString(R.string.for_you_loading)
)
.assertExists()
}
@Test @Test
fun circularProgressIndicator_whenScreenIsSyncing_exists() { fun circularProgressIndicator_whenScreenIsSyncing_exists() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = true, isSyncing = true,
onboardingUiState = OnboardingUiState.NotShown, onboardingState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success(emptyList()), feedState = NewsFeedUiState.Success(emptyList()),
onTopicCheckedChanged = { _, _ -> }, ) {
onAuthorCheckedChanged = { _, _ -> }, loadingIndicatorExists()
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
} }
composeTestRule
.onNodeWithContentDescription(
composeTestRule.activity.resources.getString(R.string.for_you_loading)
)
.assertExists()
} }
@Test @Test
fun topicSelector_whenNoTopicsSelected_showsAuthorAndTopicChipsAndDisabledDoneButton() { fun topicSelector_whenNoTopicsSelected_showsAuthorAndTopicChipsAndDisabledDoneButton() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingState = OnboardingUiState.Shown(
OnboardingUiState.Shown(
topics = testTopics, topics = testTopics,
authors = testAuthors authors = testAuthors
), ),
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(emptyList()),
feed = emptyList() ) {
), testAuthors.forEach { author ->
onTopicCheckedChanged = { _, _ -> }, clickableAuthorExists(author)
onAuthorCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
} }
testAuthors.forEach { testAuthor -> testTopics.forEach { topic ->
composeTestRule clickableTopicExists(topic)
.onNodeWithText(testAuthor.author.name)
.assertExists()
.assertHasClickAction()
} }
testTopics.forEach { testTopic -> scrollToDoneButton()
composeTestRule clickableDoneButtonExists(false)
.onNodeWithText(testTopic.topic.name)
.assertExists()
.assertHasClickAction()
} }
// Scroll until the Done button is visible
composeTestRule
.onAllNodes(hasScrollToNodeAction())
.onFirst()
.performScrollToNode(doneButtonMatcher)
composeTestRule
.onNode(doneButtonMatcher)
.assertExists()
.assertIsNotEnabled()
.assertHasClickAction()
} }
@Test @Test
fun topicSelector_whenSomeTopicsSelected_showsAuthorAndTopicChipsAndEnabledDoneButton() { fun topicSelector_whenSomeTopicsSelected_showsAuthorAndTopicChipsAndEnabledDoneButton() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingState =
OnboardingUiState.Shown( OnboardingUiState.Shown(
// Follow one topic // Follow one topic
topics = testTopics.mapIndexed { index, testTopic -> topics = testTopics.mapIndexed { index, testTopic ->
@ -157,188 +95,104 @@ class ForYouScreenTest {
}, },
authors = testAuthors authors = testAuthors
), ),
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(emptyList()),
feed = emptyList() ) {
), testAuthors.forEach { author ->
onTopicCheckedChanged = { _, _ -> }, clickableAuthorExists(author)
onAuthorCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
} }
testAuthors.forEach { testAuthor -> testTopics.forEach { topic ->
composeTestRule clickableTopicExists(topic)
.onNodeWithText(testAuthor.author.name)
.assertExists()
.assertHasClickAction()
} }
testTopics.forEach { testTopic -> scrollToDoneButton()
composeTestRule clickableDoneButtonExists(true)
.onNodeWithText(testTopic.topic.name)
.assertExists()
.assertHasClickAction()
} }
// Scroll until the Done button is visible
composeTestRule
.onAllNodes(hasScrollToNodeAction())
.onFirst()
.performScrollToNode(doneButtonMatcher)
composeTestRule
.onNode(doneButtonMatcher)
.assertExists()
.assertIsEnabled()
.assertHasClickAction()
} }
@Test @Test
fun topicSelector_whenSomeAuthorsSelected_showsAuthorAndTopicChipsAndEnabledDoneButton() { fun topicSelector_whenSomeAuthorsSelected_showsAuthorAndTopicChipsAndEnabledDoneButton() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingState = OnboardingUiState.Shown(
OnboardingUiState.Shown(
// Follow one topic // Follow one topic
topics = testTopics, topics = testTopics,
authors = testAuthors.mapIndexed { index, testAuthor -> authors = testAuthors.mapIndexed { index, testAuthor ->
testAuthor.copy(isFollowed = index == 1) testAuthor.copy(isFollowed = index == 1)
} }
), ),
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(emptyList()),
feed = emptyList() ) {
), testAuthors.forEach { author ->
onTopicCheckedChanged = { _, _ -> }, clickableAuthorExists(author)
onAuthorCheckedChanged = { _, _ -> },
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
} }
testAuthors.forEach { testAuthor -> testTopics.forEach { topic ->
composeTestRule clickableTopicExists(topic)
.onNodeWithText(testAuthor.author.name)
.assertExists()
.assertHasClickAction()
} }
testTopics.forEach { testTopic -> scrollToDoneButton()
composeTestRule clickableDoneButtonExists(true)
.onNodeWithText(testTopic.topic.name)
.assertExists()
.assertHasClickAction()
} }
// Scroll until the Done button is visible
composeTestRule
.onAllNodes(hasScrollToNodeAction())
.onFirst()
.performScrollToNode(doneButtonMatcher)
composeTestRule
.onNode(doneButtonMatcher)
.assertExists()
.assertIsEnabled()
.assertHasClickAction()
} }
@Test @Test
fun feed_whenInterestsSelectedAndLoading_showsLoadingIndicator() { fun feed_whenInterestsSelectedAndLoading_showsLoadingIndicator() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = onboardingState =
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = testTopics, topics = testTopics,
authors = testAuthors authors = testAuthors
), ),
feedState = NewsFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> }, ) {
onAuthorCheckedChanged = { _, _ -> }, loadingIndicatorExists()
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
}
} }
composeTestRule
.onNodeWithContentDescription(
composeTestRule.activity.resources.getString(R.string.for_you_loading)
)
.assertExists()
} }
@Test @Test
fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() { fun feed_whenNoInterestsSelectionAndLoading_showsLoadingIndicator() {
composeTestRule.setContent { launchForYouRobot(
BoxWithConstraints { composeTestRule = composeTestRule,
ForYouScreen(
isSyncing = false, isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown, onboardingState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Loading, feedState = NewsFeedUiState.Loading,
onTopicCheckedChanged = { _, _ -> }, ) {
onAuthorCheckedChanged = { _, _ -> }, loadingIndicatorExists()
saveFollowedTopics = {},
onNewsResourcesCheckedChanged = { _, _ -> }
)
} }
} }
composeTestRule
.onNodeWithContentDescription(
composeTestRule.activity.resources.getString(R.string.for_you_loading)
)
.assertExists()
}
@Test @Test
fun feed_whenNoInterestsSelectionAndLoaded_showsFeed() { fun feed_whenNoInterestsSelectionAndLoaded_showsFeed() {
composeTestRule.setContent { launchForYouRobot(
ForYouScreen( composeTestRule = composeTestRule,
isSyncing = false, isSyncing = false,
onboardingUiState = OnboardingUiState.NotShown, onboardingState = OnboardingUiState.NotShown,
feedState = NewsFeedUiState.Success( feedState = NewsFeedUiState.Success(
feed = previewNewsResources.map { feed = previewNewsResources.map {
SaveableNewsResource(it, false) SaveableNewsResource(it, false)
} }
), ),
onTopicCheckedChanged = { _, _ -> }, ) {
onAuthorCheckedChanged = { _, _ -> }, clickableNewsResourceExists(previewNewsResources[0])
saveFollowedTopics = {}, scrollToNewsResource(previewNewsResources[1])
onNewsResourcesCheckedChanged = { _, _ -> } clickableNewsResourceExists(previewNewsResources[1])
) }
} }
composeTestRule
.onNodeWithText(
previewNewsResources[0].title,
substring = true
)
.assertExists()
.assertHasClickAction()
composeTestRule.onNode(hasScrollToNodeAction())
.performScrollToNode(
hasText(
previewNewsResources[1].title,
substring = true
)
)
composeTestRule
.onNodeWithText(
previewNewsResources[1].title,
substring = true
)
.assertExists()
.assertHasClickAction()
} }
private fun launchForYouRobot(
composeTestRule: AndroidComposeTestRule<ActivityScenarioRule<ComponentActivity>, ComponentActivity>,
isSyncing: Boolean,
onboardingState: OnboardingUiState,
feedState: NewsFeedUiState,
func: ForYouRobot.() -> Unit
) = ForYouRobot(composeTestRule).apply {
setContent(isSyncing, onboardingState, feedState)
func()
} }
private val testTopic = Topic( private val testTopic = Topic(

Loading…
Cancel
Save