Skip to content

Commit

Permalink
Merge pull request #784 from Automattic/feature/752-search-history
Browse files Browse the repository at this point in the history
Search history - Merge feature branch
  • Loading branch information
mchowning authored Feb 17, 2023
2 parents 47e95cf + 139d617 commit f91e6d4
Show file tree
Hide file tree
Showing 33 changed files with 2,894 additions and 38 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
7.33
-----
* Bug Fixes:
* New Features:
* Added search history
([#784](https://github.com/Automattic/pocket-casts-android/pull/784)).
* Bug Fixes:
* App no longer crashes when the device browser has been disabled
([#762](https://github.com/Automattic/pocket-casts-android/issues/762)).
* Improve discovery of chromecast devices
([#780](https://github.com/Automattic/pocket-casts-android/issues/780)).

7.32
-----
* Bug Fixes:
* Bug Fixes:
* Ask notifications permission for newly-installed apps on Android 13
([#723](https://github.com/Automattic/pocket-casts-android/issues/723)).

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ class AppDatabaseTest {
AppDatabase.MIGRATION_69_70,
AppDatabase.MIGRATION_70_71,
AppDatabase.MIGRATION_71_72,
AppDatabase.MIGRATION_72_73
AppDatabase.MIGRATION_72_73,
AppDatabase.MIGRATION_73_74
)
.build()
// close the database and release any stream resources when the test finishes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,330 @@
package au.com.shiftyjelly.pocketcasts.models.db

import androidx.room.Room
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import au.com.shiftyjelly.pocketcasts.models.db.dao.SearchHistoryDao
import au.com.shiftyjelly.pocketcasts.models.entity.SearchHistoryItem
import au.com.shiftyjelly.pocketcasts.models.entity.SearchHistoryItem.Folder
import au.com.shiftyjelly.pocketcasts.models.entity.SearchHistoryItem.Podcast
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import java.util.UUID

private const val SEARCH_TERM_TEST1 = "test1"
private const val SEARCH_TERM_TEST2 = "test2"

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class SearchHistoryDaoTest {
lateinit var searchHistoryDao: SearchHistoryDao
lateinit var testDb: AppDatabase

@Before
fun setupDb() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
searchHistoryDao = testDb.searchHistoryDao()
}

@After
fun closeDb() {
testDb.close()
}

/* INSERT */
@Test
fun testInsertSearchTerm() = runTest {
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))

assertTrue(findSearchHistory().first().term == SEARCH_TERM_TEST1)
}

@Test
fun testInsertPodcastSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid))

assertTrue(findSearchHistory().first().podcast?.uuid == uuid)
}
}

@Test
fun testInsertFolderSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))

assertTrue(findSearchHistory().first().folder?.uuid == uuid)
}
}

@Test
fun testInsertEpisodeSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid))

assertTrue(findSearchHistory().first().episode?.uuid == uuid)
}
}

/* MULTIPLE INSERT OR REPLACE */
@Test
fun testMultipleInsertSameSearchTerms() {
runTest {
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))
val modifiedPrevious = findSearchHistory().first().modified
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))

val result = findSearchHistory()
assertEquals("Insert should replace, count should be 1", 1, result.size)
assertTrue(
"Replaced search term should be on top",
result.first().modified > modifiedPrevious
)
}
}

@Test
fun testMultipleInsertUniqueSearchTerms() {
runTest {
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST2))

val result = findSearchHistory()
assertEquals("Unique search terms should be inserted, count should be 2", 2, result.size)
assertEquals(
"Last search term inserted should be on top",
SEARCH_TERM_TEST2,
result.first().term
)
}
}

@Test
fun testMultipleInsertSamePodcastSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid = uuid))
val modifiedPrevious = findSearchHistory().first().modified
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid = uuid))

val result = findSearchHistory()
assertEquals("Same podcast search insert should replace, count should be 1", 1, result.size)
assertTrue(
"Replaced podcast search history item should be on top",
result.first().modified > modifiedPrevious
)
}
}

@Test
fun testMultipleInsertUniquePodcastSearchHistory() {
val uuid1 = UUID.randomUUID().toString()
val uuid2 = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid1))
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid2))

val result = findSearchHistory()
assertEquals("Unique podcast search history should be inserted, count should be 2", 2, result.size)
assertEquals(
"Last podcast search history inserted should be on top",
uuid2,
result.first().podcast?.uuid
)
}
}

@Test
fun testMultipleInsertSameFolderSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))
val modifiedPrevious = findSearchHistory().first().modified
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))

val result = findSearchHistory()
assertEquals("Same folder search insert should replace, count should be 1", 1, result.size)
assertTrue(
"Replaced folder search should be on top",
result.first().modified > modifiedPrevious
)
}
}

@Test
fun testMultipleInsertUniqueFolderSearchHistory() {
val uuid1 = UUID.randomUUID().toString()
val uuid2 = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid1))
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid2))

