ui: finish search focus and artist art review follow-ups

Avoid re-requesting search focus when the screen is restored, keep the cheaper album-credit-first art lookup, and test restoration plus association-table invalidation.
This commit is contained in:
austinried
2026-10-19 10:14:01 +00:00
parent 915225f812
commit bb71f4889d
7 changed files with 77 additions and 19 deletions
@@ -677,6 +677,32 @@ class LibraryDaoTest {
dao.upsertSongs(listOf(song(sourceId, "s-1", "Song", starred = null)))
awaitUntil("artist paging observes song writes") { songInvalidated.get() }
val albumCreditInvalidated = AtomicBoolean(false)
val albumCreditPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
albumCreditPaging.registerInvalidatedCallback { albumCreditInvalidated.set(true) }
albumCreditPaging.page()
db.useWriterConnection { connection ->
connection.usePrepared(
"INSERT INTO album_artists (sourceId, albumId, position, artistId, name) " +
"VALUES ($sourceId, 'al-1', 0, 'ar-1', 'Artist')",
) { it.step() }
}
awaitUntil("artist paging observes album artist writes") { albumCreditInvalidated.get() }
val songCreditInvalidated = AtomicBoolean(false)
val songCreditPaging = dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.Name, false))
songCreditPaging.registerInvalidatedCallback { songCreditInvalidated.set(true) }
songCreditPaging.page()
db.useWriterConnection { connection ->
connection.usePrepared(
"INSERT INTO song_artists (sourceId, songId, position, artistId, name) " +
"VALUES ($sourceId, 's-1', 0, 'ar-1', 'Artist')",
) { it.step() }
}
awaitUntil("artist paging observes song artist writes") { songCreditInvalidated.get() }
}
@Test
@@ -46,7 +46,7 @@ class ArtistChooserSheetTest {
}
@Test
fun opensTheChosenArtistBeforeTheSheetFinishesClosing() {
fun opensTheChosenArtistImmediately() {
var opened: ArtistRef? = null
composeRule.setContent {
SubtracksTheme {
@@ -6,7 +6,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.assertIsNotFocused
import androidx.compose.ui.test.hasSetTextAction
import androidx.compose.ui.test.junit4.StateRestorationTester
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
@@ -91,7 +94,7 @@ class SearchScreenTest {
}
@Test
fun typingResumesAtTheEndOfTheQueryAfterTheScreenIsRecreated() {
fun typingKeepsTheCursorAtTheEndAfterTheFieldIsRecreated() {
var query by mutableStateOf("road")
var show by mutableStateOf(true)
composeRule.setContent {
@@ -120,6 +123,21 @@ class SearchScreenTest {
.hideSoftInputFromWindow(composeRule.activity.window.decorView.windowToken, 0)
}
@Test
fun restoringTheScreenDoesNotStealFocus() {
val restorationTester = StateRestorationTester(composeRule)
restorationTester.setContent {
SubtracksTheme {
SearchScreen(query = "road", onQueryChange = {}, results = SearchResults())
}
}
composeRule.onNode(hasSetTextAction()).assertIsFocused()
restorationTester.emulateSavedInstanceStateRestore()
composeRule.onNode(hasSetTextAction()).assertIsNotFocused()
}
@Test
fun idsSharedAcrossTypesDoNotCollide() {
composeRule.setContent {