From 9045b06c027f86a2ac6a043e09eecdecddac51ed Mon Sep 17 00:00:00 2001 From: JanFidor Date: Sun, 6 Nov 2022 18:42:04 +0100 Subject: [PATCH] add robot for topic feature ui tests --- .../nowinandroid/feature/topic/TopicRobot.kt | 70 +++++++++ .../feature/topic/TopicScreenTest.kt | 135 +++++++----------- 2 files changed, 120 insertions(+), 85 deletions(-) create mode 100644 feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicRobot.kt diff --git a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicRobot.kt b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicRobot.kt new file mode 100644 index 000000000..06fc4d43b --- /dev/null +++ b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicRobot.kt @@ -0,0 +1,70 @@ +/* + * 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.topic + +import androidx.activity.ComponentActivity +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.FollowableTopic +import com.google.samples.apps.nowinandroid.core.model.data.NewsResource + +internal class TopicRobot( + private val composeTestRule: AndroidComposeTestRule, ComponentActivity> +) { + private val topicLoading = composeTestRule.activity.resources.getString(R.string.topic_loading) + + fun setContent(topicUiState: TopicUiState, newsUiState: NewsUiState) { + composeTestRule.setContent { + TopicScreen( + topicUiState = topicUiState, + newsUiState = newsUiState, + onBackClick = { }, + onFollowClick = { }, + onBookmarkChanged = { _, _ -> }, + ) + } + } + + fun loadingIndicatorExists() { + composeTestRule + .onNodeWithContentDescription(topicLoading) + .assertExists() + } + + fun topicExists(topic: FollowableTopic) { + composeTestRule + .onNodeWithText(topic.topic.name) + .assertExists() + + composeTestRule + .onNodeWithText(topic.topic.longDescription) + .assertExists() + } + + fun scrollToNewsResource(newsResource: NewsResource) { + composeTestRule + .onAllNodes(hasScrollToNodeAction()) + .onFirst() + .performScrollToNode(hasText(newsResource.title)) + } +} \ No newline at end of file diff --git a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index 0f20a5e5e..1df807635 100644 --- a/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -17,20 +17,15 @@ package com.google.samples.apps.nowinandroid.feature.topic import androidx.activity.ComponentActivity -import androidx.compose.ui.test.hasScrollToNodeAction -import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.junit4.AndroidComposeTestRule import androidx.compose.ui.test.junit4.createAndroidComposeRule -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.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video import com.google.samples.apps.nowinandroid.core.model.data.Topic import kotlinx.datetime.Instant -import org.junit.Before import org.junit.Rule import org.junit.Test @@ -44,109 +39,79 @@ class TopicScreenTest { @get:Rule val composeTestRule = createAndroidComposeRule() - private lateinit var topicLoading: String - - @Before - fun setup() { - composeTestRule.activity.apply { - topicLoading = getString(R.string.topic_loading) - } - } - @Test fun niaLoadingWheel_whenScreenIsLoading_showLoading() { - composeTestRule.setContent { - TopicScreen( - topicUiState = TopicUiState.Loading, - newsUiState = NewsUiState.Loading, - onBackClick = { }, - onFollowClick = { }, - onBookmarkChanged = { _, _ -> }, - ) + launchTopicRobot( + composeTestRule, + TopicUiState.Loading, + NewsUiState.Loading + ) { + loadingIndicatorExists() } - - composeTestRule - .onNodeWithContentDescription(topicLoading) - .assertExists() } @Test fun topicTitle_whenTopicIsSuccess_isShown() { val testTopic = testTopics.first() - composeTestRule.setContent { - TopicScreen( - topicUiState = TopicUiState.Success(testTopic), - newsUiState = NewsUiState.Loading, - onBackClick = { }, - onFollowClick = { }, - onBookmarkChanged = { _, _ -> }, - ) - } - - // Name is shown - composeTestRule - .onNodeWithText(testTopic.topic.name) - .assertExists() - // Description is shown - composeTestRule - .onNodeWithText(testTopic.topic.longDescription) - .assertExists() + launchTopicRobot( + composeTestRule, + TopicUiState.Success(testTopic), + NewsUiState.Loading + ) { + topicExists(testTopic) + } } @Test fun news_whenTopicIsLoading_isNotShown() { - composeTestRule.setContent { - TopicScreen( - topicUiState = TopicUiState.Loading, - newsUiState = NewsUiState.Success( - sampleNewsResources.mapIndexed { index, newsResource -> - SaveableNewsResource( - newsResource = newsResource, - isSaved = index % 2 == 0, - ) - } - ), - onBackClick = { }, - onFollowClick = { }, - onBookmarkChanged = { _, _ -> }, + launchTopicRobot( + composeTestRule, + TopicUiState.Loading, + NewsUiState.Success( + sampleNewsResources.mapIndexed { index, newsResource -> + SaveableNewsResource( + newsResource = newsResource, + isSaved = index % 2 == 0, + ) + } ) + ) { + loadingIndicatorExists() } - - // Loading indicator shown - composeTestRule - .onNodeWithContentDescription(topicLoading) - .assertExists() } @Test fun news_whenSuccessAndTopicIsSuccess_isShown() { val testTopic = testTopics.first() - composeTestRule.setContent { - TopicScreen( - topicUiState = TopicUiState.Success(testTopic), - newsUiState = NewsUiState.Success( - sampleNewsResources.mapIndexed { index, newsResource -> - SaveableNewsResource( - newsResource = newsResource, - isSaved = index % 2 == 0, - ) - } - ), - onBackClick = { }, - onFollowClick = { }, - onBookmarkChanged = { _, _ -> }, + launchTopicRobot( + composeTestRule, + TopicUiState.Success(testTopic), + NewsUiState.Success( + sampleNewsResources.mapIndexed { index, newsResource -> + SaveableNewsResource( + newsResource = newsResource, + isSaved = index % 2 == 0, + ) + } ) + ) { + // Scroll to first news title if available + scrollToNewsResource(sampleNewsResources.first()) } - - // Scroll to first news title if available - composeTestRule - .onAllNodes(hasScrollToNodeAction()) - .onFirst() - .performScrollToNode(hasText(sampleNewsResources.first().title)) } } +private fun launchTopicRobot( + composeTestRule: AndroidComposeTestRule, ComponentActivity>, + topicUiState: TopicUiState, + newsUiState: NewsUiState, + func: TopicRobot.() -> Unit +) = TopicRobot(composeTestRule).apply { + setContent(topicUiState, newsUiState) + func() +} + private const val TOPIC_1_NAME = "Headlines" private const val TOPIC_2_NAME = "UI" private const val TOPIC_3_NAME = "Tools"