Improved ZimHostFragmentTest to properly test the server functionality.

* Created a new class to match the how many checkbox is checked in the recyclerview, it will also help to test this type of functionality in future.
This commit is contained in:
MohitMali 2023-10-16 12:39:50 +05:30 committed by Kelson
parent d660d806a2
commit 3432b32431
4 changed files with 215 additions and 31 deletions

View File

@ -0,0 +1,75 @@
/*
* Kiwix Android
* Copyright (c) 2023 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.utils
import android.view.View
import android.widget.CheckBox
import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
import org.hamcrest.core.AllOf.allOf
class RecyclerViewSelectedCheckBoxCountAssertion(
private val recyclerViewId: Int,
private val checkBoxId: Int
) {
fun countCheckedCheckboxes(): Int {
var checkedCount = 0
// Find the RecyclerView
val recyclerViewMatcher: Matcher<View> = allOf(
isAssignableFrom(RecyclerView::class.java),
isDisplayed(),
withId(recyclerViewId)
)
// Use a custom ViewMatcher to find checkboxes that are checked
val checkBoxMatcher: Matcher<View> = object : TypeSafeMatcher<View>() {
override fun matchesSafely(view: View): Boolean =
view is CheckBox && view.isChecked
override fun describeTo(description: Description) {
description.appendText("is checked")
}
}
// Count the checked checkboxes
onView(recyclerViewMatcher).check { view, noViewFoundException ->
if (noViewFoundException != null) {
throw noViewFoundException
}
val recyclerView = view as RecyclerView
(0 until recyclerView.childCount)
.asSequence()
.map {
// Check the checkbox directly without using inRoot
recyclerView.getChildAt(it).findViewById<CheckBox>(checkBoxId)
}
.filter { it != null && checkBoxMatcher.matches(it) }
.forEach { _ -> checkedCount++ }
}
return checkedCount
}
}

View File

@ -18,19 +18,19 @@
package org.kiwix.kiwixmobile.webserver package org.kiwix.kiwixmobile.webserver
import androidx.core.content.edit import android.Manifest
import android.content.Context
import android.os.Build
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import leakcanary.LeakAssertions import leakcanary.LeakAssertions
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.main.KiwixMainActivity import org.kiwix.kiwixmobile.main.KiwixMainActivity
@ -40,28 +40,52 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.OutputStream import java.io.OutputStream
class ZimHostFragmentTest : BaseActivityTest() { class ZimHostFragmentTest {
@Rule @Rule
@JvmField @JvmField
var retryRule = RetryRule() var retryRule = RetryRule()
private lateinit var kiwixMainActivity: KiwixMainActivity
private lateinit var sharedPreferenceUtil: SharedPreferenceUtil private lateinit var sharedPreferenceUtil: SharedPreferenceUtil
private lateinit var activityScenario: ActivityScenario<KiwixMainActivity>
private val permissions = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.SYSTEM_ALERT_WINDOW,
Manifest.permission.NEARBY_WIFI_DEVICES
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_COARSE_LOCATION
)
}
@Rule
@JvmField
var permissionRules: GrantPermissionRule =
GrantPermissionRule.grant(*permissions)
private var context: Context? = null
@Before @Before
override fun waitForIdle() { fun waitForIdle() {
context = InstrumentationRegistry.getInstrumentation().targetContext
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply { UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
if (TestUtils.isSystemUINotRespondingDialogVisible(this)) { if (TestUtils.isSystemUINotRespondingDialogVisible(this)) {
TestUtils.closeSystemDialogs(context) TestUtils.closeSystemDialogs(context)
} }
waitForIdle() waitForIdle()
} }
sharedPreferenceUtil = SharedPreferenceUtil(context) context?.let {
PreferenceManager.getDefaultSharedPreferences(context).edit { sharedPreferenceUtil = SharedPreferenceUtil(it).apply {
putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false) setIntroShown()
putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) putPrefWifiOnly(false)
putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, true) setIsPlayStoreBuildType(true)
putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) prefIsTest = true
}
} }
activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply {
moveToState(Lifecycle.State.RESUMED) moveToState(Lifecycle.State.RESUMED)
@ -70,20 +94,50 @@ class ZimHostFragmentTest : BaseActivityTest() {
@Test @Test
fun testZimHostFragment() { fun testZimHostFragment() {
activityScenario.onActivity { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {
kiwixMainActivity = it activityScenario.onActivity {
kiwixMainActivity.navigate(R.id.libraryFragment) it.navigate(R.id.libraryFragment)
}
loadZimFileInApplication("testzim.zim")
loadZimFileInApplication("small.zim")
zimHost {
refreshLibraryList()
assertZimFilesLoaded()
openZimHostFragment()
clickOnTestZim()
// Start the server with one ZIM file
startServer()
assertServerStarted()
// Check that only one ZIM file is hosted on the server
assertItemHostedOnServer(1)
// Stop the server
stopServer()
assertServerStopped()
// Select the test ZIM file to host on the server
clickOnTestZim()
// Start the server with two ZIM files
startServer()
assertServerStarted()
// Check that both ZIM files are hosted on the server
assertItemHostedOnServer(2)
// Unselect the test ZIM to test restarting server functionality
clickOnTestZim()
// Check if the server is running
assertServerStarted()
// Check that only one ZIM file is hosted on the server after unselecting
assertItemHostedOnServer(1)
}
LeakAssertions.assertNoLeaks()
} }
loadZimFileInApplication("testzim.zim")
loadZimFileInApplication("small.zim")
zimHost {
refreshLibraryList()
assertZimFilesLoaded()
}
UiThreadStatement.runOnUiThread {
kiwixMainActivity.navigate(R.id.zimHostFragment)
}
LeakAssertions.assertNoLeaks()
} }
private fun loadZimFileInApplication(zimFileName: String) { private fun loadZimFileInApplication(zimFileName: String) {
@ -106,9 +160,9 @@ class ZimHostFragmentTest : BaseActivityTest() {
@After @After
fun setIsTestPreference() { fun setIsTestPreference() {
PreferenceManager.getDefaultSharedPreferences(context).edit { sharedPreferenceUtil.apply {
putBoolean(SharedPreferenceUtil.PREF_IS_TEST, false) setIsPlayStoreBuildType(false)
putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, false) prefIsTest = false
} }
} }
} }

View File

@ -18,12 +18,19 @@
package org.kiwix.kiwixmobile.webserver package org.kiwix.kiwixmobile.webserver
import androidx.test.espresso.matcher.ViewMatchers.assertThat
import applyWithViewHierarchyPrinting import applyWithViewHierarchyPrinting
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import com.adevinta.android.barista.interaction.BaristaSwipeRefreshInteractions.refresh import com.adevinta.android.barista.interaction.BaristaSwipeRefreshInteractions.refresh
import org.hamcrest.CoreMatchers
import org.kiwix.kiwixmobile.BaseRobot import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.Findable.Text
import org.kiwix.kiwixmobile.Findable.StringId.TextId import org.kiwix.kiwixmobile.Findable.StringId.TextId
import org.kiwix.kiwixmobile.Findable.Text
import org.kiwix.kiwixmobile.Findable.ViewId
import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.utils.RecyclerViewSelectedCheckBoxCountAssertion
import org.kiwix.kiwixmobile.utils.StandardActions.openDrawer
fun zimHost(func: ZimHostRobot.() -> Unit) = ZimHostRobot().applyWithViewHierarchyPrinting(func) fun zimHost(func: ZimHostRobot.() -> Unit) = ZimHostRobot().applyWithViewHierarchyPrinting(func)
@ -34,10 +41,55 @@ class ZimHostRobot : BaseRobot() {
} }
fun refreshLibraryList() { fun refreshLibraryList() {
pauseForBetterTestPerformance()
refresh(R.id.zim_swiperefresh) refresh(R.id.zim_swiperefresh)
} }
fun assertZimFilesLoaded() { fun assertZimFilesLoaded() {
pauseForBetterTestPerformance()
isVisible(Text("Test_Zim")) isVisible(Text("Test_Zim"))
} }
fun openZimHostFragment() {
openDrawer()
clickOn(TextId(R.string.menu_wifi_hotspot))
}
fun clickOnTestZim() {
clickOn(Text("Test_Zim"))
}
fun startServer() {
clickOn(ViewId(R.id.startServerButton))
pauseForBetterTestPerformance()
isVisible(TextId(R.string.wifi_dialog_title))
clickOn(TextId(R.string.hotspot_dialog_neutral_button))
}
fun assertServerStarted() {
pauseForBetterTestPerformance()
isVisible(Text("STOP SERVER"))
}
fun assertItemHostedOnServer(itemCount: Int) {
val checkedCheckboxCount =
RecyclerViewSelectedCheckBoxCountAssertion(
R.id.recyclerViewZimHost,
R.id.itemBookCheckbox
).countCheckedCheckboxes()
assertThat(checkedCheckboxCount, CoreMatchers.`is`(itemCount))
}
fun stopServer() {
clickOn(ViewId(R.id.startServerButton))
}
fun assertServerStopped() {
pauseForBetterTestPerformance()
isVisible(Text("START SERVER"))
}
private fun pauseForBetterTestPerformance() {
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS_FOR_SEARCH_TEST.toLong())
}
} }

View File

@ -60,8 +60,11 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
val prefIsFirstRun: Boolean val prefIsFirstRun: Boolean
get() = sharedPreferences.getBoolean(PREF_IS_FIRST_RUN, true) get() = sharedPreferences.getBoolean(PREF_IS_FIRST_RUN, true)
val prefIsTest: Boolean var prefIsTest: Boolean
get() = sharedPreferences.getBoolean(PREF_IS_TEST, false) get() = sharedPreferences.getBoolean(PREF_IS_TEST, false)
set(prefIsTest) {
sharedPreferences.edit { putBoolean(PREF_IS_TEST, prefIsTest) }
}
val prefShowShowCaseToUser: Boolean val prefShowShowCaseToUser: Boolean
get() = sharedPreferences.getBoolean(PREF_SHOW_SHOWCASE, true) get() = sharedPreferences.getBoolean(PREF_SHOW_SHOWCASE, true)