add library info dialogs and sort name preference

Album and artist context menus gain an Info dialog covering the plural
album artists, sort names and the play and download details the screens
do not show. Multiple artist links collapse into one ellipsised entry
that opens the artist chooser instead of listing each artist.

Sort lists can now prefer regular names over sort names, or only use
sort names for names without Latin letters. The new plain-name indexes
keep those orders out of a temp B-tree.

Also carry plural artists through the Media3 queue so Now Playing and
the mini player keep them, drop the album artist tap ripple, and show
the aggregate download progress in the tab bar where offline mode used
to sit.
This commit is contained in:
austinried
2026-10-20 14:28:32 +00:00
parent 5de5b618d1
commit c56a6d4f83
40 changed files with 3191 additions and 220 deletions
@@ -20,6 +20,7 @@ import com.subtracks.data.model.Source
import com.subtracks.data.prefs.AlbumSort
import com.subtracks.data.prefs.ArtistSort
import com.subtracks.data.prefs.PlaylistSort
import com.subtracks.data.prefs.SortNameMode
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -332,10 +333,18 @@ class LibraryDaoTest {
runTest {
val orders =
listOf(
ALBUM_ORDER_BY_NAME to "index_albums_name",
ALBUM_ORDER_BY_NAME_REVERSED to "index_albums_name",
ALBUM_ORDER_BY_ARTIST to "index_albums_artist",
ALBUM_ORDER_BY_ARTIST_REVERSED to "index_albums_artist",
"${albumNameSortKey(SortNameMode.SortName)}, id" to "index_albums_name",
"${albumNameSortKey(SortNameMode.SortName)} DESC, id DESC" to "index_albums_name",
"${albumNameSortKey(SortNameMode.Name)}, id" to "index_albums_name_plain",
"${albumNameSortKey(SortNameMode.Name)} DESC, id DESC" to "index_albums_name_plain",
"${albumArtistSortKey(SortNameMode.SortName)}, year, " +
"${albumNameSortKey(SortNameMode.SortName)}, id" to "index_albums_artist",
"${albumArtistSortKey(SortNameMode.SortName)} DESC, year DESC, " +
"${albumNameSortKey(SortNameMode.SortName)} DESC, id DESC" to "index_albums_artist",
"${albumArtistSortKey(SortNameMode.Name)}, year, " +
"${albumNameSortKey(SortNameMode.Name)}, id" to "index_albums_artist_plain",
"${albumArtistSortKey(SortNameMode.Name)} DESC, year DESC, " +
"${albumNameSortKey(SortNameMode.Name)} DESC, id DESC" to "index_albums_artist_plain",
ALBUM_ORDER_BY_YEAR to "index_albums_year",
ALBUM_ORDER_BY_YEAR_REVERSED to "index_albums_year",
ALBUM_ORDER_BY_ADDED to "index_albums_added",
@@ -359,8 +368,10 @@ class LibraryDaoTest {
runTest {
val orders =
listOf(
ARTIST_ORDER_BY_NAME to "index_artists_name",
ARTIST_ORDER_BY_NAME_REVERSED to "index_artists_name",
"${artistNameSortKey(SortNameMode.SortName)}, id" to "index_artists_name",
"${artistNameSortKey(SortNameMode.SortName)} DESC, id DESC" to "index_artists_name",
"${artistNameSortKey(SortNameMode.Name)}, id" to "index_artists_name_plain",
"${artistNameSortKey(SortNameMode.Name)} DESC, id DESC" to "index_artists_name_plain",
ARTIST_ORDER_BY_ALBUM_COUNT to "index_artists_albumCount",
ARTIST_ORDER_BY_ALBUM_COUNT_REVERSED to "index_artists_albumCount",
ARTIST_ORDER_BY_STARRED to "index_artists_starred",
@@ -1055,7 +1066,7 @@ class LibraryDaoTest {
val plan =
plan(
"SELECT * ${ALBUMS_FILTER.replace(":search", "'abc'")} " +
"ORDER BY $ALBUM_ORDER_BY_NAME LIMIT 20 OFFSET 0",
"ORDER BY ${albumNameSortKey(SortNameMode.SortName)}, id LIMIT 20 OFFSET 0",
)
assertTrue(plan, plan.contains("album_search"))
@@ -1320,6 +1331,70 @@ class LibraryDaoTest {
)
}
@Test
fun theSortNamePreferenceChoosesTheOrderKey() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertArtists(
listOf(
artist(sourceId, "ar-1", "あいみょん", albumCount = 1).copy(sortName = "Zzz"),
artist(sourceId, "ar-2", "宇多田ヒカル", albumCount = 1).copy(sortName = "Aaa"),
artist(sourceId, "ar-3", "Beatles", albumCount = 1).copy(sortName = "Beatles, The"),
artist(sourceId, "ar-4", "Zebra", albumCount = 1).copy(sortName = "Aaa"),
),
)
dao.upsertAlbums(
listOf(
album(sourceId, "al-1", "Zzz", year = null, starred = null)
.copy(sortName = "Aaa", albumArtistSort = "Zzz", albumArtist = "Beatles"),
album(sourceId, "al-2", "Aaa", year = null, starred = null)
.copy(sortName = "Zzz", albumArtistSort = "Aaa", albumArtist = "Aaa"),
album(sourceId, "al-3", "Beatles", year = null, starred = null)
.copy(sortName = "Beatles, The", albumArtistSort = "Beatles, The", albumArtist = "Zebra"),
),
)
assertEquals(
listOf("宇多田ヒカル", "Zebra", "Beatles", "あいみょん"),
dao.artistsByName(sourceId, 0, "", sortMode = SortNameMode.SortName.name).page().map { it.name },
)
assertEquals(
listOf("Beatles", "Zebra", "あいみょん", "宇多田ヒカル"),
dao.artistsByName(sourceId, 0, "", sortMode = SortNameMode.Name.name).page().map { it.name },
)
assertEquals(
listOf("宇多田ヒカル", "あいみょん", "Zebra", "Beatles"),
dao.artistsByNameReversed(sourceId, 0, "", sortMode = SortNameMode.Name.name).page().map { it.name },
)
assertEquals(
listOf("宇多田ヒカル", "Beatles", "Zebra", "あいみょん"),
dao.artistsByName(sourceId, 0, "", sortMode = SortNameMode.NonLatin.name).page().map { it.name },
)
assertEquals(
listOf("Aaa", "Beatles", "Zzz"),
dao.albumsByName(sourceId, 0, "", sortMode = SortNameMode.Name.name).page().map { it.name },
)
assertEquals(
listOf("Aaa", "Zzz", "Beatles"),
dao.albumsByArtist(sourceId, 0, "", sortMode = SortNameMode.Name.name).page().map { it.name },
)
assertEquals(
listOf("Beatles", "Zebra", "あいみょん", "宇多田ヒカル"),
dao
.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false, SortNameMode.Name))
.page()
.map { it.name },
)
assertEquals(
listOf("Aaa", "Beatles", "Zzz"),
dao
.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Name, false, SortNameMode.Name))
.page()
.map { it.name },
)
}
@Test
fun sortNamesAreSearchableInArtistsAlbumsAndSongs() =
runTest {
@@ -51,6 +51,22 @@ class UserPreferencesTest {
file.delete()
}
@Test
fun theSortNameModeRoundTrips() =
runTest {
val file = File.createTempFile("user-prefs", ".preferences_pb").apply { delete() }
val store = PreferenceDataStoreFactory.create(scope = backgroundScope) { file }
val prefs = UserPreferences(store)
assertEquals(SortNameMode.SortName, prefs.sortNameMode().first())
prefs.setSortNameMode(SortNameMode.NonLatin)
assertEquals(SortNameMode.NonLatin, prefs.sortNameMode().first())
file.delete()
}
@Test
fun theSelectedLibraryTabRoundTrips() =
runTest {
@@ -76,6 +76,7 @@ class LibraryRepositoryTest {
db,
sourceRepository,
NetworkServerActionSink(sourceRepository),
fakeUserPreferences(),
showMessage = { messages += it.resolve(resources) },
scope = scope,
)
@@ -2,6 +2,7 @@ package com.subtracks.playback
import androidx.media3.common.C
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.subtracks.data.model.ArtistRef
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,6 +71,21 @@ class MediaItemTest {
assertEquals(gain, item.replayGain())
}
@Test
fun theItemCarriesEveryArtist() {
val artists = listOf(ArtistRef("ar1", "A"), ArtistRef("ar2", "B"))
val item =
queueMediaItem(
song.copy(artists = artists),
local = null,
stream = "http://server/stream?s=s1",
transcode = false,
)
assertEquals(artists, item.artistRefs())
}
@Test
fun theItemCarriesItsGainContext() {
val item =
@@ -1,6 +1,7 @@
package com.subtracks.playback
import com.subtracks.data.model.AlbumSongItem
import com.subtracks.data.model.ArtistRef
import com.subtracks.data.model.Song
import org.junit.Assert.assertEquals
import org.junit.Test
@@ -57,6 +58,29 @@ class QueueItemTest {
assertEquals(true, item.albumContext)
}
@Test
fun carriesEveryArtist() {
val song =
Song(
sourceId = 1,
id = "s1",
albumId = "al1",
artistId = "ar1",
title = "Song",
album = "Album",
artist = "A • B",
duration = 100,
track = 1,
disc = 1,
starred = null,
artists = listOf(ArtistRef("ar1", "A"), ArtistRef("ar2", "B")),
)
val item = AlbumSongItem(song, coverArt = null).toQueueItem()
assertEquals(listOf("A", "B"), item.artists.map { it.name })
}
@Test
fun aSongWithoutGainCarriesNoGain() {
val item =
@@ -1,7 +1,10 @@
package com.subtracks.ui.components
import com.subtracks.data.model.DownloadProgress
import com.subtracks.data.model.ListDownloadStatus
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -35,4 +38,14 @@ class DownloadIndicatorTest {
fun aFullyDownloadedListIsNotDownloading() {
assertFalse(ListDownloadStatus(total = 4, downloaded = 4).isDownloading())
}
@Test
fun totalDownloadProgressCombinesEveryInFlightTransfer() {
assertEquals(0.4f, DownloadProgress(bytes = 40, total = 100).fraction)
}
@Test
fun queuedDownloadsWithoutASizeShowAnIndeterminateRing() {
assertNull(DownloadProgress(bytes = 0, total = 0).fraction)
}
}
@@ -6,15 +6,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.filter
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasClickAction
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.subtracks.data.model.ArtistRef
import com.subtracks.data.model.Song
import com.subtracks.ui.theme.SubtracksTheme
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,34 +47,69 @@ class ItemContextMenuArtistTest {
)
@Test
fun showsOneEntryPerArtistAndOpensTheChosenOne() {
var opened: String? = null
fun pluralArtistsShareOneEntryThatOpensTheChooser() {
var opened: List<ArtistRef>? = null
composeRule.setContent {
SubtracksTheme {
var show by remember { mutableStateOf(true) }
if (show) {
ItemContextMenu(
target = MenuTarget.Song(song),
actions = ItemActions(viewArtist = { opened = it }),
actions = ItemActions(viewArtist = {}),
onDismiss = { show = false },
onChooseArtists = { opened = it },
)
}
}
}
composeRule.onNodeWithText("A").assertIsDisplayed()
composeRule.onNodeWithText("B").assertIsDisplayed()
val entry = composeRule.onAllNodesWithText("A · B").filterToOne(hasClickAction())
entry.assertIsDisplayed()
assertTrue(
composeRule
.onAllNodesWithText("A")
.filter(hasClickAction())
.fetchSemanticsNodes()
.isEmpty(),
)
assertTrue(
composeRule
.onAllNodesWithText("B")
.filter(hasClickAction())
.fetchSemanticsNodes()
.isEmpty(),
)
composeRule.mainClock.autoAdvance = false
composeRule.onNodeWithText("B").performClick()
entry.performClick()
composeRule.mainClock.advanceTimeBy(1_000)
assertEquals("ar2", opened)
assertEquals(listOf("ar1", "ar2"), opened?.map { it.id })
}
@Test
fun longMenusScrollToTheLastArtist() {
fun aSingleArtistOpensDirectly() {
var opened: String? = null
composeRule.setContent {
SubtracksTheme {
ItemContextMenu(
target = MenuTarget.Song(song.copy(artists = listOf(ArtistRef("ar1", "A")))),
actions = ItemActions(viewArtist = { opened = it }),
onDismiss = {},
)
}
}
composeRule.mainClock.autoAdvance = false
composeRule.onAllNodesWithText("A").filterToOne(hasClickAction()).performClick()
composeRule.mainClock.advanceTimeBy(1_000)
assertEquals("ar1", opened)
}
@Test
fun manyArtistsStillShareOneEntry() {
var opened: List<ArtistRef>? = null
composeRule.setContent {
SubtracksTheme {
ItemContextMenu(
@@ -79,18 +117,23 @@ class ItemContextMenuArtistTest {
MenuTarget.Song(
song.copy(artists = (1..20).map { ArtistRef("ar$it", "Artist $it") }),
),
actions = ItemActions(viewArtist = { opened = it }),
actions = ItemActions(viewArtist = {}),
onDismiss = {},
onChooseArtists = { opened = it },
)
}
}
composeRule.onNodeWithText("Artist 20").performScrollTo().assertIsDisplayed()
val entry =
composeRule
.onAllNodesWithText((1..20).joinToString(" · ") { "Artist $it" })
.filterToOne(hasClickAction())
entry.assertIsDisplayed()
composeRule.mainClock.autoAdvance = false
composeRule.onNodeWithText("Artist 20").performClick()
entry.performClick()
composeRule.mainClock.advanceTimeBy(1_000)
assertEquals("ar20", opened)
assertEquals(20, opened?.size)
}
}
@@ -0,0 +1,86 @@
package com.subtracks.ui.components
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.subtracks.data.model.Album
import com.subtracks.data.model.Artist
import com.subtracks.ui.theme.SubtracksTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class ItemContextMenuInfoTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()
private val album =
Album(
sourceId = 1,
id = "al1",
artistId = "ar1",
name = "Album",
albumArtist = "Artist",
created = 0,
coverArt = null,
genre = null,
year = null,
starred = null,
songCount = 1,
)
@Test
fun anAlbumMenuOpensItsInfo() {
var opened: MenuTarget? = null
composeRule.setContent {
SubtracksTheme {
ItemContextMenu(
target = MenuTarget.Album(album),
actions = ItemActions(),
onDismiss = {},
onInfo = { opened = it },
)
}
}
composeRule.onNodeWithText("Info").performClick()
composeRule.mainClock.advanceTimeBy(1_000)
assertEquals(album, (opened as? MenuTarget.Album)?.album)
}
@Test
fun anArtistMenuOpensItsInfo() {
var opened: MenuTarget? = null
composeRule.setContent {
SubtracksTheme {
ItemContextMenu(
target =
MenuTarget.Artist(
Artist(
sourceId = 1,
id = "ar1",
name = "Artist",
albumCount = 1,
starred = null,
),
),
actions = ItemActions(),
onDismiss = {},
onInfo = { opened = it },
)
}
}
composeRule.onNodeWithText("Info").performClick()
composeRule.mainClock.advanceTimeBy(1_000)
assertEquals("ar1", (opened as? MenuTarget.Artist)?.artist?.id)
}
}
@@ -0,0 +1,81 @@
package com.subtracks.ui.components
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.subtracks.data.model.Album
import com.subtracks.data.model.Artist
import com.subtracks.data.model.ArtistRef
import com.subtracks.ui.theme.SubtracksTheme
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.GraphicsMode
@RunWith(AndroidJUnit4::class)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
class LibraryInfoDialogTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun anAlbumShowsEveryArtistAndBothSortNames() {
composeRule.setContent {
SubtracksTheme {
AlbumInfoDialog(
album =
Album(
sourceId = 1,
id = "al1",
artistId = "ar1",
name = "Album",
albumArtist = "A",
created = 0,
coverArt = null,
genre = "Rock",
year = 1999,
starred = null,
songCount = 12,
artists = listOf(ArtistRef("ar1", "A"), ArtistRef("ar2", "B")),
sortName = "Album, The",
albumArtistSort = "A, The",
),
download = null,
onDismiss = {},
)
}
}
composeRule.onNodeWithText("A · B").assertIsDisplayed()
composeRule.onNodeWithText("Album, The").assertIsDisplayed()
composeRule.onNodeWithText("A, The").assertIsDisplayed()
composeRule.onNodeWithText("1999").assertIsDisplayed()
}
@Test
fun anArtistShowsItsSortNameAndCounts() {
composeRule.setContent {
SubtracksTheme {
ArtistInfoDialog(
artist =
Artist(
sourceId = 1,
id = "ar1",
name = "The Artist",
albumCount = 4,
starred = null,
songCount = 40,
sortName = "Artist, The",
),
onDismiss = {},
)
}
}
composeRule.onNodeWithText("Artist, The").assertIsDisplayed()
composeRule.onNodeWithText("4").assertIsDisplayed()
composeRule.onNodeWithText("40").assertIsDisplayed()
}
}
@@ -90,7 +90,13 @@ class DownloadsViewModelTest {
)
viewModel =
DownloadsViewModel(
LibraryRepository(db, sourceRepository, NetworkServerActionSink(sourceRepository), scope = repoScope),
LibraryRepository(
db,
sourceRepository,
NetworkServerActionSink(sourceRepository),
fakeUserPreferences(),
scope = repoScope,
),
downloadRepository,
controller,
"Unknown artist",
@@ -1,11 +1,15 @@
package com.subtracks.ui.library
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.subtracks.data.model.DownloadProgress
import com.subtracks.data.model.DownloadStatus
import com.subtracks.data.model.SongDownload
import com.subtracks.data.prefs.AlbumSort
import com.subtracks.data.prefs.ArtistSort
import com.subtracks.data.prefs.ListQuery
import com.subtracks.data.prefs.PlaylistSort
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
@@ -24,4 +28,39 @@ class LibraryViewModelTest {
assertEquals(ArtistSort.Name, ListQuery("LegacyArtistOrder").artistSort())
assertEquals(PlaylistSort.Name, ListQuery("LegacyPlaylistOrder").playlistSort())
}
@Test
fun downloadProgressOnlyCountsTheActiveSource() {
val rows = listOf(download(1, bytes = 40, total = 100), download(2, bytes = 10, total = 20))
assertEquals(DownloadProgress(bytes = 40, total = 100), downloadProgress(rows, sourceId = 1))
}
@Test
fun anotherSourcesDownloadsDoNotShowAProgressRing() {
assertNull(downloadProgress(listOf(download(2, bytes = 10, total = 20)), sourceId = 1))
}
@Test
fun aTransferWithoutAKnownSizeIsIndeterminate() {
val rows =
listOf(
download(1, bytes = 100, total = 100),
download(1, bytes = 20, total = 0).copy(songId = "s2"),
)
assertEquals(DownloadProgress(bytes = 0, total = 0), downloadProgress(rows, sourceId = 1))
}
private fun download(
sourceId: Long,
bytes: Long,
total: Long,
) = SongDownload(
sourceId = sourceId,
songId = "s1",
status = DownloadStatus.Running,
bytes = bytes,
total = total,
)
}
@@ -98,7 +98,7 @@ class NowPlayingRouteTest {
artworkFetcher = { ByteArray(0) },
scope = repoScope,
).also { it.start() }
library = LibraryRepository(db, sources, serverActions, scope = repoScope)
library = LibraryRepository(db, sources, serverActions, prefs, scope = repoScope)
handle = FakePlayerHandle()
controller =
PlaybackController(
@@ -90,7 +90,13 @@ class SearchViewModelTest {
)
viewModel =
SearchViewModel(
LibraryRepository(db, sourceRepository, NetworkServerActionSink(sourceRepository), scope = repoScope),
LibraryRepository(
db,
sourceRepository,
NetworkServerActionSink(sourceRepository),
fakeUserPreferences(),
scope = repoScope,
),
sourceRepository,
controller,
)
@@ -12,6 +12,7 @@ import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import com.subtracks.data.prefs.SortNameMode
import com.subtracks.data.prefs.StreamQuality
import com.subtracks.ui.theme.SubtracksTheme
import org.junit.Assert.assertEquals
@@ -31,7 +32,7 @@ class SettingsScreenTest {
@Test
fun theKeepScreenOnRowTurnsTheSettingOn() {
var requested: Boolean? = null
renderSettings(keepScreenOn = false) { requested = it }
renderSettings(keepScreenOn = false, onKeepScreenOnChange = { requested = it })
composeRule.onNodeWithText("Keep screen on").performClick()
composeRule.waitForIdle()
@@ -42,7 +43,7 @@ class SettingsScreenTest {
@Test
fun theKeepScreenOnRowTurnsTheSettingOff() {
var requested: Boolean? = null
renderSettings(keepScreenOn = true) { requested = it }
renderSettings(keepScreenOn = true, onKeepScreenOnChange = { requested = it })
composeRule.onNodeWithText("Keep screen on").performClick()
composeRule.waitForIdle()
@@ -53,7 +54,7 @@ class SettingsScreenTest {
@Test
fun theKeepScreenOnSwitchTurnsTheSettingOn() {
var requested: Boolean? = null
renderSettings(keepScreenOn = false) { requested = it }
renderSettings(keepScreenOn = false, onKeepScreenOnChange = { requested = it })
composeRule
.onNode(isToggleable() and hasAnySibling(hasText("Keep screen on")), useUnmergedTree = true)
@@ -63,9 +64,29 @@ class SettingsScreenTest {
assertEquals(true, requested)
}
@Test
fun theSortNamesRowChangesTheSortNameMode() {
var requested: SortNameMode? = null
renderSettings(
keepScreenOn = false,
onKeepScreenOnChange = {},
onSortNameModeChange = { requested = it },
scrollTo = "Sort names",
)
composeRule.onNodeWithText("Sort names").performClick()
composeRule.onNodeWithText("Use regular names").performClick()
composeRule.onNodeWithText("Done").performClick()
composeRule.waitForIdle()
assertEquals(SortNameMode.Name, requested)
}
private fun renderSettings(
keepScreenOn: Boolean,
onKeepScreenOnChange: (Boolean) -> Unit,
onSortNameModeChange: (SortNameMode) -> Unit = {},
scrollTo: String = "Keep screen on",
) {
composeRule.setContent {
SubtracksTheme {
@@ -88,6 +109,7 @@ class SettingsScreenTest {
onDownloadOverMeteredChange = {},
onScrobblingChange = {},
onKeepScreenOnChange = onKeepScreenOnChange,
onSortNameModeChange = onSortNameModeChange,
onAddServer = {},
onOpenDownloads = {},
onOpenLicenses = {},
@@ -97,7 +119,7 @@ class SettingsScreenTest {
}
}
awaitText("Add server")
composeRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText("Keep screen on"))
composeRule.onNode(hasScrollToNodeAction()).performScrollToNode(hasText(scrollTo))
}
private fun awaitText(text: String) {