Skip to content

Commit c1caafe

Browse files
committed
Make test more robust by running screenshot test on smaller components
1 parent d70a724 commit c1caafe

File tree

37 files changed

+382
-336
lines changed

37 files changed

+382
-336
lines changed

app/src/main/java/dev/aungkyawpaing/ccdroidx/feature/notification/prompt/NotificationPrompt.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ fun NotificationPromptContent(
8282
start.linkTo(notificationIcon.end)
8383
end.linkTo(parent.end)
8484
width = Dimension.fillToConstraints
85-
linkTo(top = closeIcon.bottom, bottom = enableButton.top, bias = 0.0f)
85+
linkTo(top = closeIcon.bottom, bottom = enableButton.top, bias = 0.5f)
8686
}
8787
.padding(start = 8.dp)
8888
)

app/src/main/java/dev/aungkyawpaing/ccdroidx/feature/projectlist/ProjectListPage.kt

+51-161
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,15 @@
11
package dev.aungkyawpaing.ccdroidx.feature.projectlist
22

3-
import androidx.compose.foundation.layout.Column
43
import androidx.compose.foundation.layout.fillMaxSize
54
import androidx.compose.foundation.layout.padding
65
import androidx.compose.material.icons.Icons
76
import androidx.compose.material.icons.filled.Add
8-
import androidx.compose.material.icons.filled.Settings
9-
import androidx.compose.material.icons.filled.Sync
107
import androidx.compose.material3.AlertDialog
11-
import androidx.compose.material3.ExperimentalMaterial3Api
128
import androidx.compose.material3.FloatingActionButton
139
import androidx.compose.material3.Icon
14-
import androidx.compose.material3.IconButton
15-
import androidx.compose.material3.MaterialTheme
1610
import androidx.compose.material3.Scaffold
1711
import androidx.compose.material3.Text
1812
import androidx.compose.material3.TextButton
19-
import androidx.compose.material3.TopAppBar
20-
import androidx.compose.material3.TopAppBarDefaults
2113
import androidx.compose.runtime.Composable
2214
import androidx.compose.runtime.livedata.observeAsState
2315
import androidx.compose.runtime.mutableStateOf
@@ -26,9 +18,6 @@ import androidx.compose.runtime.rememberCoroutineScope
2618
import androidx.compose.ui.Modifier
2719
import androidx.compose.ui.platform.LocalContext
2820
import androidx.compose.ui.res.stringResource
29-
import androidx.compose.ui.semantics.contentDescription
30-
import androidx.compose.ui.semantics.semantics
31-
import androidx.compose.ui.semantics.stateDescription
3221
import androidx.compose.ui.unit.dp
3322
import androidx.constraintlayout.compose.ConstraintLayout
3423
import androidx.constraintlayout.compose.Dimension
@@ -41,122 +30,36 @@ import dev.aungkyawpaing.ccdroidx.common.Project
4130
import dev.aungkyawpaing.ccdroidx.common.extensions.findActivity
4231
import dev.aungkyawpaing.ccdroidx.feature.browser.openInBrowser
4332
import dev.aungkyawpaing.ccdroidx.feature.destinations.AddProjectDialogScreenDestination
44-
import dev.aungkyawpaing.ccdroidx.feature.destinations.SettingsPageDestination
4533
import dev.aungkyawpaing.ccdroidx.feature.notification.prompt.NotificationPrompt
4634
import dev.aungkyawpaing.ccdroidx.feature.notification.prompt.NotificationPromptViewModel
4735
import dev.aungkyawpaing.ccdroidx.feature.projectlist.component.ProjectList
48-
import dev.aungkyawpaing.ccdroidx.feature.sync.LastSyncedState
36+
import dev.aungkyawpaing.ccdroidx.feature.projectlist.component.ProjectListTopAppBar
4937
import dev.aungkyawpaing.ccdroidx.feature.sync.LastSyncedStatus
5038
import kotlinx.coroutines.launch
51-
import org.ocpsoft.prettytime.PrettyTime
5239
import java.time.Clock
5340

