Skip to content

Commit f5a0412

Browse files
authored
Merge pull request #330 from android/jv/theme-switcher-ui
Add settings dialog which allows user to select theme and view licenses
2 parents a0b22d8 + b9156f6 commit f5a0412

File tree

50 files changed

+2403
-366
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2403
-366
lines changed

app/LICENSES.md

Lines changed: 931 additions & 0 deletions
Large diffs are not rendered by default.

app/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,13 @@ dependencies {
8282
implementation(project(":feature:foryou"))
8383
implementation(project(":feature:bookmarks"))
8484
implementation(project(":feature:topic"))
85+
implementation(project(":feature:settings"))
8586

87+
implementation(project(":core:common"))
8688
implementation(project(":core:ui"))
8789
implementation(project(":core:designsystem"))
90+
implementation(project(":core:data"))
91+
implementation(project(":core:model"))
8892

8993
implementation(project(":sync:work"))
9094
implementation(project(":sync:sync-test"))
@@ -96,11 +100,13 @@ dependencies {
96100
androidTestImplementation(libs.androidx.navigation.testing)
97101
debugImplementation(libs.androidx.compose.ui.testManifest)
98102

103+
implementation(libs.accompanist.systemuicontroller)
99104
implementation(libs.androidx.activity.compose)
100105
implementation(libs.androidx.appcompat)
101106
implementation(libs.androidx.core.ktx)
102107
implementation(libs.androidx.core.splashscreen)
103108
implementation(libs.androidx.compose.runtime)
109+
implementation(libs.androidx.lifecycle.runtimeCompose)
104110
implementation(libs.androidx.compose.runtime.tracing)
105111
implementation(libs.androidx.compose.material3.windowSizeClass)
106112
implementation(libs.androidx.hilt.navigation.compose)

app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@
1616

1717
package com.google.samples.apps.nowinandroid.ui
1818

19+
import androidx.compose.ui.test.assertCountEquals
1920
import androidx.compose.ui.test.assertIsOn
2021
import androidx.compose.ui.test.assertIsSelected
2122
import androidx.compose.ui.test.junit4.createAndroidComposeRule
23+
import androidx.compose.ui.test.onAllNodesWithText
24+
import androidx.compose.ui.test.onLast
2225
import androidx.compose.ui.test.onNodeWithContentDescription
2326
import androidx.compose.ui.test.onNodeWithText
2427
import androidx.compose.ui.test.performClick
2528
import androidx.test.espresso.Espresso
2629
import androidx.test.espresso.NoActivityResumedException
2730
import com.google.samples.apps.nowinandroid.MainActivity
31+
import com.google.samples.apps.nowinandroid.R
32+
import com.google.samples.apps.nowinandroid.feature.bookmarks.R as BookmarksR
2833
import com.google.samples.apps.nowinandroid.feature.foryou.R as FeatureForyouR
2934
import com.google.samples.apps.nowinandroid.feature.interests.R as FeatureInterestsR
35+
import com.google.samples.apps.nowinandroid.feature.settings.R as SettingsR
3036
import dagger.hilt.android.testing.BindValue
3137
import dagger.hilt.android.testing.HiltAndroidRule
3238
import dagger.hilt.android.testing.HiltAndroidTest
@@ -67,6 +73,11 @@ class NavigationTest {
6773
private lateinit var forYou: String
6874
private lateinit var interests: String
6975
private lateinit var sampleTopic: String
76+
private lateinit var appName: String
77+
private lateinit var saved: String
78+
private lateinit var settings: String
79+
private lateinit var brand: String
80+
private lateinit var ok: String
7081

7182
@Before
7283
fun setup() {
@@ -77,6 +88,11 @@ class NavigationTest {
7788
forYou = getString(FeatureForyouR.string.for_you)
7889
interests = getString(FeatureInterestsR.string.interests)
7990
sampleTopic = "Headlines"
91+
appName = getString(R.string.app_name)
92+
saved = getString(BookmarksR.string.saved)
93+
settings = getString(SettingsR.string.top_app_bar_action_icon_description)
94+
brand = getString(SettingsR.string.brand_android)
95+
ok = getString(SettingsR.string.dismiss_dialog_button_text)
8096
}
8197
}
8298

@@ -146,6 +162,62 @@ class NavigationTest {
146162
}
147163
}
148164

165+
@Test
166+
fun topLevelDestinations_showTopBarWithTitle() {
167+
composeTestRule.apply {
168+
169+
// Verify that the top bar contains the app name on the first screen.
170+
onNodeWithText(appName).assertExists()
171+
172+
// Go to the saved tab, verify that the top bar contains "saved". This means
173+
// we'll have 2 elements with the text "saved" on screen. One in the top bar, and
174+
// one in the bottom navigation.
175+
onNodeWithText(saved).performClick()
176+
onAllNodesWithText(saved).assertCountEquals(2)
177+
178+
// As above but for the interests tab.
179+
onNodeWithText(interests).performClick()
180+
onAllNodesWithText(interests).assertCountEquals(2)
181+
}
182+
}
183+
184+
@Test
185+
fun topLevelDestinations_showSettingsIcon() {
186+
composeTestRule.apply {
187+
onNodeWithContentDescription(settings).assertExists()
188+
189+
onNodeWithText(saved).performClick()
190+
onNodeWithContentDescription(settings).assertExists()
191+
192+
onNodeWithText(interests).performClick()
193+
onNodeWithContentDescription(settings).assertExists()
194+
}
195+
}
196+
197+
@Test
198+
fun whenSettingsIconIsClicked_settingsDialogIsShown() {
199+
composeTestRule.apply {
200+
onNodeWithContentDescription(settings).performClick()
201+
202+
// Check that one of the settings is actually displayed.
203+
onNodeWithText(brand).assertExists()
204+
}
205+
}
206+
207+
@Test
208+
fun whenSettingsDialogDismissed_previousScreenIsDisplayed() {
209+
composeTestRule.apply {
210+
211+
// Navigate to the saved screen, open the settings dialog, then close it.
212+
onNodeWithText(saved).performClick()
213+
onNodeWithContentDescription(settings).performClick()
214+
onNodeWithText(ok).performClick()
215+
216+
// Check that the saved screen is still visible and selected.
217+
onAllNodesWithText(saved).onLast().assertIsSelected()
218+
}
219+
}
220+
149221
/*
150222
* There should always be at most one instance of a top-level destination at the same time.
151223
*/

app/src/androidTest/java/com/google/samples/apps/nowinandroid/ui/NiaAppStateTest.kt

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import androidx.navigation.compose.ComposeNavigator
3030
import androidx.navigation.compose.composable
3131
import androidx.navigation.createGraph
3232
import androidx.navigation.testing.TestNavHostController
33+
import com.google.samples.apps.nowinandroid.core.testing.util.TestNetworkMonitor
34+
import kotlinx.coroutines.flow.collect
35+
import kotlinx.coroutines.launch
36+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
37+
import kotlinx.coroutines.test.runTest
3338
import org.junit.Assert.assertEquals
3439
import org.junit.Assert.assertFalse
3540
import org.junit.Assert.assertTrue
@@ -48,18 +53,24 @@ class NiaAppStateTest {
4853
@get:Rule
4954
val composeTestRule = createComposeRule()
5055

56+
// Create the test dependencies.
57+
private val networkMonitor = TestNetworkMonitor()
58+
59+
// Subject under test.
5160
private lateinit var state: NiaAppState
5261

5362
@Test
54-
fun niaAppState_currentDestination() {
63+
fun niaAppState_currentDestination() = runTest {
5564
var currentDestination: String? = null
5665

5766
composeTestRule.setContent {
5867
val navController = rememberTestNavController()
5968
state = remember(navController) {
6069
NiaAppState(
6170
windowSizeClass = getCompactWindowClass(),
62-
navController = navController
71+
navController = navController,
72+
networkMonitor = networkMonitor,
73+
coroutineScope = backgroundScope
6374
)
6475
}
6576

@@ -76,9 +87,12 @@ class NiaAppStateTest {
7687
}
7788

7889
@Test
79-
fun niaAppState_destinations() {
90+
fun niaAppState_destinations() = runTest {
8091
composeTestRule.setContent {
81-
state = rememberNiaAppState(getCompactWindowClass())
92+
state = rememberNiaAppState(
93+
windowSizeClass = getCompactWindowClass(),
94+
networkMonitor = networkMonitor
95+
)
8296
}
8397

8498
assertEquals(3, state.topLevelDestinations.size)
@@ -88,11 +102,13 @@ class NiaAppStateTest {
88102
}
89103

90104
@Test
91-
fun niaAppState_showBottomBar_compact() {
105+
fun niaAppState_showBottomBar_compact() = runTest {
92106
composeTestRule.setContent {
93107
state = NiaAppState(
94108
windowSizeClass = getCompactWindowClass(),
95-
navController = NavHostController(LocalContext.current)
109+
navController = NavHostController(LocalContext.current),
110+
networkMonitor = networkMonitor,
111+
coroutineScope = backgroundScope
96112
)
97113
}
98114

@@ -101,11 +117,13 @@ class NiaAppStateTest {
101117
}
102118

103119
@Test
104-
fun niaAppState_showNavRail_medium() {
120+
fun niaAppState_showNavRail_medium() = runTest {
105121
composeTestRule.setContent {
106122
state = NiaAppState(
107123
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(800.dp, 800.dp)),
108-
navController = NavHostController(LocalContext.current)
124+
navController = NavHostController(LocalContext.current),
125+
networkMonitor = networkMonitor,
126+
coroutineScope = backgroundScope
109127
)
110128
}
111129

@@ -114,18 +132,41 @@ class NiaAppStateTest {
114132
}
115133

116134
@Test
117-
fun niaAppState_showNavRail_large() {
135+
fun niaAppState_showNavRail_large() = runTest {
136+
118137
composeTestRule.setContent {
119138
state = NiaAppState(
120139
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)),
121-
navController = NavHostController(LocalContext.current)
140+
navController = NavHostController(LocalContext.current),
141+
networkMonitor = networkMonitor,
142+
coroutineScope = backgroundScope
122143
)
123144
}
124145

125146
assertTrue(state.shouldShowNavRail)
126147
assertFalse(state.shouldShowBottomBar)
127148
}
128149

150+
@Test
151+
fun stateIsOfflineWhenNetworkMonitorIsOffline() = runTest(UnconfinedTestDispatcher()) {
152+
153+
composeTestRule.setContent {
154+
state = NiaAppState(
155+
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(900.dp, 1200.dp)),
156+
navController = NavHostController(LocalContext.current),
157+
networkMonitor = networkMonitor,
158+
coroutineScope = backgroundScope
159+
)
160+
}
161+
162+
backgroundScope.launch { state.isOffline.collect() }
163+
networkMonitor.setConnected(false)
164+
assertEquals(
165+
true,
166+
state.isOffline.value
167+
)
168+
}
169+
129170
private fun getCompactWindowClass() = WindowSizeClass.calculateFromSize(DpSize(500.dp, 300.dp))
130171
}
131172

0 commit comments

Comments
 (0)