val result = findSearchHistory()
assertEquals("Unique folder search history should be inserted, count should be 2", 2, result.size)
assertEquals(
"Last folder search history inserted should be on top",
uuid2,
result.first().folder?.uuid
)
}
}

@Test
fun testMultipleInsertSameEpisodeSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid))
val modifiedPrevious = findSearchHistory().first().modified
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid))

val result = findSearchHistory()
assertEquals("Same episode insert should replace, count should be 1", 1, result.size)
assertTrue(
"Replaced episode search should be on top",
result.first().modified > modifiedPrevious
)
}
}

@Test
fun testMultipleInsertUniqueEpisodeSearchHistory() {
val uuid1 = UUID.randomUUID().toString()
val uuid2 = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid1))
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid2))

val result = findSearchHistory()
assertEquals("Unique episode search history should be inserted, count should be 2", 2, result.size)
assertEquals(
"Last episode search history inserted should be on top",
uuid2,
result.first().episode?.uuid
)
}
}

@Test
fun testInsertTooManyItemsKeepsMostRecent() = runTest {
val oldSearch = createTermSearchHistoryItem("old_search", modified = 0)
val recentSearch = createTermSearchHistoryItem("recent_search", modified = 1)
assertTrue(
"second search occurred after first search",
recentSearch.modified > oldSearch.modified
)

searchHistoryDao.deleteAll()
searchHistoryDao.insert(oldSearch)
searchHistoryDao.insert(recentSearch)
assertEquals(
"results contain both searches before truncation",
2,
findSearchHistory().size
)

val limit = 1
searchHistoryDao.truncateHistory(limit)
val results = findSearchHistory()
assertEquals(
"truncation reduces number of items to limit",
limit,
results.size
)
assertTrue(
"truncated results should contain most recent search item (${recentSearch.term}), but instead has (${results.map { it.term }})",
results.first().term == recentSearch.term
)
}

/* DELETE */
@Test
fun testDeleteSearchHistoryItem() {
runTest {
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))

searchHistoryDao.delete(findSearchHistory().first())

assertTrue(findSearchHistory().isEmpty())
}
}

@Test
fun testDeleteAllSearchHistory() {
runTest {
val uuid = UUID.randomUUID().toString()
searchHistoryDao.insert(createTermSearchHistoryItem(SEARCH_TERM_TEST1))
searchHistoryDao.insert(createPodcastSearchHistoryItem(uuid))
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))
searchHistoryDao.insert(createEpisodeSearchHistoryItem(uuid))

searchHistoryDao.deleteAll()

assertTrue(findSearchHistory().isEmpty())
}
}

/* SHOW FOLDERS FILTER */
@Test
fun testFoldersShownInSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))

assertTrue(findSearchHistory(showFolders = true).size == 1)
}
}

@Test
fun testFoldersHiddenInSearchHistory() {
val uuid = UUID.randomUUID().toString()
runTest {
searchHistoryDao.insert(createFolderSearchHistoryItem(uuid))

assertTrue(findSearchHistory(showFolders = false).isEmpty())
}
}

/* HELPER FUNCTIONS */
private fun createTermSearchHistoryItem(
term: String,
modified: Long = System.currentTimeMillis()
) = SearchHistoryItem(term = term, modified = modified)

private fun createPodcastSearchHistoryItem(uuid: String) =
SearchHistoryItem(
podcast = Podcast(
uuid = uuid,
title = "",
author = "",
)
)

private fun createFolderSearchHistoryItem(uuid: String) =
SearchHistoryItem(folder = Folder(uuid = uuid, title = "", color = 0, podcastIds = ""))

private fun createEpisodeSearchHistoryItem(uuid: String) =
SearchHistoryItem(
episode = SearchHistoryItem.Episode(
uuid = uuid,
title = "",
duration = 0.0,
podcastUuid = "",
podcastTitle = "",
artworkUrl = ""
)
)

private suspend fun findSearchHistory(
showFolders: Boolean = true,
) = searchHistoryDao.findAll(showFolders)
}
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,9 @@ class MainActivity :
viewModel.waitingForSignInToShowStories = false
} else if (!settings.getEndOfYearModalHasBeenShown()) {
viewModel.updateStoriesModalShowState(true)
setupEndOfYearLaunchBottomSheet()
launch(Dispatchers.Main) {
if (viewModel.isEndOfYearStoriesEligible()) setupEndOfYearLaunchBottomSheet()
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ fun OnboardingRecommendationsSearchPage(
}

BackHandler {
viewModel.onBackPressed()
onBackPressed()
}

Expand All @@ -77,7 +78,10 @@ fun OnboardingRecommendationsSearchPage(
) {
ThemedTopAppBar(
title = stringResource(LR.string.onboarding_find_podcasts),
onNavigationClick = onBackPressed
onNavigationClick = {
viewModel.onBackPressed()
onBackPressed()
}
)

SearchBar(
Expand Down
Loading

0 comments on commit f91e6d4

Please sign in to comment.