Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.main.LEFT_DRAWER_BOOKMARK_ITEM_TESTING_TAG
import org.kiwix.kiwixmobile.core.main.reader.READER_BOTTOM_BAR_BOOKMARK_BUTTON_TESTING_TAG
import org.kiwix.kiwixmobile.core.main.reader.READER_BOTTOM_BAR_HOME_BUTTON_TESTING_TAG
import org.kiwix.kiwixmobile.core.main.reader.READER_BOTTOM_BAR_PREVIOUS_SCREEN_BUTTON_TESTING_TAG
import org.kiwix.kiwixmobile.core.page.DELETE_MENU_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.page.NO_ITEMS_TEXT_TESTING_TAG
Expand Down Expand Up @@ -151,14 +152,6 @@ class BookmarksRobot : BaseRobot() {
})
}

fun assertBookmarkRemoved(composeTestRule: ComposeTestRule) {
pauseForBetterTestPerformance()
composeTestRule.apply {
waitForIdle()
composeTestRule.onNodeWithText("Test Zim").assertDoesNotExist()
}
}

private fun pauseForBetterTestPerformance() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS_FOR_SEARCH_TEST.toLong())
}
Expand Down Expand Up @@ -255,6 +248,19 @@ class BookmarksRobot : BaseRobot() {
}
}

fun clickOnHomeButton(composeTestRule: ComposeContentTestRule) {
composeTestRule.apply {
waitForIdle()
// wait for disappearing the snack-bar after removing the bookmark
waitUntilTimeout(TEST_PAUSE_MS_FOR_DOWNLOAD_TEST.toLong())
waitUntil(TEST_PAUSE_MS_FOR_DOWNLOAD_TEST.toLong()) {
onNodeWithTag(READER_BOTTOM_BAR_HOME_BUTTON_TESTING_TAG).isDisplayed()
}
onNodeWithTag(READER_BOTTOM_BAR_HOME_BUTTON_TESTING_TAG)
.performClick()
}
}

