From f56e5b65e6e3cac5971ef53bffc3b9eff95743bb Mon Sep 17 00:00:00 2001 From: Jonathan Koren Date: Thu, 27 Jun 2024 10:12:10 -0700 Subject: [PATCH] Instrumented tests for InterestsListDetailScreen Change-Id: I0325cea93bf1e1dc0b8d1fc56434d5413caf6fea --- .../apps/nowinandroid/ui/NavigationTest.kt | 6 - .../apps/nowinandroid/ui/UiTestExtentions.kt | 24 ++ .../InterestsListDetailScreenTest.kt | 205 ++++++++++++++++++ 3 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtentions.kt create mode 100644 app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index 93c674bcc..b15024cc7 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -16,13 +16,11 @@ package com.google.samples.apps.nowinandroid.ui -import androidx.annotation.StringRes import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.hasTestTag 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.onAllNodesWithText import androidx.compose.ui.test.onNodeWithContentDescription @@ -47,7 +45,6 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder import javax.inject.Inject -import kotlin.properties.ReadOnlyProperty import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR import com.google.samples.apps.nowinandroid.feature.search.R as FeatureSearchR @@ -88,9 +85,6 @@ class NavigationTest { @Inject lateinit var topicsRepository: TopicsRepository - private fun AndroidComposeTestRule<*, *>.stringResource(@StringRes resId: Int) = - ReadOnlyProperty { _, _ -> activity.getString(resId) } - // The strings used for matching in these tests private val navigateUp by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_navigate_up) private val forYou by composeTestRule.stringResource(FeatureForyouR.string.feature_foryou_title) diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtentions.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtentions.kt new file mode 100644 index 000000000..16c269c3a --- /dev/null +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/UiTestExtentions.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2024 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.ui + +import androidx.annotation.StringRes +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import kotlin.properties.ReadOnlyProperty + +fun AndroidComposeTestRule<*, *>.stringResource(@StringRes resId: Int) = + ReadOnlyProperty { _, _ -> activity.getString(resId) } diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt new file mode 100644 index 000000000..b94d543dd --- /dev/null +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/interests2pane/InterestsListDetailScreenTest.kt @@ -0,0 +1,205 @@ +/* + * Copyright 2024 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.ui.interests2pane + +import androidx.compose.ui.test.DeviceConfigurationOverride +import androidx.compose.ui.test.ForcedSize +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.test.espresso.Espresso +import androidx.test.espresso.NoActivityResumedException +import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.ui.stringResource +import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import javax.inject.Inject +import kotlin.test.assertFailsWith +import com.google.samples.apps.nowinandroid.feature.topic.R as FeatureTopicR + +@HiltAndroidTest +class InterestsListDetailScreenTest { + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @BindValue + @get:Rule(order = 1) + val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() + + @get:Rule(order = 2) + val composeTestRule = createAndroidComposeRule() + + @Inject + lateinit var topicsRepository: TopicsRepository + + // The strings used for matching in these tests. + private val placeholderText by composeTestRule.stringResource(FeatureTopicR.string.feature_topic_select_an_interest) + private val listPaneTag = "interests:topics" + + // Overrides for device sizes. + private val expandedWidth = DeviceConfigurationOverride.ForcedSize(DpSize(1200.dp, 840.dp)) + private val compactWidth = DeviceConfigurationOverride.ForcedSize(DpSize(412.dp, 915.dp)) + + private val Topic.testTag + get() = "topic:${this.id}" + + @Before + fun setup() { + hiltRule.inject() + composeTestRule.activityRule + } + + @Test + fun expandedWidth_initialState_showsTwoPanesWithPlaceholder() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = expandedWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsDisplayed() + } + } + + @Test + fun notExpandedWidth_initialState_showsListPane() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = compactWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + } + } + + @Test + fun expandedWidth_topicSelected_updatesDetailPane() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = expandedWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + val firstTopic = runBlocking { + topicsRepository.getTopics().first().first() + } + onNodeWithText(firstTopic.name).performClick() + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsDisplayed() + } + } + + @Test + fun notExpandedWidth_topicSelected_showsTopicDetailPane() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = compactWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + val firstTopic = runBlocking { + topicsRepository.getTopics().first().first() + } + onNodeWithText(firstTopic.name).performClick() + + onNodeWithTag(listPaneTag).assertIsNotDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsDisplayed() + } + } + + @Test + fun expandedWidth_backPressFromTopicDetail_leavesInterests() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = expandedWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + val firstTopic = runBlocking { + topicsRepository.getTopics().first().first() + } + onNodeWithText(firstTopic.name).performClick() + + assertFailsWith(NoActivityResumedException::class) { + // Back would exit the app, which causes Espresso to throw this exception. + Espresso.pressBack() + } + } + } + + @Test + fun notExpandedWidth_backPressFromTopicDetail_showsListPane() { + composeTestRule.apply { + setContent { + DeviceConfigurationOverride(override = compactWidth) { + NiaTheme { + InterestsListDetailScreen() + } + } + } + + val firstTopic = runBlocking { + topicsRepository.getTopics().first().first() + } + onNodeWithText(firstTopic.name).performClick() + composeTestRule.waitForIdle() + + Espresso.pressBack() + composeTestRule.waitForIdle() + + onNodeWithTag(listPaneTag).assertIsDisplayed() + onNodeWithText(placeholderText).assertIsNotDisplayed() + onNodeWithTag(firstTopic.testTag).assertIsNotDisplayed() + } + } +}