54-
@Composable
55-
private fun getSubtitleText(lastSyncedStatus: LastSyncedStatus?, clock: Clock): String =
56-
if (lastSyncedStatus == null) {
57-
stringResource(R.string.welcome)
58-
} else {
59-
when (lastSyncedStatus.lastSyncedState) {
60-
LastSyncedState.SYNCING -> {
61-
stringResource(R.string.syncing)
62-
}
63-
64-
LastSyncedState.SUCCESS, LastSyncedState.FAILED -> {
65-
stringResource(
66-
R.string.last_synced_x,
67-
PrettyTime(clock.instant()).format(lastSyncedStatus.lastSyncedDateTime)
68-
)
69-
}
70-
}
71-
}
72-
73-
@OptIn(ExperimentalMaterial3Api::class)
74-
@Composable
75-
fun ProjectListTopAppBar(
76-
lastSyncedStatus: LastSyncedStatus?,
77-
onProgressSyncedEvent: Boolean,
78-
onPressSync: () -> Unit,
79-
clearOnProgressSyncedEvent: () -> Unit,
80-
navigator: DestinationsNavigator,
81-
clock: Clock
82-
) {
83-
TopAppBar(
84-
title = {
85-
Column {
86-
Text(stringResource(id = R.string.app_name), modifier = Modifier.semantics {
87-
contentDescription = "CC Droid X"
88-
})
89-
Text(
90-
text = getSubtitleText(lastSyncedStatus, clock),
91-
style = MaterialTheme.typography.bodyMedium
92-
)
93-
}
94-
},
95-
actions = {
96-
IconButton(
97-
onClick = onPressSync,
98-
modifier = Modifier.semantics {
99-
stateDescription = if (onProgressSyncedEvent) "Finished syncing" else ""
100-
101-
if (onProgressSyncedEvent) {
102-
clearOnProgressSyncedEvent()
103-
}
104-
}
105-
) {
106-
Icon(
107-
Icons.Filled.Sync,
108-
contentDescription = stringResource(R.string.menu_item_sync_project_status)
109-
)
110-
}
111-
112-
IconButton(onClick = {
113-
navigator.navigate(SettingsPageDestination())
114-
}) {
115-
Icon(
116-
Icons.Filled.Settings,
117-
contentDescription = stringResource(R.string.menu_item_settings)
118-
)
119-
}
120-
},
121-
colors = TopAppBarDefaults.topAppBarColors(
122-
containerColor = MaterialTheme.colorScheme.primary,
123-
titleContentColor = MaterialTheme.colorScheme.onPrimary,
124-
actionIconContentColor = MaterialTheme.colorScheme.onPrimary
125-
)
126-
)
127-
}
128-
12941
@Composable
13042
fun DeleteConfirmationDialog(
131-
onConfirmDelete: () -> Unit,
132-
onDismiss: () -> Unit
43+
onConfirmDelete: () -> Unit, onDismiss: () -> Unit
13344
) {
134-
AlertDialog(
135-
onDismissRequest = onDismiss,
136-
title = {
137-
Text(text = stringResource(id = R.string.confirm_delete_title))
138-
},
139-
text = {
140-
Text(text = stringResource(id = R.string.confirm_delete_message))
141-
},
142-
confirmButton = {
143-
TextButton(
144-
onClick = {
145-
onConfirmDelete()
146-
onDismiss()
147-
}
148-
) {
149-
Text(stringResource(id = R.string.action_item_project_delete_project))
150-
}
151-
},
152-
dismissButton = {
153-
TextButton(
154-
onClick = onDismiss
155-
) {
156-
Text(stringResource(id = android.R.string.cancel))
157-
}
45+
AlertDialog(onDismissRequest = onDismiss, title = {
46+
Text(text = stringResource(id = R.string.confirm_delete_title))
47+
}, text = {
48+
Text(text = stringResource(id = R.string.confirm_delete_message))
49+
}, confirmButton = {
50+
TextButton(onClick = {
51+
onConfirmDelete()
52+
onDismiss()
53+
}) {
54+
Text(stringResource(id = R.string.action_item_project_delete_project))
15855
}
159-
)
56+
}, dismissButton = {
57+
TextButton(
58+
onClick = onDismiss
59+
) {
60+
Text(stringResource(id = android.R.string.cancel))
61+
}
62+
})
16063
}
16164

16265
@Composable
@@ -176,20 +79,17 @@ fun ProjectListPageContent(
17679
val context = LocalContext.current
17780
val scope = rememberCoroutineScope()
17881

179-
Scaffold(
180-
topBar = {
181-
ProjectListTopAppBar(
182-
lastSyncedStatus,
183-
onProgressSyncedEvent,
184-
onPressSync,
185-
clearOnProgressSyncedEvent,
186-
navigator,
187-
clock
188-
)
189-
}
190-
) { contentPadding ->
191-
val deleteConfirmDialog =
192-
remember { mutableStateOf<Project?>(null) }
82+
Scaffold(topBar = {
83+
ProjectListTopAppBar(
84+
lastSyncedStatus,
85+
onProgressSyncedEvent,
86+
onPressSync,
87+
clearOnProgressSyncedEvent,
88+
navigator,
89+
clock
90+
)
91+
}) { contentPadding ->
92+
val deleteConfirmDialog = remember { mutableStateOf<Project?>(null) }
19393

19494
ConstraintLayout(
19595
modifier = Modifier
@@ -199,28 +99,22 @@ fun ProjectListPageContent(
19999

200100
val (notificationPrompt, projectListComponent, fabAddProject) = createRefs()
201101

202-
ProjectList(
203-
projectList = projectList,
204-
onOpenRepoClick = { project ->
205-
context.findActivity()?.let { activity ->
206-
scope.launch {
207-
openInBrowser(activity, project.webUrl)
208-
}
102+
ProjectList(projectList = projectList, onOpenRepoClick = { project ->
103+
context.findActivity()?.let { activity ->
104+
scope.launch {
105+
openInBrowser(activity, project.webUrl)
209106
}
210-
},
211-
onDeleteClick = { project ->
212-
deleteConfirmDialog.value = project
213-
},
214-
onToggleMute = onToggleMute,
215-
modifier = Modifier.constrainAs(projectListComponent) {
216-
end.linkTo(parent.end)
217-
start.linkTo(parent.start)
218-
bottom.linkTo(notificationPrompt.top)
219-
top.linkTo(parent.top)
220-
height = Dimension.fillToConstraints
221-
width = Dimension.fillToConstraints
222-
},
223-
clock = clock
107+
}
108+
}, onDeleteClick = { project ->
109+
deleteConfirmDialog.value = project
110+
}, onToggleMute = onToggleMute, modifier = Modifier.constrainAs(projectListComponent) {
111+
end.linkTo(parent.end)
112+
start.linkTo(parent.start)
113+
bottom.linkTo(notificationPrompt.top)
114+
top.linkTo(parent.top)
115+
height = Dimension.fillToConstraints
116+
width = Dimension.fillToConstraints
117+
}, clock = clock
224118
)
225119

226120
NotificationPrompt(
@@ -240,22 +134,18 @@ fun ProjectListPageContent(
240134
}
241135
.padding(16.dp)) {
242136
Icon(
243-
Icons.Filled.Add,
244-
contentDescription = stringResource(R.string.cd_fab_add_project)
137+
Icons.Filled.Add, contentDescription = stringResource(R.string.cd_fab_add_project)
245138
)
246139
}
247140

248141
}
249142

250143
if (deleteConfirmDialog.value != null) {
251-
DeleteConfirmationDialog(
252-
onConfirmDelete = {
253-
onDeleteProject(deleteConfirmDialog.value!!)
254-
},
255-
onDismiss = {
256-
deleteConfirmDialog.value = null
257-
}
258-
)
144+
DeleteConfirmationDialog(onConfirmDelete = {
145+
onDeleteProject(deleteConfirmDialog.value!!)
146+
}, onDismiss = {
147+
deleteConfirmDialog.value = null
148+
})
259149
}
260150
}
261151
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package dev.aungkyawpaing.ccdroidx.feature.projectlist.component
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.material.icons.Icons
5+
import androidx.compose.material.icons.filled.Settings
6+
import androidx.compose.material.icons.filled.Sync
7+
import androidx.compose.material3.ExperimentalMaterial3Api
8+
import androidx.compose.material3.Icon
9+
import androidx.compose.material3.IconButton
10+
import androidx.compose.material3.MaterialTheme
11+
import androidx.compose.material3.Text
12+
import androidx.compose.material3.TopAppBar
13+
import androidx.compose.material3.TopAppBarDefaults
14+
import androidx.compose.runtime.Composable
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.res.stringResource
17+
import androidx.compose.ui.semantics.contentDescription
18+
import androidx.compose.ui.semantics.semantics
19+
import androidx.compose.ui.semantics.stateDescription
20+
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
21+
import dev.aungkyawpaing.ccdroidx.R
22+
import dev.aungkyawpaing.ccdroidx.feature.destinations.SettingsPageDestination
23+
import dev.aungkyawpaing.ccdroidx.feature.sync.LastSyncedState
24+
import dev.aungkyawpaing.ccdroidx.feature.sync.LastSyncedStatus
25+
import org.ocpsoft.prettytime.PrettyTime
26+
import java.time.Clock
27+
28+
@Composable
29+
private fun getSubtitleText(lastSyncedStatus: LastSyncedStatus?, clock: Clock): String =
30+
if (lastSyncedStatus == null) {
31+
stringResource(R.string.welcome)
32+
} else {
33+
when (lastSyncedStatus.lastSyncedState) {
34+
LastSyncedState.SYNCING -> {
35+
stringResource(R.string.syncing)
36+
}
37+
38+
LastSyncedState.SUCCESS, LastSyncedState.FAILED -> {
39+
stringResource(
40+
R.string.last_synced_x,
41+
PrettyTime(clock.instant()).format(lastSyncedStatus.lastSyncedDateTime)
42+
)
43+
}
44+
}
45+
}
46+
47+
@OptIn(ExperimentalMaterial3Api::class)
48+
@Composable
49+
fun ProjectListTopAppBar(
50+
lastSyncedStatus: LastSyncedStatus?,
51+
onProgressSyncedEvent: Boolean,
52+
onPressSync: () -> Unit,
53+
clearOnProgressSyncedEvent: () -> Unit,
54+
navigator: DestinationsNavigator,
55+
clock: Clock
56+
) {
57+
TopAppBar(
58+
title = {
59+
Column {
60+
Text(stringResource(id = R.string.app_name), modifier = Modifier.semantics {
61+
contentDescription = "CC Droid X"
62+
})
63+
Text(
64+
text = getSubtitleText(lastSyncedStatus, clock),
65+
style = MaterialTheme.typography.bodyMedium
66+
)
67+
}
68+
},
69+
actions = {
70+
IconButton(
71+
onClick = onPressSync,
72+
modifier = Modifier.semantics {
73+
stateDescription = if (onProgressSyncedEvent) "Finished syncing" else ""
74+
75+
if (onProgressSyncedEvent) {
76+
clearOnProgressSyncedEvent()
77+
}
78+
}
79+
) {
80+
Icon(
81+
Icons.Filled.Sync,
82+
contentDescription = stringResource(R.string.menu_item_sync_project_status)
83+
)
84+
}
85+
86+
IconButton(onClick = {
87+
navigator.navigate(SettingsPageDestination())
88+
}) {
89+
Icon(
90+
Icons.Filled.Settings,
91+
contentDescription = stringResource(R.string.menu_item_settings)
92+
)
93+
}
94+
},
95+
colors = TopAppBarDefaults.topAppBarColors(
96+
containerColor = MaterialTheme.colorScheme.primary,
97+
titleContentColor = MaterialTheme.colorScheme.onPrimary,
98+
actionIconContentColor = MaterialTheme.colorScheme.onPrimary
99+
)
100+
)
101+
}

0 commit comments

Comments
 (0)