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,305 +34,167 @@ 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, onboardingState = OnboardingUiState.Loading,
onboardingUiState = OnboardingUiState.Loading, feedState = NewsFeedUiState.Loading,
feedState = NewsFeedUiState.Loading, ) {
onTopicCheckedChanged = { _, _ -> }, loadingIndicatorExists()
onAuthorCheckedChanged = { _, _ -> },
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, onboardingState = OnboardingUiState.NotShown,
onboardingUiState = OnboardingUiState.NotShown, feedState = NewsFeedUiState.Success(emptyList()),
feedState = NewsFeedUiState.Success(emptyList()), ) {
onTopicCheckedChanged = { _, _ -> }, loadingIndicatorExists()
onAuthorCheckedChanged = { _, _ -> },
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, onboardingState = OnboardingUiState.Shown(
onboardingUiState = topics = testTopics,
OnboardingUiState.Shown( authors = testAuthors
topics = testTopics, ),
authors = testAuthors feedState = NewsFeedUiState.Success(emptyList()),
), ) {
feedState = NewsFeedUiState.Success( testAuthors.forEach { author ->
feed = emptyList() clickableAuthorExists(author)
),
onTopicCheckedChanged = { _, _ -> },
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, onboardingState =
onboardingUiState = OnboardingUiState.Shown(
OnboardingUiState.Shown( // Follow one topic
// Follow one topic topics = testTopics.mapIndexed { index, testTopic ->
topics = testTopics.mapIndexed { index, testTopic -> testTopic.copy(isFollowed = index == 1)
testTopic.copy(isFollowed = index == 1) },
}, authors = testAuthors
authors = testAuthors ),
), feedState = NewsFeedUiState.Success(emptyList()),
feedState = NewsFeedUiState.Success( ) {
feed = emptyList() testAuthors.forEach { author ->
), clickableAuthorExists(author)
onTopicCheckedChanged = { _, _ -> },
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, onboardingState = OnboardingUiState.Shown(
onboardingUiState = // Follow one topic
OnboardingUiState.Shown( topics = testTopics,
// Follow one topic authors = testAuthors.mapIndexed { index, testAuthor ->
topics = testTopics, testAuthor.copy(isFollowed = index == 1)
authors = testAuthors.mapIndexed { index, testAuthor -> }
testAuthor.copy(isFollowed = index == 1) ),
} feedState = NewsFeedUiState.Success(emptyList()),
), ) {
feedState = NewsFeedUiState.Success( testAuthors.forEach { author ->
feed = emptyList() clickableAuthorExists(author)
),
onTopicCheckedChanged = { _, _ -> },
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, onboardingState =
onboardingUiState = OnboardingUiState.Shown(
OnboardingUiState.Shown( topics = testTopics,
topics = testTopics, authors = testAuthors
authors = testAuthors ),
), feedState = NewsFeedUiState.Loading,
feedState = NewsFeedUiState.Loading, ) {
onTopicCheckedChanged = { _, _ -> }, loadingIndicatorExists()
onAuthorCheckedChanged = { _, _ -> },
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, onboardingState = OnboardingUiState.NotShown,
onboardingUiState = OnboardingUiState.NotShown, feedState = NewsFeedUiState.Loading,
feedState = NewsFeedUiState.Loading, ) {
onTopicCheckedChanged = { _, _ -> }, loadingIndicatorExists()
onAuthorCheckedChanged = { _, _ -> },
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(
id = "", id = "",
name = "", name = "",

Loading…
Cancel
Save