Added test cases for custom apps to cover this scenario and prevent future errors.

* Refactored some deprecated methods used in test cases.
This commit is contained in:
MohitMaliFtechiz 2025-01-30 18:37:24 +05:30 committed by Kelson
parent 1ed02e5ab0
commit 5e05fe81d9
3 changed files with 118 additions and 10 deletions

View File

@ -90,8 +90,10 @@ class SearchFragmentTestForCustomApp {
private lateinit var downloadingZimFile: File private lateinit var downloadingZimFile: File
private lateinit var activityScenario: ActivityScenario<CustomMainActivity> private lateinit var activityScenario: ActivityScenario<CustomMainActivity>
private val rayCharlesZimFileUrl = private val scientificAllianceZIMUrl =
"https://download.kiwix.org/zim/zimit/scientific-alliance.obscurative.ru_ru_all_2024-06.zim" "https://download.kiwix.org/zim/zimit/scientific-alliance.obscurative.ru_ru_all_2024-06.zim"
private val rayCharlesZIMFileUrl =
"https://dev.kiwix.org/kiwix-android/test/wikipedia_en_ray_charles_maxi_2023-12.zim"
@Before @Before
fun waitForIdle() { fun waitForIdle() {
@ -258,6 +260,44 @@ class SearchFragmentTestForCustomApp {
} }
} }
@Test
fun testPreviouslyLoadedArticleLoadsAgainWhenSwitchingToAnotherScreen() {
activityScenario.onActivity {
customMainActivity = it
}
// test with a large ZIM file to properly test the scenario
downloadingZimFile = getDownloadingZimFileFromDataFolder()
getOkkHttpClientForTesting().newCall(downloadRequest(rayCharlesZIMFileUrl)).execute()
.use { response ->
if (response.isSuccessful) {
response.body?.let { responseBody ->
writeZimFileData(responseBody, downloadingZimFile)
}
} else {
throw RuntimeException(
"Download Failed. Error: ${response.message}\n" +
" Status Code: ${response.code}"
)
}
}
UiThreadStatement.runOnUiThread {
customMainActivity.navigate(customMainActivity.readerFragmentResId)
}
openZimFileInReader(zimFile = downloadingZimFile)
search {
// click on home button to load the main page of ZIM file.
clickOnHomeButton()
// click on an article to load the other page.
clickOnAFoolForYouArticle()
assertAFoolForYouArticleLoaded()
// open note screen.
openNoteFragment()
pressBack()
// after came back check the previously loaded article is still showing or not.
assertAFoolForYouArticleLoaded()
}
}
private fun openSearchWithQuery(query: String = "") { private fun openSearchWithQuery(query: String = "") {
UiThreadStatement.runOnUiThread { UiThreadStatement.runOnUiThread {
customMainActivity.openSearch(searchString = query) customMainActivity.openSearch(searchString = query)
@ -270,7 +310,10 @@ class SearchFragmentTestForCustomApp {
} }
} }
private fun openZimFileInReader(assetFileDescriptor: AssetFileDescriptor) { private fun openZimFileInReader(
assetFileDescriptor: AssetFileDescriptor? = null,
zimFile: File? = null
) {
UiThreadStatement.runOnUiThread { UiThreadStatement.runOnUiThread {
val navHostFragment: NavHostFragment = val navHostFragment: NavHostFragment =
customMainActivity.supportFragmentManager customMainActivity.supportFragmentManager
@ -280,10 +323,17 @@ class SearchFragmentTestForCustomApp {
val customReaderFragment = val customReaderFragment =
navHostFragment.childFragmentManager.fragments[0] as CustomReaderFragment navHostFragment.childFragmentManager.fragments[0] as CustomReaderFragment
runBlocking { runBlocking {
assetFileDescriptor?.let {
customReaderFragment.openZimFile( customReaderFragment.openZimFile(
ZimReaderSource(null, null, listOf(assetFileDescriptor)), ZimReaderSource(assetFileDescriptorList = listOf(assetFileDescriptor)),
true true
) )
} ?: run {
customReaderFragment.openZimFile(
ZimReaderSource(zimFile),
true
)
}
} }
} }
} }
@ -318,9 +368,9 @@ class SearchFragmentTestForCustomApp {
} }
} }
private fun downloadRequest() = private fun downloadRequest(zimUrl: String = scientificAllianceZIMUrl) =
Request.Builder() Request.Builder()
.url(URI.create(rayCharlesZimFileUrl).toURL()) .url(URI.create(zimUrl).toURL())
.build() .build()
private fun getDownloadingZimFile(): File { private fun getDownloadingZimFile(): File {
@ -330,6 +380,13 @@ class SearchFragmentTestForCustomApp {
return zimFile return zimFile
} }
private fun getDownloadingZimFileFromDataFolder(): File {
val zimFile = File(context.getExternalFilesDirs(null)[0], "ray_charles.zim")
if (zimFile.exists()) zimFile.delete()
zimFile.createNewFile()
return zimFile
}
@Singleton @Singleton
private fun getOkkHttpClientForTesting(): OkHttpClient = private fun getOkkHttpClientForTesting(): OkHttpClient =
OkHttpClient().newBuilder() OkHttpClient().newBuilder()

View File

@ -19,19 +19,31 @@
package org.kiwix.kiwixmobile.custom.search package org.kiwix.kiwixmobile.custom.search
import android.view.KeyEvent import android.view.KeyEvent
import androidx.core.view.GravityCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.web.assertion.WebViewAssertions.webMatches
import androidx.test.espresso.web.sugar.Web import androidx.test.espresso.web.sugar.Web
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.espresso.web.webdriver.DriverAtoms import androidx.test.espresso.web.webdriver.DriverAtoms
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
import androidx.test.espresso.web.webdriver.DriverAtoms.getText
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
import androidx.test.espresso.web.webdriver.Locator import androidx.test.espresso.web.webdriver.Locator
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import com.adevinta.android.barista.interaction.BaristaDrawerInteractions.openDrawerWithGravity
import com.adevinta.android.barista.interaction.BaristaSleepInteractions import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import com.adevinta.android.barista.internal.matcher.HelperMatchers import com.adevinta.android.barista.internal.matcher.HelperMatchers
import org.hamcrest.CoreMatchers.containsString
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.custom.R.id
import org.kiwix.kiwixmobile.custom.testutils.TestUtils import org.kiwix.kiwixmobile.custom.testutils.TestUtils
import org.kiwix.kiwixmobile.custom.testutils.TestUtils.testFlakyView import org.kiwix.kiwixmobile.custom.testutils.TestUtils.testFlakyView
@ -118,4 +130,43 @@ class SearchRobot {
) )
}) })
} }
fun clickOnHomeButton() {
testFlakyView({
Espresso.onView(ViewMatchers.withId(R.id.bottom_toolbar_home))
.perform(ViewActions.click())
})
}
fun clickOnAFoolForYouArticle() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS.toLong())
testFlakyView({
onWebView()
.withElement(
findElement(
Locator.XPATH,
"//*[contains(text(), 'A Fool for You')]"
)
).perform(webClick())
})
}
fun assertAFoolForYouArticleLoaded() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS.toLong())
testFlakyView({
onWebView()
.withElement(
findElement(
Locator.XPATH,
"//*[contains(text(), '\"A Fool for You\"')]"
)
).check(webMatches(getText(), containsString("\"A Fool for You\"")))
})
}
fun openNoteFragment() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS.toLong())
openDrawerWithGravity(id.custom_drawer_container, GravityCompat.START)
testFlakyView({ onView(withText(R.string.pref_notes)).perform(click()) })
}
} }

