From 6a4a01dcf5deb6a084cc94c6307397c149568eef Mon Sep 17 00:00:00 2001 From: Don Turner Date: Wed, 19 Oct 2022 18:18:39 +0100 Subject: [PATCH] Add tests --- .../apps/nowinandroid/ui/NavigationTest.kt | 29 +++++++ .../feature/settings/SettingsDialogTest.kt | 83 +++++++++++++++++++ .../feature/settings/SettingsDialog.kt | 13 +-- .../feature/settings/SettingsViewModel.kt | 2 +- .../settings/src/main/res/values/strings.xml | 1 + .../feature/settings/SettingsViewModelTest.kt | 73 ++++++++++++++++ 6 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt create mode 100644 feature/settings/src/test/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt diff --git a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index abbf774fe..0d00f6ed5 100644 --- a/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -75,6 +75,8 @@ class NavigationTest { private lateinit var appName: String private lateinit var saved: String private lateinit var settings: String + private lateinit var brand: String + private lateinit var ok: String @Before fun setup() { @@ -88,6 +90,8 @@ class NavigationTest { appName = getString(R.string.app_name) saved = getString(BookmarksR.string.saved) settings = getString(SettingsR.string.top_app_bar_action_icon_description) + brand = getString(SettingsR.string.brand_android) + ok = getString(SettingsR.string.dismiss_dialog_button_text) } } @@ -189,6 +193,31 @@ class NavigationTest { } } + @Test + fun whenSettingsIconIsClicked_settingsDialogIsShown() { + composeTestRule.apply { + onNodeWithContentDescription(settings).performClick() + + // Check that one of the settings is actually displayed. + onNodeWithText(brand).assertExists() + } + } + + @Test + fun whenSettingsDialogDismissed_previousScreenIsDisplayed(){ + + composeTestRule.apply { + + // Navigate to the saved screen, open the settings dialog, then close it. + onNodeWithText(saved).performClick() + onNodeWithContentDescription(settings).performClick() + onNodeWithText(ok).performClick() + + // Check that the saved screen is still visible and selected. + onNodeWithText(saved).assertIsSelected() + } + } + /* * There should always be at most one instance of a top-level destination at the same time. */ diff --git a/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt b/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt new file mode 100644 index 000000000..edd281c89 --- /dev/null +++ b/feature/settings/src/androidTest/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.feature.settings + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.assertIsSelected +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithText +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.DARK +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID +import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading +import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success +import org.junit.Rule +import org.junit.Test + +class SettingsDialogTest { + + @get:Rule + val composeTestRule = createAndroidComposeRule() + + private fun getString(id: Int) = composeTestRule.activity.resources.getString(id) + + @Test + fun whenLoading_showsLoadingText() { + + composeTestRule.setContent { + SettingsDialog( + settingsUiState = Loading, + onDismiss = { }, + onChangeThemeBrand = {}, + onChangeDarkThemeConfig = {} + ) + } + + composeTestRule + .onNodeWithText(getString(R.string.loading)) + .assertExists() + } + + @Test + fun whenStateIsSuccess_allSettingsAreDisplayed(){ + composeTestRule.setContent { + SettingsDialog( + settingsUiState = Success( + UserEditableSettings( + brand = ANDROID, + darkThemeConfig = DARK + ) + ), + onDismiss = { }, + onChangeThemeBrand = {}, + onChangeDarkThemeConfig = {} + ) + } + + // Check that all the possible settings are displayed. + composeTestRule.onNodeWithText(getString(R.string.brand_default)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.brand_android)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.dark_mode_config_system_default) + ).assertExists() + composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_light)).assertExists() + composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_dark)).assertExists() + + // Check that the correct settings are selected. + composeTestRule.onNodeWithText(getString(R.string.brand_android)).assertIsSelected() + composeTestRule.onNodeWithText(getString(R.string.dark_mode_config_dark)).assertIsSelected() + } +} \ No newline at end of file diff --git a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index 518262fa9..33ebd53b1 100644 --- a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -25,8 +25,10 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme @@ -53,6 +55,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.LIGH import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT +import com.google.samples.apps.nowinandroid.feature.settings.R.string import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success @@ -73,8 +76,8 @@ internal fun SettingsDialog( @Composable fun SettingsDialog( - onDismiss: () -> Unit, settingsUiState: SettingsUiState, + onDismiss: () -> Unit, onChangeThemeBrand: (themeBrand: ThemeBrand) -> Unit, onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit ) { @@ -88,8 +91,8 @@ fun SettingsDialog( ) }, text = { - Column { - Divider() + Divider() + Column (Modifier.verticalScroll(rememberScrollState())) { when (settingsUiState) { Loading -> { Text( @@ -111,7 +114,7 @@ fun SettingsDialog( }, confirmButton = { Text( - text = "OK", + text = stringResource(string.dismiss_dialog_button_text), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, modifier = Modifier @@ -182,7 +185,7 @@ fun SettingsDialogThemeChooserRow( .selectable( selected = selected, role = Role.RadioButton, - onClick = onClick + onClick = onClick, ) .padding(8.dp), verticalAlignment = Alignment.CenterVertically diff --git a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt index 96394722c..7ff9b0ca3 100644 --- a/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt @@ -73,5 +73,5 @@ data class UserEditableSettings(val brand: ThemeBrand, val darkThemeConfig: Dark sealed interface SettingsUiState { object Loading : SettingsUiState - class Success(val settings: UserEditableSettings) : SettingsUiState + data class Success(val settings: UserEditableSettings) : SettingsUiState } \ No newline at end of file diff --git a/feature/settings/src/main/res/values/strings.xml b/feature/settings/src/main/res/values/strings.xml index 85998ced4..e37ff439d 100644 --- a/feature/settings/src/main/res/values/strings.xml +++ b/feature/settings/src/main/res/values/strings.xml @@ -26,4 +26,5 @@ System default Light Dark + OK \ No newline at end of file diff --git a/feature/settings/src/test/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt b/feature/settings/src/test/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt new file mode 100644 index 000000000..a78cf14ab --- /dev/null +++ b/feature/settings/src/test/java/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.feature.settings + +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.DARK +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID +import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository +import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule +import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading +import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success +import junit.framework.Assert.assertEquals +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class SettingsViewModelTest { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val userDataRepository = TestUserDataRepository() + + private lateinit var viewModel: SettingsViewModel + + @Before + fun setup() { + viewModel = SettingsViewModel(userDataRepository) + } + + @Test + fun stateIsInitiallyLoading() = runTest { + assertEquals(Loading, viewModel.settingsUiState.value) + } + + @Test + fun stateIsSuccessAfterUserDataLoaded() = runTest { + + val collectJob = + launch(UnconfinedTestDispatcher()) { viewModel.settingsUiState.collect() } + + userDataRepository.setThemeBrand(ANDROID) + userDataRepository.setDarkThemeConfig(DARK) + + assertEquals( + Success( + UserEditableSettings( + brand = ANDROID, + darkThemeConfig = DARK + ) + ), + viewModel.settingsUiState.value) + + collectJob.cancel() + } +} \ No newline at end of file