fun openBookmarkInReader(composeTestRule: ComposeContentTestRule) {
testFlakyView({
composeTestRule.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import android.content.Context
import android.content.Intent
import androidx.compose.ui.test.junit4.accessibility.enableAccessibilityChecks
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.navigation.NavOptions
Expand All @@ -34,6 +36,7 @@ import com.google.android.apps.common.testing.accessibility.framework.Accessibil
import com.google.android.apps.common.testing.accessibility.framework.checks.DuplicateClickableBoundsCheck
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.runBlocking
import org.hamcrest.Matchers.anyOf
import org.junit.Before
Expand All @@ -45,6 +48,7 @@ import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.main.ZIM_FILE_URI_KEY
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
import org.kiwix.kiwixmobile.core.ui.components.NAVIGATION_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
Expand All @@ -54,6 +58,7 @@ import org.kiwix.kiwixmobile.main.topLevel
import org.kiwix.kiwixmobile.testutils.RetryRule
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.TEST_PAUSE_MS_FOR_DOWNLOAD_TEST
import org.kiwix.kiwixmobile.testutils.TestUtils.TEST_PAUSE_MS_FOR_SNACKBAR
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
import org.kiwix.kiwixmobile.ui.KiwixDestination
import org.kiwix.libkiwix.Book
Expand Down Expand Up @@ -115,90 +120,13 @@ class LibkiwixBookmarkTest : BaseActivityTest() {
composeTestRule.enableAccessibilityChecks(accessibilityValidator)
}

@Test
fun testBookmarks() {
openZimFileInReader()
bookmarks {
// delete any bookmark if already saved to properly perform this test case.
longClickOnSaveBookmarkImage(composeTestRule)
clickOnTrashIcon(composeTestRule)
assertDeleteBookmarksDialogDisplayed(composeTestRule)
clickOnDeleteButton(composeTestRule)
assertNoBookMarkTextDisplayed(composeTestRule)
pressBack()
waitComposeToSettleViews()
// Test saving bookmark
clickOnSaveBookmarkImage(composeTestRule)
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
assertBookmarkSaved(composeTestRule)
pressBack()
// Test removing bookmark
waitComposeToSettleViews()
clickOnSaveBookmarkImage(composeTestRule)
longClickOnSaveBookmarkImage(composeTestRule, TEST_PAUSE_MS_FOR_DOWNLOAD_TEST.toLong())
assertBookmarkRemoved(composeTestRule)
pressBack()
// Save the bookmark to test whether it remains saved after the application restarts or not.
waitComposeToSettleViews()
clickOnSaveBookmarkImage(composeTestRule)
waitComposeToSettleViews()
// Close the application.
InstrumentationRegistry.getInstrumentation().uiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_HOME
)
// wait a bit
waitComposeToSettleViews()
// reopen the application to test that book remains saved or not.
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
context.startActivity(intent)
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
waitComposeToSettleViews()
topLevel {
clickBookmarksOnNavDrawer(kiwixMainActivity as CoreMainActivity, composeTestRule) {
assertBookmarkSaved(composeTestRule)
}
}
}
}

private fun waitComposeToSettleViews() {
composeTestRule.apply {
waitForIdle()
waitUntilTimeout()
}
}

@Test
fun testBookMarkPageOpenInReader() {
openZimFileInReader()
bookmarks {
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
clickOnTrashIcon(composeTestRule)
assertDeleteBookmarksDialogDisplayed(composeTestRule)
clickOnDeleteButton(composeTestRule)
assertNoBookMarkTextDisplayed(composeTestRule)
pressBack()
waitComposeToSettleViews() // to properly load the ZIM file in reader.
assertZimFileLoadedIntoTheReader(composeTestRule)
clickOnAndroidArticle(composeTestRule)
waitComposeToSettleViews()
assertAndroidArticleLoadedInReader(composeTestRule)
waitComposeToSettleViews()
// Save bookmark
clickOnSaveBookmarkImage(composeTestRule)
// open previous page
clickOnBackwardButton(composeTestRule)
// open bookmark screen.
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
// tries to open the bookmark page in reader.
openBookmarkInReader(composeTestRule)
waitComposeToSettleViews()
assertAndroidArticleLoadedInReader(composeTestRule)
}
}

@Test
fun testSavedBookmarksShowingOnBookmarkScreen() {
openZimFileInReader()
Expand Down Expand Up @@ -247,6 +175,116 @@ class LibkiwixBookmarkTest : BaseActivityTest() {
}
}

@Test
fun testBookmarks() {
// Open a ZIM file and ensure the reader screen is initialized.
openZimFileInReader()
bookmarks {
// Ensure a clean starting state by removing any previously saved bookmarks
deletePreviouslySavedBookmarks()

// Save current page as a bookmark
clickOnSaveBookmarkImage(composeTestRule)
waitComposeToSettleViews()

// Verify bookmark appears in the bookmark list
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
assertBookmarkSaved(composeTestRule)
waitComposeToSettleViews()

// Open the saved bookmark from the list and verify it loads correctly in the reader
openBookmarkInReader(composeTestRule)
waitComposeToSettleViews()
assertZimFileLoadedIntoTheReader(composeTestRule)

// Ensure the bookmark toggle reflects the saved state.
assertEquals(true, isBookmarked())
waitComposeToSettleViews()

// Remove the bookmark and verify it is removed from the reader and the bookmark list.
clickOnSaveBookmarkImage(composeTestRule)
composeTestRule.waitUntilTimeout(TEST_PAUSE_MS_FOR_SNACKBAR)
assertEquals(false, isBookmarked())
longClickOnSaveBookmarkImage(composeTestRule, TEST_PAUSE_MS_FOR_DOWNLOAD_TEST.toLong())
assertNoBookMarkTextDisplayed(composeTestRule)
pressBack()
waitComposeToSettleViews()
clickOnSaveBookmarkImage(composeTestRule)
// Verify going to other pages does not affect the saved
// bookmark(Test scenario of custom apps where ZIM file is already opened
// in reader and user navigate back to reader).
topLevel {
// open settings screen
clickSettingsOnSideNav(kiwixMainActivity as CoreMainActivity, composeTestRule, true) {
composeTestRule.onNodeWithTag(NAVIGATION_ICON_TESTING_TAG).performClick()
waitComposeToSettleViews()
assertZimFileLoadedIntoTheReader(composeTestRule)
assertEquals(true, isBookmarked())
}
}

// Verify saved bookmark properly opened in reader(If some other article is opened in reader).
clickOnSaveBookmarkImage(composeTestRule)
composeTestRule.waitUntilTimeout(TEST_PAUSE_MS_FOR_SNACKBAR)
assertZimFileLoadedIntoTheReader(composeTestRule)
clickOnAndroidArticle(composeTestRule)
waitComposeToSettleViews()
assertAndroidArticleLoadedInReader(composeTestRule)
waitComposeToSettleViews()
clickOnSaveBookmarkImage(composeTestRule)
clickOnBackwardButton(composeTestRule)
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
openBookmarkInReader(composeTestRule)
waitComposeToSettleViews()
assertAndroidArticleLoadedInReader(composeTestRule)
assertEquals(true, isBookmarked())
clickOnSaveBookmarkImage(composeTestRule)
composeTestRule.waitUntilTimeout(TEST_PAUSE_MS_FOR_SNACKBAR)

// Save again and verify bookmark persistence after app restart.
clickOnHomeButton(composeTestRule)
waitComposeToSettleViews()
clickOnSaveBookmarkImage(composeTestRule)
waitComposeToSettleViews()
InstrumentationRegistry.getInstrumentation().uiAutomation.performGlobalAction(
AccessibilityService.GLOBAL_ACTION_HOME
)
waitComposeToSettleViews()

val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
context.startActivity(intent)
InstrumentationRegistry.getInstrumentation().waitForIdleSync()
waitComposeToSettleViews()
assertZimFileLoadedIntoTheReader(composeTestRule)
assertEquals(true, isBookmarked())
topLevel {
// Verify the bookmark appears in the bookmark screen.
clickBookmarksOnNavDrawer(kiwixMainActivity as CoreMainActivity, composeTestRule) {
assertBookmarkSaved(composeTestRule)
}
}
}
}

private fun isBookmarked() = kiwixMainActivity.supportFragmentManager.fragments
.filterIsInstance<CoreReaderFragment>()
.firstOrNull()
?.getIsBookmarked() ?: false

private fun deletePreviouslySavedBookmarks() {
bookmarks {
openBookmarkScreen(kiwixMainActivity as CoreMainActivity, composeTestRule)
clickOnTrashIcon(composeTestRule)
assertDeleteBookmarksDialogDisplayed(composeTestRule)
clickOnDeleteButton(composeTestRule)
assertNoBookMarkTextDisplayed(composeTestRule)
pressBack()
waitComposeToSettleViews()
}
}

private fun openZimFileInReader() {
val zimFile = getZimFile()
composeTestRule.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ object TestUtils {
const val TEST_PAUSE_MS = 3000
const val TEST_PAUSE_MS_FOR_SEARCH_TEST = 1000
const val TEST_PAUSE_MS_FOR_DOWNLOAD_TEST = 10000
const val TEST_PAUSE_MS_FOR_SNACKBAR = 6000L
const val RETRY_COUNT_FOR_FLAKY_TEST = 3

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import android.widget.FrameLayout
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
Expand Down Expand Up @@ -329,6 +330,9 @@ abstract class CoreReaderFragment :
return readerLifeCycleScope
}

@VisibleForTesting
fun getIsBookmarked() = isBookmarked

/**
* Handles actions that require the ZIM file to be fully loaded in the reader
* before opening the search screen. The search screen depends on the ZIM file,
Expand Down Expand Up @@ -1640,23 +1644,35 @@ abstract class CoreReaderFragment :

protected fun setUpBookmarks(zimFileReader: ZimFileReader) {
if (!isAdded) return
safelyCancelBookmarkJob()
val zimFileReaderId = zimFileReader.id
bookmarkingJob = viewLifecycleOwner.lifecycleScope.launch {
combine(
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReaderId) ?: emptyFlow(),
webUrlsFlow,
List<String?>::contains
).collect { isBookmarked ->
[email protected] = isBookmarked
readerScreenState.update {
copy(
bookmarkButtonItem = bookmarkButtonItem.copy(third = getBookMarkButtonIcon(isBookmarked))
)
runCatching {
safelyCancelBookmarkJob()
val zimFileReaderId = zimFileReader.id
bookmarkingJob = viewLifecycleOwner.lifecycleScope.launch {
combine(
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReaderId) ?: emptyFlow(),
webUrlsFlow,
List<String?>::contains
).collect { isBookmarked ->
[email protected] = isBookmarked
readerScreenState.update {
copy(
bookmarkButtonItem = bookmarkButtonItem.copy(
third = getBookMarkButtonIcon(isBookmarked)
)
)
}
}
}
updateUrlFlow()
}.onFailure {
// Since everything runs on the IO thread, cancelling the ongoing job when the fragment
// detaches from the window may take some time. To avoid crashes during this transition,
// we safely catch and log any exceptions that occur.
Log.e(
TAG_KIWIX,
"Could not set up the bookmark flow. Original exception $it"
)
}
updateUrlFlow()
}

private fun getBookMarkButtonIcon(isBookmarked: Boolean) =
Expand Down Expand Up @@ -2454,6 +2470,11 @@ abstract class CoreReaderFragment :
currentTab,
restoreOrigin
) {
// Set up the bookmark for the currently opened book after all pages are restored.
// This is especially important for custom apps, where the ZIM file is now loaded
// only if it's not already open in the reader. So when the user navigates to another
// screen and returns, we ensure the bookmark is restored correctly.
zimReaderContainer?.zimFileReader?.let(::setUpBookmarks)
// This lambda is executed after the tabs have been restored. It checks if there is a
// search item to open. If `searchItemToOpen` is not null, it calls `openSearchItem`
// to open the specified item, then sets `searchItemToOpen` to null to prevent
Expand Down