diff --git a/.github/workflows/Build.yaml b/.github/workflows/Build.yaml index 643ad0b92..30b2fd04c 100644 --- a/.github/workflows/Build.yaml +++ b/.github/workflows/Build.yaml @@ -105,32 +105,43 @@ jobs: disable_globbing: true commit_message: "🤖 Updates baselines for Dependency Guard" - - name: Run all local screenshot tests (Roborazzi) + - name: Run Android screenshot tests (Roborazzi) id: screenshotsverify continue-on-error: true run: ./gradlew verifyRoborazziDebug + - name: Run JVM Desktop screenshot tests (Roborazzi) + id: screenshotsverify_jvm + continue-on-error: true + run: ./gradlew verifyRoborazziJvm + - name: Prevent pushing new screenshots if this is a fork id: checkfork_screenshots continue-on-error: false - if: steps.screenshotsverify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository + if: (steps.screenshotsverify.outcome == 'failure' || steps.screenshotsverify_jvm.outcome == 'failure') && github.event.pull_request.head.repo.full_name != github.repository run: | - echo "::error::Screenshot tests failed, please create a PR in your fork first." + echo "::error::Screenshot tests failed, please create a PR in your fork first." echo "Your fork's CI will take screenshots for your fork." exit 1 # Runs if previous job failed - - name: Generate new screenshots if verification failed and it's a PR + - name: Generate new Android screenshots if verification failed and it's a PR id: screenshotsrecord if: steps.screenshotsverify.outcome == 'failure' && github.event_name == 'pull_request' run: | ./gradlew recordRoborazziDebug + - name: Generate new JVM Desktop screenshots if verification failed and it's a PR + id: screenshotsrecord_jvm + if: steps.screenshotsverify_jvm.outcome == 'failure' && github.event_name == 'pull_request' + run: | + ./gradlew recordRoborazziJvm + - name: Push new screenshots if available uses: stefanzweifel/git-auto-commit-action@v5 - if: steps.screenshotsrecord.outcome == 'success' + if: steps.screenshotsrecord.outcome == 'success' || steps.screenshotsrecord_jvm.outcome == 'success' with: - file_pattern: '*/*.png' + file_pattern: '**/src/test/screenshots/**/*.png **/src/jvmTest/screenshots/**/*.png' disable_globbing: true commit_message: "🤖 Updates screenshots" diff --git a/README.md b/README.md index 4cfe09d9a..4b6a9d2cc 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ As Firebase Analytics does not yet support Kotlin Multiplatform, the implementat | :core:model | Done | ✅ | ✅️ | ✅ | ❌ | | :core:network | Done | ✅ | ✅️ | ✅ | ❌ | | :core:notification | Done | No implmentaion | ✅️ |No implmentaion| ❌ | -| :core:screenshot-testing | Not started | ❌ | ❌ | ❌ | ❌ | +| :core:screenshot-testing | Done | ✅ | ✅️ | ✅ | ❌ | | :core:testing | Done | ✅ | ✅️ | ✅ | ❌ | | :core:ui | Done | ✅ | ✅️ | ✅ | ❌ | | :feature:bookmarks | Done | ✅ | ✅️ | ✅ | ❌ | diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d7d8ba965..ee7fef66d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -119,6 +119,10 @@ kotlin { } androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) + implementation(libs.robolectric) + implementation(libs.roborazzi) implementation(projects.core.screenshotTesting) } @@ -139,7 +143,9 @@ kotlin { } jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) } } } diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt deleted file mode 100644 index a2409dd89..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/DeviceConfigurationOverrideWindowInsets.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 android.view.WindowInsets -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.platform.AbstractComposeView -import androidx.compose.ui.test.DeviceConfigurationOverride -import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.children - -/** - * A [DeviceConfigurationOverride] that overrides the window insets for the contained content. - */ -@Suppress("ktlint:standard:function-naming") -fun DeviceConfigurationOverride.Companion.WindowInsets( - windowInsets: WindowInsetsCompat, -): DeviceConfigurationOverride = DeviceConfigurationOverride { contentUnderTest -> - val currentContentUnderTest by rememberUpdatedState(contentUnderTest) - val currentWindowInsets by rememberUpdatedState(windowInsets) - AndroidView( - factory = { context -> - object : AbstractComposeView(context) { - @Composable - override fun Content() { - currentContentUnderTest() - } - - override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets { - children.forEach { - it.dispatchApplyWindowInsets( - WindowInsets(currentWindowInsets.toWindowInsets()), - ) - } - return WindowInsetsCompat.CONSUMED.toWindowInsets()!! - } - - /** - * Deprecated, but intercept the `requestApplyInsets` call via the deprecated - * method. - */ - @Deprecated("Deprecated in Java") - override fun requestFitSystemWindows() { - dispatchApplyWindowInsets(WindowInsets(currentWindowInsets.toWindowInsets()!!)) - } - } - }, - update = { with(currentWindowInsets) { it.requestApplyInsets() } }, - ) -} diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/InterestsListDetailScreenTest.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/InterestsListDetailScreenTest.kt deleted file mode 100644 index 1062c7e56..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/InterestsListDetailScreenTest.kt +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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.activity.compose.BackHandler -import androidx.annotation.StringRes -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertIsNotDisplayed -import androidx.compose.ui.test.junit4.AndroidComposeTestRule -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.test.espresso.Espresso -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.interests2pane.InterestsListDetailScreen -import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest -import dagger.hilt.android.testing.HiltTestApplication -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import javax.inject.Inject -import kotlin.properties.ReadOnlyProperty -import kotlin.test.assertTrue -import com.google.samples.apps.nowinandroid.feature.topic.R as FeatureTopicR - -private const val EXPANDED_WIDTH = "w1200dp-h840dp" -private const val COMPACT_WIDTH = "w412dp-h915dp" - -@HiltAndroidTest -@RunWith(RobolectricTestRunner::class) -@Config(application = HiltTestApplication::class) -class InterestsListDetailScreenTest { - - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - @get:Rule(order = 1) - val composeTestRule = createAndroidComposeRule() - - @Inject - lateinit var topicsRepository: TopicsRepository - - /** Convenience function for getting all topics during tests, */ - private fun getTopics(): List = runBlocking { - topicsRepository.getTopics().first().sortedBy { it.name } - } - - // 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" - - private val Topic.testTag - get() = "topic:${this.id}" - - @Before - fun setup() { - hiltRule.inject() - } - - @Test - @Config(qualifiers = EXPANDED_WIDTH) - fun expandedWidth_initialState_showsTwoPanesWithPlaceholder() { - composeTestRule.apply { - setContent { - NiaTheme { - InterestsListDetailScreen() - } - } - - onNodeWithTag(listPaneTag).assertIsDisplayed() - onNodeWithText(placeholderText).assertIsDisplayed() - } - } - - @Test - @Config(qualifiers = COMPACT_WIDTH) - fun compactWidth_initialState_showsListPane() { - composeTestRule.apply { - setContent { - NiaTheme { - InterestsListDetailScreen() - } - } - - onNodeWithTag(listPaneTag).assertIsDisplayed() - onNodeWithText(placeholderText).assertIsNotDisplayed() - } - } - - @Test - @Config(qualifiers = EXPANDED_WIDTH) - fun expandedWidth_topicSelected_updatesDetailPane() { - composeTestRule.apply { - setContent { - NiaTheme { - InterestsListDetailScreen() - } - } - - val firstTopic = getTopics().first() - onNodeWithText(firstTopic.name).performClick() - - onNodeWithTag(listPaneTag).assertIsDisplayed() - onNodeWithText(placeholderText).assertIsNotDisplayed() - onNodeWithTag(firstTopic.testTag).assertIsDisplayed() - } - } - - @Test - @Config(qualifiers = COMPACT_WIDTH) - fun compactWidth_topicSelected_showsTopicDetailPane() { - composeTestRule.apply { - setContent { - NiaTheme { - InterestsListDetailScreen() - } - } - - val firstTopic = getTopics().first() - onNodeWithText(firstTopic.name).performClick() - - onNodeWithTag(listPaneTag).assertIsNotDisplayed() - onNodeWithText(placeholderText).assertIsNotDisplayed() - onNodeWithTag(firstTopic.testTag).assertIsDisplayed() - } - } - - @Test - @Config(qualifiers = EXPANDED_WIDTH) - fun expandedWidth_backPressFromTopicDetail_leavesInterests() { - var unhandledBackPress = false - composeTestRule.apply { - setContent { - NiaTheme { - // Back press should not be handled by the two pane layout, and thus - // "fall through" to this BackHandler. - BackHandler { - unhandledBackPress = true - } - InterestsListDetailScreen() - } - } - - val firstTopic = getTopics().first() - onNodeWithText(firstTopic.name).performClick() - - waitForIdle() - Espresso.pressBack() - - assertTrue(unhandledBackPress) - } - } - - @Test - @Config(qualifiers = COMPACT_WIDTH) - fun compactWidth_backPressFromTopicDetail_showsListPane() { - composeTestRule.apply { - setContent { - NiaTheme { - InterestsListDetailScreen() - } - } - - val firstTopic = getTopics().first() - onNodeWithText(firstTopic.name).performClick() - - waitForIdle() - Espresso.pressBack() - - onNodeWithTag(listPaneTag).assertIsDisplayed() - onNodeWithText(placeholderText).assertIsNotDisplayed() - onNodeWithTag(firstTopic.testTag).assertIsNotDisplayed() - } - } -} - -private fun AndroidComposeTestRule<*, *>.stringResource( - @StringRes resId: Int, -): ReadOnlyProperty = - ReadOnlyProperty { _, _ -> activity.getString(resId) } diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt deleted file mode 100644 index 9c9488fde..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2023 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.compose.material3.adaptive.Posture -import androidx.compose.material3.adaptive.WindowAdaptiveInfo -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.DeviceConfigurationOverride -import androidx.compose.ui.test.ForcedSize -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onRoot -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import androidx.window.core.layout.WindowSizeClass -import com.github.takahirom.roborazzi.captureRoboImage -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor -import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor -import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions -import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest -import dagger.hilt.android.testing.HiltTestApplication -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import org.robolectric.annotation.GraphicsMode -import org.robolectric.annotation.LooperMode -import java.util.TimeZone -import javax.inject.Inject - -/** - * Tests that the navigation UI is rendered correctly on different screen sizes. - */ -@RunWith(RobolectricTestRunner::class) -@GraphicsMode(GraphicsMode.Mode.NATIVE) -// Configure Robolectric to use a very large screen size that can fit all of the test sizes. -// This allows enough room to render the content under test without clipping or scaling. -@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi") -@LooperMode(LooperMode.Mode.PAUSED) -@HiltAndroidTest -class NiaAppScreenSizesScreenshotTests { - - /** - * Manages the components' state and is used to perform injection on your test - */ - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - /** - * Use a test activity to set the content on. - */ - @get:Rule(order = 1) - val composeTestRule = createAndroidComposeRule() - - @Inject - lateinit var networkMonitor: NetworkMonitor - - @Inject - lateinit var timeZoneMonitor: TimeZoneMonitor - - @Inject - lateinit var userDataRepository: UserDataRepository - - @Inject - lateinit var topicsRepository: TopicsRepository - - @Inject - lateinit var userNewsResourceRepository: UserNewsResourceRepository - - @Before - fun setup() { - hiltRule.inject() - - // Configure user data - runBlocking { - userDataRepository.setShouldHideOnboarding(true) - - userDataRepository.setFollowedTopicIds( - setOf(topicsRepository.getTopics().first().first().id), - ) - } - } - - @Before - fun setTimeZone() { - // Make time zone deterministic in tests - TimeZone.setDefault(TimeZone.getTimeZone("UTC")) - } - - private fun testNiaAppScreenshotWithSize(width: Dp, height: Dp, screenshotName: String) { - composeTestRule.setContent { - CompositionLocalProvider( - LocalInspectionMode provides true, - ) { - DeviceConfigurationOverride( - override = DeviceConfigurationOverride.ForcedSize(DpSize(width, height)), - ) { - NiaTheme { - val fakeAppState = rememberNiaAppState( - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - timeZoneMonitor = timeZoneMonitor, - ) - NiaApp( - fakeAppState, - windowAdaptiveInfo = WindowAdaptiveInfo( - windowSizeClass = WindowSizeClass.compute( - width.value, - height.value, - ), - windowPosture = Posture(), - ), - ) - } - } - } - } - - composeTestRule.onRoot() - .captureRoboImage( - "src/testDemo/screenshots/$screenshotName.png", - roborazziOptions = DefaultRoborazziOptions, - ) - } - - @Test - fun compactWidth_compactHeight_showsNavigationBar() { - testNiaAppScreenshotWithSize( - 400.dp, - 400.dp, - "compactWidth_compactHeight_showsNavigationBar", - ) - } - - @Test - fun mediumWidth_compactHeight_showsNavigationBar() { - testNiaAppScreenshotWithSize( - 610.dp, - 400.dp, - "mediumWidth_compactHeight_showsNavigationBar", - ) - } - - @Test - fun expandedWidth_compactHeight_showsNavigationBar() { - testNiaAppScreenshotWithSize( - 900.dp, - 400.dp, - "expandedWidth_compactHeight_showsNavigationBar", - ) - } - - @Test - fun compactWidth_mediumHeight_showsNavigationBar() { - testNiaAppScreenshotWithSize( - 400.dp, - 500.dp, - "compactWidth_mediumHeight_showsNavigationBar", - ) - } - - @Test - fun mediumWidth_mediumHeight_showsNavigationRail() { - testNiaAppScreenshotWithSize( - 610.dp, - 500.dp, - "mediumWidth_mediumHeight_showsNavigationRail", - ) - } - - @Test - fun expandedWidth_mediumHeight_showsNavigationRail() { - testNiaAppScreenshotWithSize( - 900.dp, - 500.dp, - "expandedWidth_mediumHeight_showsNavigationRail", - ) - } - - @Test - fun compactWidth_expandedHeight_showsNavigationBar() { - testNiaAppScreenshotWithSize( - 400.dp, - 1000.dp, - "compactWidth_expandedHeight_showsNavigationBar", - ) - } - - @Test - fun mediumWidth_expandedHeight_showsNavigationRail() { - testNiaAppScreenshotWithSize( - 610.dp, - 1000.dp, - "mediumWidth_expandedHeight_showsNavigationRail", - ) - } - - @Test - fun expandedWidth_expandedHeight_showsNavigationRail() { - testNiaAppScreenshotWithSize( - 900.dp, - 1000.dp, - "expandedWidth_expandedHeight_showsNavigationRail", - ) - } -} diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt deleted file mode 100644 index 78f568e03..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarInsetsScreenshotTests.kt +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2023 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.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.safeDrawing -import androidx.compose.foundation.layout.windowInsetsBottomHeight -import androidx.compose.foundation.layout.windowInsetsEndWidth -import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.foundation.layout.windowInsetsStartWidth -import androidx.compose.foundation.layout.windowInsetsTopHeight -import androidx.compose.material3.SnackbarDuration.Indefinite -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.adaptive.Posture -import androidx.compose.material3.adaptive.WindowAdaptiveInfo -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toAndroidRect -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.test.DeviceConfigurationOverride -import androidx.compose.ui.test.ForcedSize -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpRect -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.roundToIntRect -import androidx.core.graphics.Insets -import androidx.core.view.WindowInsetsCompat -import androidx.window.core.layout.WindowSizeClass -import com.github.takahirom.roborazzi.captureRoboImage -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository -import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor -import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor -import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions -import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest -import dagger.hilt.android.testing.HiltTestApplication -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import org.robolectric.annotation.GraphicsMode -import org.robolectric.annotation.LooperMode -import java.util.TimeZone -import javax.inject.Inject - -/** - * Tests that the Snackbar is correctly displayed on different screen sizes. - */ -@RunWith(RobolectricTestRunner::class) -@GraphicsMode(GraphicsMode.Mode.NATIVE) -// Configure Robolectric to use a very large screen size that can fit all of the test sizes. -// This allows enough room to render the content under test without clipping or scaling. -@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi") -@LooperMode(LooperMode.Mode.PAUSED) -@HiltAndroidTest -class SnackbarInsetsScreenshotTests { - - /** - * Manages the components' state and is used to perform injection on your test - */ - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - /** - * Use a test activity to set the content on. - */ - @get:Rule(order = 1) - val composeTestRule = createAndroidComposeRule() - - @Inject - lateinit var networkMonitor: NetworkMonitor - - @Inject - lateinit var timeZoneMonitor: TimeZoneMonitor - - @Inject - lateinit var userDataRepository: FakeUserDataRepository - - @Inject - lateinit var topicsRepository: TopicsRepository - - @Inject - lateinit var userNewsResourceRepository: UserNewsResourceRepository - - @Before - fun setup() { - hiltRule.inject() - - // Configure user data - runBlocking { - userDataRepository.setShouldHideOnboarding(true) - - userDataRepository.setFollowedTopicIds( - setOf(topicsRepository.getTopics().first().first().id), - ) - } - } - - @Before - fun setTimeZone() { - // Make time zone deterministic in tests - TimeZone.setDefault(TimeZone.getTimeZone("UTC")) - } - - @Test - fun phone_noSnackbar() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 400.dp, - 500.dp, - "insets_snackbar_compact_medium_noSnackbar", - action = { }, - ) - } - - @Test - fun snackbarShown_phone() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 400.dp, - 500.dp, - "insets_snackbar_compact_medium", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - @Test - fun snackbarShown_foldable() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 600.dp, - 600.dp, - "insets_snackbar_medium_medium", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - @Test - fun snackbarShown_tablet() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 900.dp, - 900.dp, - "insets_snackbar_expanded_expanded", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - private fun testSnackbarScreenshotWithSize( - snackbarHostState: SnackbarHostState, - width: Dp, - height: Dp, - screenshotName: String, - action: suspend () -> Unit, - ) { - lateinit var scope: CoroutineScope - composeTestRule.setContent { - CompositionLocalProvider( - // Replaces images with placeholders - LocalInspectionMode provides true, - ) { - scope = rememberCoroutineScope() - - DeviceConfigurationOverride( - DeviceConfigurationOverride.ForcedSize(DpSize(width, height)), - ) { - DeviceConfigurationOverride( - DeviceConfigurationOverride.WindowInsets( - WindowInsetsCompat.Builder() - .setInsets( - WindowInsetsCompat.Type.statusBars(), - DpRect( - left = 0.dp, - top = 64.dp, - right = 0.dp, - bottom = 0.dp, - ).toInsets(), - ) - .setInsets( - WindowInsetsCompat.Type.navigationBars(), - DpRect( - left = 64.dp, - top = 0.dp, - right = 64.dp, - bottom = 64.dp, - ).toInsets(), - ) - .build(), - ), - ) { - BoxWithConstraints(Modifier.testTag("root")) { - NiaTheme { - val appState = rememberNiaAppState( - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - timeZoneMonitor = timeZoneMonitor, - ) - NiaApp( - appState = appState, - snackbarHostState = snackbarHostState, - showSettingsDialog = false, - onSettingsDismissed = {}, - onTopAppBarActionClick = {}, - windowAdaptiveInfo = WindowAdaptiveInfo( - windowSizeClass = WindowSizeClass.compute( - maxWidth.value, - maxHeight.value, - ), - windowPosture = Posture(), - ), - ) - DebugVisibleWindowInsets() - } - } - } - } - } - } - - scope.launch { - action() - } - - composeTestRule.onNodeWithTag("root") - .captureRoboImage( - "src/testDemo/screenshots/$screenshotName.png", - roborazziOptions = DefaultRoborazziOptions, - ) - } -} - -@Composable -fun DebugVisibleWindowInsets( - modifier: Modifier = Modifier, - debugColor: Color = Color.Magenta.copy(alpha = 0.5f), -) { - Box(modifier = modifier.fillMaxSize()) { - Spacer( - modifier = Modifier - .align(Alignment.CenterStart) - .fillMaxHeight() - .windowInsetsStartWidth(WindowInsets.safeDrawing) - .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical)) - .background(debugColor), - ) - Spacer( - modifier = Modifier - .align(Alignment.CenterEnd) - .fillMaxHeight() - .windowInsetsEndWidth(WindowInsets.safeDrawing) - .windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Vertical)) - .background(debugColor), - ) - Spacer( - modifier = Modifier - .align(Alignment.TopCenter) - .fillMaxWidth() - .windowInsetsTopHeight(WindowInsets.safeDrawing) - .background(debugColor), - ) - Spacer( - modifier = Modifier - .align(Alignment.BottomCenter) - .fillMaxWidth() - .windowInsetsBottomHeight(WindowInsets.safeDrawing) - .background(debugColor), - ) - } -} - -@Composable -private fun DpRect.toInsets() = toInsets(LocalDensity.current) - -private fun DpRect.toInsets(density: Density) = - Insets.of(with(density) { toRect() }.roundToIntRect().toAndroidRect()) diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt deleted file mode 100644 index b9b1047c1..000000000 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/SnackbarScreenshotTests.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2023 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.compose.foundation.layout.BoxWithConstraints -import androidx.compose.material3.SnackbarDuration.Indefinite -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.adaptive.Posture -import androidx.compose.material3.adaptive.WindowAdaptiveInfo -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.platform.LocalInspectionMode -import androidx.compose.ui.test.DeviceConfigurationOverride -import androidx.compose.ui.test.ForcedSize -import androidx.compose.ui.test.junit4.createAndroidComposeRule -import androidx.compose.ui.test.onRoot -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import androidx.window.core.layout.WindowSizeClass -import com.github.takahirom.roborazzi.captureRoboImage -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository -import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor -import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor -import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme -import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions -import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity -import dagger.hilt.android.testing.HiltAndroidRule -import dagger.hilt.android.testing.HiltAndroidTest -import dagger.hilt.android.testing.HiltTestApplication -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config -import org.robolectric.annotation.GraphicsMode -import org.robolectric.annotation.LooperMode -import java.util.TimeZone -import javax.inject.Inject - -/** - * Tests that the Snackbar is correctly displayed on different screen sizes. - */ -@RunWith(RobolectricTestRunner::class) -@GraphicsMode(GraphicsMode.Mode.NATIVE) -// Configure Robolectric to use a very large screen size that can fit all of the test sizes. -// This allows enough room to render the content under test without clipping or scaling. -@Config(application = HiltTestApplication::class, qualifiers = "w1000dp-h1000dp-480dpi") -@LooperMode(LooperMode.Mode.PAUSED) -@HiltAndroidTest -class SnackbarScreenshotTests { - - /** - * Manages the components' state and is used to perform injection on your test - */ - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - /** - * Use a test activity to set the content on. - */ - @get:Rule(order = 1) - val composeTestRule = createAndroidComposeRule() - - @Inject - lateinit var networkMonitor: NetworkMonitor - - @Inject - lateinit var timeZoneMonitor: TimeZoneMonitor - - @Inject - lateinit var userDataRepository: FakeUserDataRepository - - @Inject - lateinit var topicsRepository: TopicsRepository - - @Inject - lateinit var userNewsResourceRepository: UserNewsResourceRepository - - @Before - fun setup() { - hiltRule.inject() - - // Configure user data - runBlocking { - userDataRepository.setShouldHideOnboarding(true) - - userDataRepository.setFollowedTopicIds( - setOf(topicsRepository.getTopics().first().first().id), - ) - } - } - - @Before - fun setTimeZone() { - // Make time zone deterministic in tests - TimeZone.setDefault(TimeZone.getTimeZone("UTC")) - } - - @Test - fun phone_noSnackbar() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 400.dp, - 500.dp, - "snackbar_compact_medium_noSnackbar", - action = { }, - ) - } - - @Test - fun snackbarShown_phone() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 400.dp, - 500.dp, - "snackbar_compact_medium", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - @Test - fun snackbarShown_foldable() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 600.dp, - 600.dp, - "snackbar_medium_medium", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - @Test - fun snackbarShown_tablet() { - val snackbarHostState = SnackbarHostState() - testSnackbarScreenshotWithSize( - snackbarHostState, - 900.dp, - 900.dp, - "snackbar_expanded_expanded", - ) { - snackbarHostState.showSnackbar( - "This is a test snackbar message", - actionLabel = "Action Label", - duration = Indefinite, - ) - } - } - - private fun testSnackbarScreenshotWithSize( - snackbarHostState: SnackbarHostState, - width: Dp, - height: Dp, - screenshotName: String, - action: suspend () -> Unit, - ) { - lateinit var scope: CoroutineScope - composeTestRule.setContent { - CompositionLocalProvider( - // Replaces images with placeholders - LocalInspectionMode provides true, - ) { - scope = rememberCoroutineScope() - - DeviceConfigurationOverride( - DeviceConfigurationOverride.ForcedSize(DpSize(width, height)), - ) { - BoxWithConstraints { - NiaTheme { - val appState = rememberNiaAppState( - networkMonitor = networkMonitor, - userNewsResourceRepository = userNewsResourceRepository, - timeZoneMonitor = timeZoneMonitor, - ) - NiaApp( - appState = appState, - snackbarHostState = snackbarHostState, - showSettingsDialog = false, - onSettingsDismissed = {}, - onTopAppBarActionClick = {}, - windowAdaptiveInfo = WindowAdaptiveInfo( - windowSizeClass = WindowSizeClass.compute( - maxWidth.value, - maxHeight.value, - ), - windowPosture = Posture(), - ), - ) - } - } - } - } - } - - scope.launch { - action() - } - - composeTestRule.onRoot() - .captureRoboImage( - "src/testDemo/screenshots/$screenshotName.png", - roborazziOptions = DefaultRoborazziOptions, - ) - } -} diff --git a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png deleted file mode 100644 index 7a49b2c32..000000000 Binary files a/app/src/testDemo/screenshots/compactWidth_compactHeight_showsNavigationBar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png deleted file mode 100644 index ab20746cb..000000000 Binary files a/app/src/testDemo/screenshots/compactWidth_expandedHeight_showsNavigationBar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png deleted file mode 100644 index 926ce00f1..000000000 Binary files a/app/src/testDemo/screenshots/compactWidth_mediumHeight_showsNavigationBar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png deleted file mode 100644 index d793bed51..000000000 Binary files a/app/src/testDemo/screenshots/expandedWidth_compactHeight_showsNavigationBar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png deleted file mode 100644 index b6f89e516..000000000 Binary files a/app/src/testDemo/screenshots/expandedWidth_expandedHeight_showsNavigationRail.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png deleted file mode 100644 index 2bf46ca4f..000000000 Binary files a/app/src/testDemo/screenshots/expandedWidth_mediumHeight_showsNavigationRail.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png deleted file mode 100644 index e95e700ba..000000000 Binary files a/app/src/testDemo/screenshots/insets_snackbar_compact_medium.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png b/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png deleted file mode 100644 index db6488e34..000000000 Binary files a/app/src/testDemo/screenshots/insets_snackbar_compact_medium_noSnackbar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png b/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png deleted file mode 100644 index becfe5715..000000000 Binary files a/app/src/testDemo/screenshots/insets_snackbar_expanded_expanded.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png b/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png deleted file mode 100644 index 578ff0b6d..000000000 Binary files a/app/src/testDemo/screenshots/insets_snackbar_medium_medium.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png b/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png deleted file mode 100644 index 64cf8f32f..000000000 Binary files a/app/src/testDemo/screenshots/mediumWidth_compactHeight_showsNavigationBar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png deleted file mode 100644 index 679846fc1..000000000 Binary files a/app/src/testDemo/screenshots/mediumWidth_expandedHeight_showsNavigationRail.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png b/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png deleted file mode 100644 index b8ddeb12b..000000000 Binary files a/app/src/testDemo/screenshots/mediumWidth_mediumHeight_showsNavigationRail.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/snackbar_compact_medium.png b/app/src/testDemo/screenshots/snackbar_compact_medium.png deleted file mode 100644 index ad12a4173..000000000 Binary files a/app/src/testDemo/screenshots/snackbar_compact_medium.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png b/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png deleted file mode 100644 index bc466edcb..000000000 Binary files a/app/src/testDemo/screenshots/snackbar_compact_medium_noSnackbar.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/snackbar_expanded_expanded.png b/app/src/testDemo/screenshots/snackbar_expanded_expanded.png deleted file mode 100644 index fc98037a6..000000000 Binary files a/app/src/testDemo/screenshots/snackbar_expanded_expanded.png and /dev/null differ diff --git a/app/src/testDemo/screenshots/snackbar_medium_medium.png b/app/src/testDemo/screenshots/snackbar_medium_medium.png deleted file mode 100644 index 1dfc73e5a..000000000 Binary files a/app/src/testDemo/screenshots/snackbar_medium_medium.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png deleted file mode 100644 index 34b6b9589..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png deleted file mode 100644 index 6a9a51fe6..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 04de28429..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_androidTheme_notDynamic.png deleted file mode 100644 index 758c35dce..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_dynamic.png deleted file mode 100644 index 1b16b8fc4..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png deleted file mode 100644 index 3d802687a..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/Background_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png deleted file mode 100644 index 34b6b9589..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png deleted file mode 100644 index 6e30d8b63..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png deleted file mode 100644 index ca46d579b..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png deleted file mode 100644 index 758c35dce..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png deleted file mode 100644 index d1961cd01..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png deleted file mode 100644 index a9baaaac3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Background/GradientBackground_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png deleted file mode 100644 index aadd4c9dc..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 01de6984a..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png deleted file mode 100644 index 58f94b221..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png deleted file mode 100644 index c7b6cee37..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/ButtonLeadingIcon_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png deleted file mode 100644 index 0883a4497..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png deleted file mode 100644 index 1086161e0..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png deleted file mode 100644 index fc12d5a7d..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_androidTheme_notDynamic.png deleted file mode 100644 index 2863a06d3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_dynamic.png deleted file mode 100644 index f67bd4342..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png deleted file mode 100644 index fad380492..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/Button_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png deleted file mode 100644 index a113e51bc..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png deleted file mode 100644 index c1bd448e1..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 8855a359e..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png deleted file mode 100644 index d6d5bd628..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png deleted file mode 100644 index 50a6570f1..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png deleted file mode 100644 index ea96acea3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Button/OutlineButton_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png deleted file mode 100644 index 464be9cb4..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png deleted file mode 100644 index 129bd707f..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 54e665cf9..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png deleted file mode 100644 index 0e35be51d..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png deleted file mode 100644 index 01e8d9690..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png deleted file mode 100644 index 55d3a80fa..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChipSelected_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png deleted file mode 100644 index 568e41d78..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png deleted file mode 100644 index 27c1f4195..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 5d86614f2..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_fontScale2.png deleted file mode 100644 index fbd4109cd..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_fontScale2.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png deleted file mode 100644 index c4015dbc6..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png deleted file mode 100644 index df97855f0..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png deleted file mode 100644 index 28a1c895f..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/FilterChip/FilterChip_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png deleted file mode 100644 index 8e372685a..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png deleted file mode 100644 index 436c8d999..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 536e5cf25..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png deleted file mode 100644 index abc882504..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png deleted file mode 100644 index 5628b5698..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png deleted file mode 100644 index f88fa1d08..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButtonUnchecked_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png deleted file mode 100644 index d217e1116..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/IconButton/IconButton_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png deleted file mode 100644 index 316d899d2..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_1000.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png deleted file mode 100644 index 4705b9beb..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_115.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png deleted file mode 100644 index 203a2ea2e..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_20.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png deleted file mode 100644 index 50a822621..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_animation_724.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png deleted file mode 100644 index d266c68f0..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png deleted file mode 100644 index 87ae3ac55..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 73ad8f0fb..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png deleted file mode 100644 index 17b56813d..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png deleted file mode 100644 index e0b86ebb6..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png deleted file mode 100644 index e1b73d22b..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/LoadingWheel_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png deleted file mode 100644 index fbfa9fd0a..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png deleted file mode 100644 index dcd3b3eca..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 8f1a9343d..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png deleted file mode 100644 index e66b491a3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png deleted file mode 100644 index daa5ee299..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png deleted file mode 100644 index b922f641b..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/LoadingWheel/OverlayLoadingWheel_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png deleted file mode 100644 index 5e2a27c65..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png deleted file mode 100644 index efb1f8756..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 160a72d61..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_fontScale2.png deleted file mode 100644 index b4b4842c3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_fontScale2.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png deleted file mode 100644 index c3200091b..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png deleted file mode 100644 index 877c45cd0..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png deleted file mode 100644 index 9fd6fc843..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Navigation/Navigation_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png deleted file mode 100644 index bdf5d18f3..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png deleted file mode 100644 index 3c5fffc04..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 074f3dc8c..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_fontScale2.png deleted file mode 100644 index d7feb863b..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_fontScale2.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png deleted file mode 100644 index 5c38870dc..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png deleted file mode 100644 index 1e3270f94..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png deleted file mode 100644 index 759641c93..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tabs/Tabs_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png deleted file mode 100644 index 2a065eb57..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png deleted file mode 100644 index c532a307e..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 61d3321b8..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_fontScale2.png deleted file mode 100644 index 7cfdbb2a7..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_fontScale2.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png deleted file mode 100644 index fc29b6e11..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png deleted file mode 100644 index ed825ba71..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png deleted file mode 100644 index 8acc6b0a8..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/Tag/Tag_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png deleted file mode 100644 index 3617feab1..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png deleted file mode 100644 index 418e0f88d..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png deleted file mode 100644 index 921698d22..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_dark_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_fontScale2.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_fontScale2.png deleted file mode 100644 index 497561ef7..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_fontScale2.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png deleted file mode 100644 index 4988252e5..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_androidTheme_notDynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png deleted file mode 100644 index 6ab40ebf9..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_dynamic.png and /dev/null differ diff --git a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png b/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png deleted file mode 100644 index d9d014a18..000000000 Binary files a/core/designsystem/src/androidUnitTest/screenshots/TopAppBar/TopAppBar_light_defaultTheme_notDynamic.png and /dev/null differ diff --git a/core/screenshot-testing/build.gradle.kts b/core/screenshot-testing/build.gradle.kts index 6a33213cf..a6582c223 100644 --- a/core/screenshot-testing/build.gradle.kts +++ b/core/screenshot-testing/build.gradle.kts @@ -34,5 +34,13 @@ kotlin { implementation(libs.robolectric) implementation(projects.core.designsystem) } + jvmMain.dependencies { + api(libs.roborazzi.compose.desktop) + implementation(projects.core.designsystem) + } + iosMain.dependencies { + api(libs.roborazzi.compose.ios) + implementation(projects.core.designsystem) + } } } diff --git a/core/screenshot-testing/src/iosMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/IosScreenshotHelper.kt b/core/screenshot-testing/src/iosMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/IosScreenshotHelper.kt new file mode 100644 index 000000000..00b7a2bec --- /dev/null +++ b/core/screenshot-testing/src/iosMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/IosScreenshotHelper.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2026 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.core.testing.util + +import androidx.compose.runtime.Composable +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage + +@OptIn(ExperimentalTestApi::class) +fun captureIosScreenshot( + screenshotName: String, + content: @Composable () -> Unit, +) = runComposeUiTest { + setContent { content() } + onRoot().captureRoboImage(this, filePath = "src/iosTest/screenshots/$screenshotName.png") +} diff --git a/core/screenshot-testing/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/DesktopScreenshotHelper.kt b/core/screenshot-testing/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/DesktopScreenshotHelper.kt new file mode 100644 index 000000000..f873b1fe5 --- /dev/null +++ b/core/screenshot-testing/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/testing/util/DesktopScreenshotHelper.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2026 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.core.testing.util + +import androidx.compose.runtime.Composable +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.RoborazziOptions +import com.github.takahirom.roborazzi.RoborazziOptions.CompareOptions +import com.github.takahirom.roborazzi.RoborazziOptions.RecordOptions +import com.github.takahirom.roborazzi.captureRoboImage + +val DefaultRoborazziOptions = + RoborazziOptions( + // Pixel-perfect matching + compareOptions = CompareOptions(changeThreshold = 0f), + // Reduce the size of the PNGs + recordOptions = RecordOptions(resizeScale = 0.5), + ) + +enum class DefaultDesktopTestSizes(val description: String, val width: Int, val height: Int) { + COMPACT("compact", 400, 800), + MEDIUM("medium", 700, 900), + EXPANDED("expanded", 1200, 800), +} + +@OptIn(ExperimentalTestApi::class) +fun captureDesktopScreenshot( + screenshotName: String, + width: Int = 800, + height: Int = 600, + roborazziOptions: RoborazziOptions = DefaultRoborazziOptions, + content: @Composable () -> Unit, +) = runDesktopComposeUiTest(width = width, height = height) { + setContent { content() } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/$screenshotName.png", + roborazziOptions = roborazziOptions, + ) +} + +@OptIn(ExperimentalTestApi::class) +fun captureMultiSize( + screenshotName: String, + roborazziOptions: RoborazziOptions = DefaultRoborazziOptions, + content: @Composable () -> Unit, +) { + DefaultDesktopTestSizes.entries.forEach { size -> + runDesktopComposeUiTest(width = size.width, height = size.height) { + setContent { content() } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/${screenshotName}_${size.description}.png", + roborazziOptions = roborazziOptions, + ) + } + } +} diff --git a/feature/bookmarks/build.gradle.kts b/feature/bookmarks/build.gradle.kts index b562748bc..3134969a2 100644 --- a/feature/bookmarks/build.gradle.kts +++ b/feature/bookmarks/build.gradle.kts @@ -23,6 +23,7 @@ plugins { android { namespace = "com.google.samples.apps.nowinandroid.feature.bookmarks" + testOptions.unitTests.isIncludeAndroidResources = true } kotlin { @@ -35,11 +36,22 @@ kotlin { implementation(libs.jetbrains.compose.components.resources) implementation(libs.jetbrains.compose.uiToolingPreview) implementation(libs.kotlinx.serialization.core) - } commonTest.dependencies { implementation(projects.core.testing) } + androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) + implementation(libs.robolectric) + implementation(libs.roborazzi) + implementation(projects.core.screenshotTesting) + } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.androidx.lifecycle.runtime.testing) diff --git a/feature/bookmarks/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenScreenshotTests.kt b/feature/bookmarks/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenScreenshotTests.kt new file mode 100644 index 000000000..0e3a81cf0 --- /dev/null +++ b/feature/bookmarks/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenScreenshotTests.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2026 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.bookmarks + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices +import com.google.samples.apps.nowinandroid.core.testing.util.captureForDevice +import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiDevice +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode +import java.util.TimeZone + +/** + * Screenshot tests for the [BookmarksScreen]. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(qualifiers = "480dpi") +@LooperMode(LooperMode.Mode.PAUSED) +class BookmarksScreenScreenshotTests { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + + @Before + fun setTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + @Test + fun bookmarksScreenPopulatedFeed() { + composeTestRule.captureMultiDevice("BookmarksScreenPopulatedFeed") { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + } + + @Test + fun bookmarksScreenLoading() { + composeTestRule.captureMultiDevice("BookmarksScreenLoading") { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Loading, + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + } + + @Test + fun bookmarksScreenPopulatedFeed_dark() { + composeTestRule.captureForDevice( + deviceName = "phone_dark", + deviceSpec = DefaultTestDevices.PHONE.spec, + screenshotName = "BookmarksScreenPopulatedFeed", + darkMode = true, + ) { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + } +} diff --git a/feature/bookmarks/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenDesktopScreenshotTests.kt b/feature/bookmarks/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenDesktopScreenshotTests.kt new file mode 100644 index 000000000..8a6e14b55 --- /dev/null +++ b/feature/bookmarks/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenDesktopScreenshotTests.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2026 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.bookmarks + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [BookmarksScreen]. + */ +class BookmarksScreenDesktopScreenshotTests { + + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + + @OptIn(ExperimentalTestApi::class) + @Test + fun bookmarksScreen_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/BookmarksScreen_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun bookmarksScreen_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/BookmarksScreen_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun bookmarksScreen_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + BookmarksScreen( + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onShowSnackbar = { _, _ -> false }, + removeFromBookmarks = {}, + onNewsResourceViewed = {}, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/BookmarksScreen_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/feature/foryou/build.gradle.kts b/feature/foryou/build.gradle.kts index 3e0c0de54..2ab1bb3d3 100644 --- a/feature/foryou/build.gradle.kts +++ b/feature/foryou/build.gradle.kts @@ -54,6 +54,11 @@ kotlin { implementation(libs.roborazzi) implementation(projects.core.screenshotTesting) } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_foldable.png deleted file mode 100644 index a699345c2..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_foldable.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_phone.png deleted file mode 100644 index 30c8fdad7..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_phone.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_tablet.png deleted file mode 100644 index f54966dbc..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenLoading_tablet.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_foldable.png deleted file mode 100644 index 3990aee3f..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_foldable.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone.png deleted file mode 100644 index a86a8232f..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png deleted file mode 100644 index d500394ef..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_phone_dark.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_tablet.png deleted file mode 100644 index 3b735462f..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedAndLoading_tablet.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_foldable.png deleted file mode 100644 index 0da7e19ac..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_foldable.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_phone.png deleted file mode 100644 index a7b775315..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_phone.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_tablet.png deleted file mode 100644 index e7f0cc050..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenPopulatedFeed_tablet.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_foldable.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_foldable.png deleted file mode 100644 index f5d68fce2..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_foldable.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone.png deleted file mode 100644 index b04c632c3..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone_dark.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone_dark.png deleted file mode 100644 index 6c4d66b28..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_phone_dark.png and /dev/null differ diff --git a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_tablet.png b/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_tablet.png deleted file mode 100644 index f7c20a890..000000000 Binary files a/feature/foryou/src/androidUnitTest/screenshots/ForYouScreenTopicSelection_tablet.png and /dev/null differ diff --git a/feature/foryou/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenDesktopScreenshotTests.kt b/feature/foryou/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenDesktopScreenshotTests.kt new file mode 100644 index 000000000..3b7517eaf --- /dev/null +++ b/feature/foryou/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenDesktopScreenshotTests.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2026 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.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [ForYouScreen]. + */ +class ForYouScreenDesktopScreenshotTests { + + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + + @OptIn(ExperimentalTestApi::class) + @Test + fun forYouScreen_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.NotShown, + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + deepLinkedUserNewsResource = null, + onDeepLinkOpened = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/ForYouScreen_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun forYouScreen_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.NotShown, + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + deepLinkedUserNewsResource = null, + onDeepLinkOpened = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/ForYouScreen_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun forYouScreen_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + ForYouScreen( + isSyncing = false, + onboardingUiState = OnboardingUiState.NotShown, + feedState = NewsFeedUiState.Success(feed = userNewsResources), + onTopicCheckedChanged = { _, _ -> }, + saveFollowedTopics = {}, + onNewsResourcesCheckedChanged = { _, _ -> }, + onNewsResourceViewed = {}, + onTopicClick = {}, + deepLinkedUserNewsResource = null, + onDeepLinkOpened = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/ForYouScreen_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index 2041ac62f..dda81c446 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -22,6 +22,7 @@ plugins { } android { namespace = "com.google.samples.apps.nowinandroid.feature.interests" + testOptions.unitTests.isIncludeAndroidResources = true } kotlin { @@ -41,11 +42,18 @@ kotlin { implementation(projects.core.testing) } androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) implementation(libs.robolectric) implementation(libs.roborazzi) implementation(libs.androidx.navigation.testing) implementation(projects.core.screenshotTesting) } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/interests/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenScreenshotTests.kt b/feature/interests/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenScreenshotTests.kt new file mode 100644 index 000000000..61def0102 --- /dev/null +++ b/feature/interests/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenScreenshotTests.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2026 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.interests + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices +import com.google.samples.apps.nowinandroid.core.testing.util.captureForDevice +import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiDevice +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode + +/** + * Screenshot tests for the [InterestsScreen]. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(qualifiers = "480dpi") +@LooperMode(LooperMode.Mode.PAUSED) +class InterestsScreenScreenshotTests { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private val topics = FollowableTopicPreviewParameterProvider().values.first() + + @Test + fun interestsScreenPopulated() { + composeTestRule.captureMultiDevice("InterestsScreenPopulated") { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = topics, + ), + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + } + + @Test + fun interestsScreenLoading() { + composeTestRule.captureMultiDevice("InterestsScreenLoading") { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Loading, + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + } + + @Test + fun interestsScreenEmpty() { + composeTestRule.captureMultiDevice("InterestsScreenEmpty") { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Empty, + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + } + + @Test + fun interestsScreenPopulated_dark() { + composeTestRule.captureForDevice( + deviceName = "phone_dark", + deviceSpec = DefaultTestDevices.PHONE.spec, + screenshotName = "InterestsScreenPopulated", + darkMode = true, + ) { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = topics, + ), + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + } +} diff --git a/feature/interests/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenDesktopScreenshotTests.kt b/feature/interests/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenDesktopScreenshotTests.kt new file mode 100644 index 000000000..685ea66fc --- /dev/null +++ b/feature/interests/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreenDesktopScreenshotTests.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2026 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.interests + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [InterestsScreen]. + */ +class InterestsScreenDesktopScreenshotTests { + + private val topics = FollowableTopicPreviewParameterProvider().values.first() + + @OptIn(ExperimentalTestApi::class) + @Test + fun interestsScreen_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = topics, + ), + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/InterestsScreen_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun interestsScreen_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = topics, + ), + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/InterestsScreen_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun interestsScreen_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + InterestsScreen( + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = topics, + ), + followTopic = { _, _ -> }, + onTopicClick = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/InterestsScreen_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/feature/search/build.gradle.kts b/feature/search/build.gradle.kts index 94699386e..b324b9bce 100644 --- a/feature/search/build.gradle.kts +++ b/feature/search/build.gradle.kts @@ -23,6 +23,7 @@ plugins { android { namespace = "com.google.samples.apps.nowinandroid.feature.search" + testOptions.unitTests.isIncludeAndroidResources = true } kotlin { @@ -41,10 +42,17 @@ kotlin { implementation(projects.core.testing) } androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) implementation(libs.robolectric) implementation(libs.roborazzi) implementation(projects.core.screenshotTesting) } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/search/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenScreenshotTests.kt b/feature/search/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenScreenshotTests.kt new file mode 100644 index 000000000..8cf9473f3 --- /dev/null +++ b/feature/search/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenScreenshotTests.kt @@ -0,0 +1,118 @@ +/* + * Copyright 2026 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.search + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices +import com.google.samples.apps.nowinandroid.core.testing.util.captureForDevice +import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiDevice +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode +import java.util.TimeZone + +/** + * Screenshot tests for the [SearchScreen]. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(qualifiers = "480dpi") +@LooperMode(LooperMode.Mode.PAUSED) +class SearchScreenScreenshotTests { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + private val topics = FollowableTopicPreviewParameterProvider().values.first() + + @Before + fun setTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + @Test + fun searchScreenWithResults() { + composeTestRule.captureMultiDevice("SearchScreenWithResults") { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Success( + topics = topics, + newsResources = userNewsResources, + ), + recentSearchesUiState = RecentSearchQueriesUiState.Success(), + ) + } + } + } + + @Test + fun searchScreenLoading() { + composeTestRule.captureMultiDevice("SearchScreenLoading") { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Loading, + recentSearchesUiState = RecentSearchQueriesUiState.Loading, + ) + } + } + } + + @Test + fun searchScreenEmptyQuery() { + composeTestRule.captureMultiDevice("SearchScreenEmptyQuery") { + NiaTheme { + SearchScreen( + searchQuery = "", + searchResultUiState = SearchResultUiState.EmptyQuery, + ) + } + } + } + + @Test + fun searchScreenWithResults_dark() { + composeTestRule.captureForDevice( + deviceName = "phone_dark", + deviceSpec = DefaultTestDevices.PHONE.spec, + screenshotName = "SearchScreenWithResults", + darkMode = true, + ) { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Success( + topics = topics, + newsResources = userNewsResources, + ), + recentSearchesUiState = RecentSearchQueriesUiState.Success(), + ) + } + } + } +} diff --git a/feature/search/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenDesktopScreenshotTests.kt b/feature/search/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenDesktopScreenshotTests.kt new file mode 100644 index 000000000..461f8f787 --- /dev/null +++ b/feature/search/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchScreenDesktopScreenshotTests.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2026 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.search + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [SearchScreen]. + */ +class SearchScreenDesktopScreenshotTests { + + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + private val topics = FollowableTopicPreviewParameterProvider().values.first() + + @OptIn(ExperimentalTestApi::class) + @Test + fun searchScreen_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Success( + topics = topics, + newsResources = userNewsResources, + ), + recentSearchesUiState = RecentSearchQueriesUiState.Success(), + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SearchScreen_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun searchScreen_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Success( + topics = topics, + newsResources = userNewsResources, + ), + recentSearchesUiState = RecentSearchQueriesUiState.Success(), + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SearchScreen_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun searchScreen_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + SearchScreen( + searchQuery = "android", + searchResultUiState = SearchResultUiState.Success( + topics = topics, + newsResources = userNewsResources, + ), + recentSearchesUiState = RecentSearchQueriesUiState.Success(), + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SearchScreen_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 4d5e5799e..c016ee479 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -17,10 +17,12 @@ plugins { alias(libs.plugins.nowinandroid.cmp.feature) alias(libs.plugins.nowinandroid.android.library.jacoco) + alias(libs.plugins.roborazzi) } android { namespace = "com.google.samples.apps.nowinandroid.feature.settings" + testOptions.unitTests.isIncludeAndroidResources = true } kotlin { @@ -38,10 +40,17 @@ kotlin { implementation(projects.core.testing) } androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) implementation(libs.robolectric) implementation(libs.roborazzi) implementation(projects.core.screenshotTesting) } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/settings/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogScreenshotTests.kt b/feature/settings/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogScreenshotTests.kt new file mode 100644 index 000000000..ef34d0ba4 --- /dev/null +++ b/feature/settings/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogScreenshotTests.kt @@ -0,0 +1,173 @@ +/* + * Copyright 2026 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.settings + +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.test.DarkMode +import androidx.compose.ui.test.DeviceConfigurationOverride +import androidx.compose.ui.test.isRoot +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode + +/** + * Screenshot tests for the [SettingsDialog]. + * + * SettingsDialog uses AlertDialog which creates multiple root nodes in the compose tree. + * Therefore we capture the dialog root node directly instead of using [captureMultiDevice]. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(qualifiers = "480dpi") +@LooperMode(LooperMode.Mode.PAUSED) +class SettingsDialogScreenshotTests { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private fun captureDialogForDevice( + deviceName: String, + deviceSpec: String, + screenshotName: String, + darkMode: Boolean = false, + body: @Composable () -> Unit, + ) { + val specs = deviceSpec.substringAfter("spec:") + .split(",").map { it.split("=") }.associate { it[0] to it[1] } + val width = specs["width"]?.toInt() ?: 640 + val height = specs["height"]?.toInt() ?: 480 + val dpi = specs["dpi"]?.toInt() ?: 480 + + RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi") + + composeTestRule.activity.setContent { + CompositionLocalProvider( + LocalInspectionMode provides true, + ) { + DeviceConfigurationOverride( + override = DeviceConfigurationOverride.Companion.DarkMode(darkMode), + ) { + body() + } + } + } + + composeTestRule.mainClock.autoAdvance = false + + // AlertDialog creates 2 root nodes; capture the dialog root (index 1) + composeTestRule.onAllNodes(isRoot())[1] + .captureRoboImage( + "src/test/screenshots/${screenshotName}_$deviceName.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + private fun captureDialogMultiDevice( + screenshotName: String, + body: @Composable () -> Unit, + ) { + DefaultTestDevices.entries.forEach { + captureDialogForDevice( + deviceName = it.description, + deviceSpec = it.spec, + screenshotName = screenshotName, + body = body, + ) + } + } + + @Test + fun settingsDialogLoaded() { + captureDialogMultiDevice("SettingsDialogLoaded") { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Success( + settings = UserEditableSettings( + brand = ThemeBrand.DEFAULT, + useDynamicColor = false, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + ), + ), + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + } + + @Test + fun settingsDialogLoading() { + captureDialogMultiDevice("SettingsDialogLoading") { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Loading, + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + } + + @Test + fun settingsDialogLoaded_dark() { + captureDialogForDevice( + deviceName = "phone_dark", + deviceSpec = DefaultTestDevices.PHONE.spec, + screenshotName = "SettingsDialogLoaded", + darkMode = true, + ) { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Success( + settings = UserEditableSettings( + brand = ThemeBrand.DEFAULT, + useDynamicColor = false, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + ), + ), + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + } +} diff --git a/feature/settings/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogDesktopScreenshotTests.kt b/feature/settings/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogDesktopScreenshotTests.kt new file mode 100644 index 000000000..f3addeb0c --- /dev/null +++ b/feature/settings/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogDesktopScreenshotTests.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2026 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.settings + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [SettingsDialog]. + */ +class SettingsDialogDesktopScreenshotTests { + + @OptIn(ExperimentalTestApi::class) + @Test + fun settingsDialog_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Success( + settings = UserEditableSettings( + brand = ThemeBrand.DEFAULT, + useDynamicColor = false, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + ), + ), + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SettingsDialog_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun settingsDialog_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Success( + settings = UserEditableSettings( + brand = ThemeBrand.DEFAULT, + useDynamicColor = false, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + ), + ), + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SettingsDialog_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun settingsDialog_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + SettingsDialog( + settingsUiState = SettingsUiState.Success( + settings = UserEditableSettings( + brand = ThemeBrand.DEFAULT, + useDynamicColor = false, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + ), + ), + supportDynamicColor = false, + onDismiss = {}, + onChangeThemeBrand = {}, + onChangeDynamicColorPreference = {}, + onChangeDarkThemeConfig = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/SettingsDialog_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/feature/topic/build.gradle.kts b/feature/topic/build.gradle.kts index 0b215c584..9f8230a82 100644 --- a/feature/topic/build.gradle.kts +++ b/feature/topic/build.gradle.kts @@ -17,11 +17,13 @@ plugins { alias(libs.plugins.nowinandroid.cmp.feature) alias(libs.plugins.nowinandroid.android.library.jacoco) + alias(libs.plugins.roborazzi) alias(libs.plugins.kotlin.serialization) } android { namespace = "com.google.samples.apps.nowinandroid.feature.topic" + testOptions.unitTests.isIncludeAndroidResources = true } kotlin { @@ -42,11 +44,18 @@ kotlin { implementation(projects.core.testing) } androidUnitTest.dependencies { + implementation(libs.androidx.compose.ui.test) + implementation(libs.androidx.compose.ui.testManifest) implementation(libs.robolectric) implementation(libs.roborazzi) implementation(libs.androidx.navigation.testing) implementation(projects.core.screenshotTesting) } + jvmTest.dependencies { + implementation(libs.roborazzi.compose.desktop) + implementation(libs.jetbrains.compose.ui.test.junit4) + implementation(projects.core.screenshotTesting) + } androidInstrumentedTest.dependencies { implementation(projects.core.testing) implementation(libs.bundles.androidx.compose.ui.test) diff --git a/feature/topic/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenScreenshotTests.kt b/feature/topic/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenScreenshotTests.kt new file mode 100644 index 000000000..161bca130 --- /dev/null +++ b/feature/topic/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenScreenshotTests.kt @@ -0,0 +1,127 @@ +/* + * Copyright 2026 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.junit4.createAndroidComposeRule +import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils +import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultTestDevices +import com.google.samples.apps.nowinandroid.core.testing.util.captureForDevice +import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiDevice +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import org.robolectric.annotation.LooperMode +import java.util.TimeZone + +/** + * Screenshot tests for the [TopicScreen]. + */ +@RunWith(RobolectricTestRunner::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config(qualifiers = "480dpi") +@LooperMode(LooperMode.Mode.PAUSED) +class TopicScreenScreenshotTests { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private val followableTopic = FollowableTopicPreviewParameterProvider().values.first().first() + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + + @Before + fun setTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone("UTC")) + } + + private val accessibilitySuppressions = + AccessibilityCheckResultUtils.matchesCheck(SpeakableTextPresentCheck::class.java) + + @Test + fun topicScreenPopulated() { + composeTestRule.captureMultiDevice( + "TopicScreenPopulated", + accessibilitySuppressions = accessibilitySuppressions, + ) { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Success(followableTopic = followableTopic), + newsUiState = NewsUiState.Success(news = userNewsResources), + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + } + + @Test + fun topicScreenLoading() { + composeTestRule.captureMultiDevice( + "TopicScreenLoading", + accessibilitySuppressions = accessibilitySuppressions, + ) { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Loading, + newsUiState = NewsUiState.Loading, + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + } + + @Test + fun topicScreenPopulated_dark() { + composeTestRule.captureForDevice( + deviceName = "phone_dark", + deviceSpec = DefaultTestDevices.PHONE.spec, + screenshotName = "TopicScreenPopulated", + darkMode = true, + accessibilitySuppressions = accessibilitySuppressions, + ) { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Success(followableTopic = followableTopic), + newsUiState = NewsUiState.Success(news = userNewsResources), + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + } +} diff --git a/feature/topic/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenDesktopScreenshotTests.kt b/feature/topic/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenDesktopScreenshotTests.kt new file mode 100644 index 000000000..c072693eb --- /dev/null +++ b/feature/topic/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenDesktopScreenshotTests.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2026 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.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.runDesktopComposeUiTest +import com.github.takahirom.roborazzi.captureRoboImage +import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions +import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider +import com.google.samples.apps.nowinandroid.core.ui.UserNewsResourcePreviewParameterProvider +import org.junit.Test + +/** + * JVM Desktop screenshot tests for the [TopicScreen]. + */ +class TopicScreenDesktopScreenshotTests { + + private val followableTopic = FollowableTopicPreviewParameterProvider().values.first().first() + private val userNewsResources = UserNewsResourcePreviewParameterProvider().values.first() + + @OptIn(ExperimentalTestApi::class) + @Test + fun topicScreen_compact() = runDesktopComposeUiTest(width = 400, height = 800) { + setContent { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Success(followableTopic = followableTopic), + newsUiState = NewsUiState.Success(news = userNewsResources), + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/TopicScreen_compact.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun topicScreen_medium() = runDesktopComposeUiTest(width = 700, height = 900) { + setContent { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Success(followableTopic = followableTopic), + newsUiState = NewsUiState.Success(news = userNewsResources), + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/TopicScreen_medium.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } + + @OptIn(ExperimentalTestApi::class) + @Test + fun topicScreen_expanded() = runDesktopComposeUiTest(width = 1200, height = 800) { + setContent { + NiaTheme { + TopicScreen( + topicUiState = TopicUiState.Success(followableTopic = followableTopic), + newsUiState = NewsUiState.Success(news = userNewsResources), + showBackButton = true, + onBackClick = {}, + onFollowClick = {}, + onTopicClick = {}, + onBookmarkChanged = { _, _ -> }, + onNewsResourceViewed = {}, + ) + } + } + onRoot().captureRoboImage( + filePath = "src/jvmTest/screenshots/TopicScreen_expanded.png", + roborazziOptions = DefaultRoborazziOptions, + ) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4418c42e6..9ee67467f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -139,6 +139,8 @@ logback-classic = { group = "ch.qos.logback", name = "logback-classic", version. robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" } roborazzi-accessibility-check = { group = "io.github.takahirom.roborazzi", name = "roborazzi-accessibility-check", version.ref = "roborazzi" } +roborazzi-compose-desktop = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose-desktop", version.ref = "roborazzi" } +roborazzi-compose-ios = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose-ios", version.ref = "roborazzi" } truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }