Adds screenshot tests to :core:designsystem (#931)

* WIP: Adds screenshot tests to :core:designsystem

Change-Id: I0672845feba4064652dd8d60f07047b87864e121

* 🤖 Updates screenshots

* Creates tests for more components and cleans up

Change-Id: I61fe3ae6a4e8a41a599d520e16fc14aa6a643a22

* WIP: More cleanup and more combinations of themes

Change-Id: I34312bc7d147b31f1c638cd505a9c241f8267523

* Added the rest of the screenshot tests for designsystem

Change-Id: Ic427db5491910781c038882055524e3f3dbed194

* Some more cleanup

Change-Id: I7384e55864719af9122ad9da8e50a09cb9a60180

* Spotless

Change-Id: I22aa46e1f56b8b638c9d609ababbe49d471a26c6

* 🤖 Updates screenshots

* ScreenshotHelper cleanup

Change-Id: Ic94d41618e7850ab47f294d8022b405f18c843f0
pull/848/merge
Jose Alcérreca 10 months ago committed by GitHub
parent d0909a9c8f
commit 151f877bbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
/*
* Copyright 2022 The Android Open Source Project
* 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.

@ -18,7 +18,9 @@ import com.android.build.gradle.LibraryExtension
import com.google.samples.apps.nowinandroid.configureAndroidCompose
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.kotlin
class AndroidLibraryComposeConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {

@ -47,9 +47,11 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
disableUnnecessaryAndroidTests(target)
}
dependencies {
add("androidTestImplementation", kotlin("test"))
add("testImplementation", kotlin("test"))
add("testImplementation", project(":core:testing"))
add("androidTestImplementation", kotlin("test"))
add("androidTestImplementation", project(":core:testing"))
}
}
}
}
}

@ -21,15 +21,20 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android filled button with generic content slot. Wraps Material 3 [Button].
@ -260,6 +265,50 @@ private fun NiaButtonContent(
}
}
@ThemePreviews
@Composable
fun NiaButtonPreview() {
NiaTheme {
NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) {
NiaButton(onClick = {}, text = { Text("Test button") })
}
}
}
@ThemePreviews
@Composable
fun NiaOutlinedButtonPreview() {
NiaTheme() {
NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) {
NiaOutlinedButton(onClick = {}, text = { Text("Test button") })
}
}
}
@ThemePreviews
@Composable
fun NiaButtonPreview2() {
NiaTheme {
NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) {
NiaButton(onClick = {}, text = { Text("Test button") })
}
}
}
@ThemePreviews
@Composable
fun NiaButtonLeadingIconPreview() {
NiaTheme {
NiaBackground(modifier = Modifier.size(150.dp, 50.dp)) {
NiaButton(
onClick = {},
text = { Text("Test button") },
leadingIcon = { Icon(imageVector = NiaIcons.Add, contentDescription = null) },
)
}
}
}
/**
* Now in Android button default values.
*/

@ -16,6 +16,7 @@
package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
@ -23,11 +24,13 @@ import androidx.compose.material3.FilterChipDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android filter chip with included leading checked icon as well as text content slot.
@ -103,6 +106,18 @@ fun NiaFilterChip(
)
}
@ThemePreviews
@Composable
fun ChipPreview() {
NiaTheme {
NiaBackground(modifier = Modifier.size(80.dp, 20.dp)) {
NiaFilterChip(selected = true, onSelectedChange = {}) {
Text("Chip")
}
}
}
}
/**
* Now in Android chip default values.
*/

@ -17,12 +17,15 @@
package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android toggle button with icon and checked icon content slots. Wraps Material 3
@ -68,6 +71,52 @@ fun NiaIconToggleButton(
}
}
@ThemePreviews
@Composable
fun IconButtonPreview() {
NiaTheme {
NiaIconToggleButton(
checked = true,
onCheckedChange = { },
icon = {
Icon(
imageVector = NiaIcons.BookmarkBorder,
contentDescription = null,
)
},
checkedIcon = {
Icon(
imageVector = NiaIcons.Bookmark,
contentDescription = null,
)
},
)
}
}
@ThemePreviews
@Composable
fun IconButtonPreviewUnchecked() {
NiaTheme {
NiaIconToggleButton(
checked = false,
onCheckedChange = { },
icon = {
Icon(
imageVector = NiaIcons.BookmarkBorder,
contentDescription = null,
)
},
checkedIcon = {
Icon(
imageVector = NiaIcons.Bookmark,
contentDescription = null,
)
},
)
}
}
/**
* Now in Android icon button default values.
*/

@ -88,6 +88,7 @@ fun NiaLoadingWheel(
// Specifies the color animation for the base-to-progress line color change
val baseLineColor = MaterialTheme.colorScheme.onBackground
val progressLineColor = MaterialTheme.colorScheme.inversePrimary
val colorAnimValues = (0 until NUM_OF_LINES).map { index ->
infiniteTransition.animateColor(
initialValue = baseLineColor,

@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid.core.designsystem.component
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
@ -25,10 +26,13 @@ import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.NavigationRailItemDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android navigation bar item with icon and label content slots. Wraps Material 3
@ -161,6 +165,46 @@ fun NiaNavigationRail(
)
}
@ThemePreviews
@Composable
fun NiaNavigationPreview() {
val items = listOf("For you", "Saved", "Interests")
val icons = listOf(
NiaIcons.UpcomingBorder,
NiaIcons.BookmarksBorder,
NiaIcons.Grid3x3,
)
val selectedIcons = listOf(
NiaIcons.Upcoming,
NiaIcons.Bookmarks,
NiaIcons.Grid3x3,
)
NiaTheme {
NiaNavigationBar {
items.forEachIndexed { index, item ->
NiaNavigationBarItem(
icon = {
Icon(
imageVector = icons[index],
contentDescription = item,
)
},
selectedIcon = {
Icon(
imageVector = selectedIcons[index],
contentDescription = item,
)
},
label = { Text(item) },
selected = index == 0,
onClick = { },
)
}
}
}
}
/**
* Now in Android navigation default values.
*/

@ -24,11 +24,15 @@ import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android tab. Wraps Material 3 [Tab] and shifts text label down.
@ -97,6 +101,23 @@ fun NiaTabRow(
)
}
@ThemePreviews
@Composable
fun TabsPreview() {
NiaTheme {
val titles = listOf("Topics", "People")
NiaTabRow(selectedTabIndex = 0) {
titles.forEachIndexed { index, title ->
NiaTab(
selected = index == 0,
onClick = { },
text = { Text(text = title) },
)
}
}
}
}
object NiaTabDefaults {
val TabTopPadding = 7.dp
}

@ -20,10 +20,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@Composable
fun NiaTopicTag(
@ -59,6 +61,16 @@ fun NiaTopicTag(
}
}
@ThemePreviews
@Composable
fun TagPreview() {
NiaTheme {
NiaTopicTag(followed = true, onClick = {}) {
Text("Topic".uppercase())
}
}
}
/**
* Now in Android tag default values.
*/

@ -73,35 +73,6 @@ fun NiaTopAppBar(
)
}
/**
* Top app bar with action, displayed on the right
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NiaTopAppBar(
@StringRes titleRes: Int,
actionIcon: ImageVector,
actionIconContentDescription: String?,
modifier: Modifier = Modifier,
colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors(),
onActionClick: () -> Unit = {},
) {
CenterAlignedTopAppBar(
title = { Text(text = stringResource(id = titleRes)) },
actions = {
IconButton(onClick = onActionClick) {
Icon(
imageVector = actionIcon,
contentDescription = actionIconContentDescription,
tint = MaterialTheme.colorScheme.onSurface,
)
}
},
colors = colors,
modifier = modifier.testTag("niaTopAppBar"),
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Preview("Top App Bar")
@Composable

@ -24,11 +24,15 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
/**
* Now in Android view toggle button with included trailing icon as well as compact and expanded
@ -105,6 +109,36 @@ private fun NiaViewToggleButtonContent(
}
}
@ThemePreviews
@Composable
fun ViewTogglePreviewExpanded() {
NiaTheme {
Surface {
NiaViewToggleButton(
expanded = true,
onExpandedChange = { },
compactText = { Text(text = "Compact view") },
expandedText = { Text(text = "Expanded view") },
)
}
}
}
@Preview
@Composable
fun ViewTogglePreviewCompact() {
NiaTheme {
Surface {
NiaViewToggleButton(
expanded = false,
onExpandedChange = { },
compactText = { Text(text = "Compact view") },
expandedText = { Text(text = "Expanded view") },
)
}
}
}
/**
* Now in Android view toggle default values.
*/

@ -0,0 +1,63 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaGradientBackground
import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class BackgroundScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun niaBackground_multipleThemes() {
composeTestRule.captureMultiTheme("Background") { description ->
NiaBackground(Modifier.size(100.dp)) {
Text("$description background")
}
}
}
@Test
fun niaGradientBackground_multipleThemes() {
composeTestRule.captureMultiTheme("Background", "GradientBackground") { description ->
NiaGradientBackground(Modifier.size(100.dp)) {
Text("$description background")
}
}
}
}

@ -0,0 +1,80 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaButton
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOutlinedButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class ButtonScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun niaButton_multipleThemes() {
composeTestRule.captureMultiTheme("Button") { description ->
Surface {
NiaButton(onClick = {}, text = { Text("$description Button") })
}
}
}
@Test
fun niaOutlineButton_multipleThemes() {
composeTestRule.captureMultiTheme("Button", "OutlineButton") { description ->
Surface {
NiaOutlinedButton(onClick = {}, text = { Text("$description OutlineButton") })
}
}
}
@Test
fun niaButton_leadingIcon_multipleThemes() {
composeTestRule.captureMultiTheme(
name = "Button",
overrideFileName = "ButtonLeadingIcon",
shouldCompareAndroidTheme = false,
) { description ->
Surface {
NiaButton(
onClick = {},
text = { Text("$description Icon Button") },
leadingIcon = { Icon(imageVector = NiaIcons.Add, contentDescription = null) },
)
}
}
}
}

@ -0,0 +1,98 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.accompanist.testharness.TestHarness
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaFilterChip
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class FilterChipScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun filterChip_multipleThemes() {
composeTestRule.captureMultiTheme("FilterChip") { description ->
Surface {
NiaFilterChip(selected = false, onSelectedChange = {}) {
Text("Unselected chip")
}
}
}
}
@Test
fun filterChip_multipleThemes_selected() {
composeTestRule.captureMultiTheme("FilterChip", "FilterChipSelected") { description ->
Surface {
NiaFilterChip(selected = true, onSelectedChange = {}) {
Text("Selected Chip")
}
}
}
}
@Test
fun filterChip_hugeFont() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalInspectionMode provides true,
) {
TestHarness(fontScale = 2f, size = DpSize(80.dp, 40.dp)) {
NiaTheme {
NiaBackground {
NiaFilterChip(selected = true, onSelectedChange = {}) {
Text("Chip")
}
}
}
}
}
}
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/FilterChip/FilterChip_fontScale2.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
}

@ -0,0 +1,80 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconToggleButton
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class IconButtonScreenshotTests {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun iconButton_multipleThemes() {
composeTestRule.captureMultiTheme("IconButton") { description ->
NiaIconToggleExample(false)
}
}
@Test
fun iconButton_unchecked_multipleThemes() {
composeTestRule.captureMultiTheme("IconButton", "IconButtonUnchecked") { description ->
Surface {
NiaIconToggleExample(true)
}
}
}
@Composable
private fun NiaIconToggleExample(checked: Boolean) {
NiaIconToggleButton(
checked = checked,
onCheckedChange = { },
icon = {
Icon(
imageVector = NiaIcons.BookmarkBorder,
contentDescription = null,
)
},
checkedIcon = {
Icon(
imageVector = NiaIcons.Bookmark,
contentDescription = null,
)
},
)
}
}

@ -0,0 +1,83 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Surface
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaOverlayLoadingWheel
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class LoadingWheelScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun loadingWheel_multipleThemes() {
composeTestRule.captureMultiTheme("LoadingWheel") {
Surface {
NiaLoadingWheel(contentDesc = "test")
}
}
}
@Test
fun overlayLoadingWheel_multipleThemes() {
composeTestRule.captureMultiTheme("LoadingWheel", "OverlayLoadingWheel") {
Surface {
NiaOverlayLoadingWheel(contentDesc = "test")
}
}
}
@Test
fun loadingWheelAnimation() {
composeTestRule.mainClock.autoAdvance = false
composeTestRule.setContent {
NiaTheme() {
NiaLoadingWheel(contentDesc = "")
}
}
// Try multiple frames of the animation; some arbitrary, some synchronized with duration.
listOf(20L, 115L, 724L, 1000L).forEach { deltaTime ->
composeTestRule.mainClock.advanceTimeBy(deltaTime)
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/LoadingWheel/LoadingWheel_animation_$deltaTime.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
}
}

@ -0,0 +1,108 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.accompanist.testharness.TestHarness
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBar
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaNavigationBarItem
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class NavigationScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun navigation_multipleThemes() {
composeTestRule.captureMultiTheme("Navigation") {
Surface {
NiaNavigationBarExample()
}
}
}
@Test
fun navigation_hugeFont() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalInspectionMode provides true,
) {
TestHarness(fontScale = 2f) {
NiaTheme {
NiaNavigationBarExample("Looong item")
}
}
}
}
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/Navigation" +
"/Navigation_fontScale2.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
@Composable
private fun NiaNavigationBarExample(label: String = "Item") {
NiaNavigationBar {
(0..2).forEach { index ->
NiaNavigationBarItem(
icon = {
Icon(
imageVector = NiaIcons.UpcomingBorder,
contentDescription = "",
)
},
selectedIcon = {
Icon(
imageVector = NiaIcons.Upcoming,
contentDescription = "",
)
},
label = { Text(label) },
selected = index == 0,
onClick = { },
)
}
}
}
}

@ -0,0 +1,94 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.accompanist.testharness.TestHarness
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTab
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTabRow
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class TabsScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun tabs_multipleThemes() {
composeTestRule.captureMultiTheme("Tabs") {
NiaTabsExample()
}
}
@Test
fun tabs_hugeFont() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalInspectionMode provides true,
) {
TestHarness(fontScale = 2f) {
NiaTheme {
NiaTabsExample("Looooong item")
}
}
}
}
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/Tabs/Tabs_fontScale2.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
@Composable
private fun NiaTabsExample(label: String = "Topics") {
Surface {
val titles = listOf(label, "People")
NiaTabRow(selectedTabIndex = 0) {
titles.forEachIndexed { index, title ->
NiaTab(
selected = index == 0,
onClick = { },
text = { Text(text = title) },
)
}
}
}
}
}

@ -0,0 +1,79 @@
/*
* 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.core.designsystem
import androidx.activity.ComponentActivity
import androidx.compose.material3.Text
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.accompanist.testharness.TestHarness
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class TagScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun Tag_multipleThemes() {
composeTestRule.captureMultiTheme("Tag") {
NiaTopicTag(followed = true, onClick = {}) {
Text("TOPIC")
}
}
}
@Test
fun tag_hugeFont() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalInspectionMode provides true,
) {
TestHarness(fontScale = 2f) {
NiaTheme {
NiaTopicTag(followed = true, onClick = {}) {
Text("LOOOOONG TOPIC")
}
}
}
}
}
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/Tag/Tag_fontScale2.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
}

@ -0,0 +1,90 @@
/*
* 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.core.designsystem
import android.R.string
import androidx.activity.ComponentActivity
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import com.github.takahirom.roborazzi.captureRoboImage
import com.google.accompanist.testharness.TestHarness
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopAppBar
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
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.testing.util.captureMultiTheme
import dagger.hilt.android.testing.HiltTestApplication
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
@OptIn(ExperimentalMaterial3Api::class)
@RunWith(RobolectricTestRunner::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(application = HiltTestApplication::class, sdk = [33], qualifiers = "480dpi")
@LooperMode(LooperMode.Mode.PAUSED)
class TopAppBarScreenshotTests() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun topAppBar_multipleThemes() {
composeTestRule.captureMultiTheme("TopAppBar") {
NiaTopAppBarExample()
}
}
@Test
fun topAppBar_hugeFont() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalInspectionMode provides true,
) {
TestHarness(fontScale = 2f) {
NiaTheme {
NiaTopAppBarExample()
}
}
}
}
composeTestRule.onRoot()
.captureRoboImage(
"src/test/screenshots/TopAppBar/TopAppBar_fontScale2.png",
roborazziOptions = DefaultRoborazziOptions,
)
}
@Composable
private fun NiaTopAppBarExample() {
NiaTopAppBar(
titleRes = android.R.string.untitled,
navigationIcon = NiaIcons.Search,
navigationIconContentDescription = "Navigation icon",
actionIcon = NiaIcons.MoreVert,
actionIconContentDescription = "Action icon",
)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save