View File

@ -21,7 +21,6 @@ package org.kiwix.kiwixmobile.custom.testutils
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.Intent import android.content.Intent
import androidx.core.content.ContextCompat
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiObject import androidx.test.uiautomator.UiObject
@ -32,6 +31,7 @@ import java.io.File
object TestUtils { object TestUtils {
private const val TAG = "TESTUTILS" private const val TAG = "TESTUTILS"
var TEST_PAUSE_MS_FOR_SEARCH_TEST = 1000 var TEST_PAUSE_MS_FOR_SEARCH_TEST = 1000
var TEST_PAUSE_MS = 3000
@JvmStatic @JvmStatic
fun isSystemUINotRespondingDialogVisible(uiDevice: UiDevice) = fun isSystemUINotRespondingDialogVisible(uiDevice: UiDevice) =
@ -92,7 +92,7 @@ object TestUtils {
@JvmStatic @JvmStatic
fun deleteTemporaryFilesOfTestCases(context: Context) { fun deleteTemporaryFilesOfTestCases(context: Context) {
ContextCompat.getExternalFilesDirs(context, null).filterNotNull() context.getExternalFilesDirs(null).filterNotNull()
.map(::deleteAllFilesInDirectory) .map(::deleteAllFilesInDirectory)
ContextWrapper(context).externalMediaDirs.filterNotNull() ContextWrapper(context).externalMediaDirs.filterNotNull()
.map(::deleteAllFilesInDirectory) .map(::deleteAllFilesInDirectory)