diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/utils/RecyclerViewSelectedCheckBoxCountAssertion.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/utils/RecyclerViewSelectedCheckBoxCountAssertion.kt new file mode 100644 index 000000000..631c04fdd --- /dev/null +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/utils/RecyclerViewSelectedCheckBoxCountAssertion.kt @@ -0,0 +1,75 @@ +/* + * Kiwix Android + * Copyright (c) 2023 Kiwix + * 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 . + * + */ + +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 = allOf( + isAssignableFrom(RecyclerView::class.java), + isDisplayed(), + withId(recyclerViewId) + ) + + // Use a custom ViewMatcher to find checkboxes that are checked + val checkBoxMatcher: Matcher = object : TypeSafeMatcher() { + 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(checkBoxId) + } + .filter { it != null && checkBoxMatcher.matches(it) } + .forEach { _ -> checkedCount++ } + } + + return checkedCount + } +} diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt index b9f2a995d..251be3edc 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt @@ -18,19 +18,19 @@ 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.preference.PreferenceManager import androidx.test.core.app.ActivityScenario -import androidx.test.internal.runner.junit4.statement.UiThreadStatement import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.rule.GrantPermissionRule import androidx.test.uiautomator.UiDevice import leakcanary.LeakAssertions import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test -import org.kiwix.kiwixmobile.BaseActivityTest import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.main.KiwixMainActivity @@ -40,28 +40,52 @@ import java.io.File import java.io.FileOutputStream import java.io.OutputStream -class ZimHostFragmentTest : BaseActivityTest() { +class ZimHostFragmentTest { @Rule @JvmField var retryRule = RetryRule() - private lateinit var kiwixMainActivity: KiwixMainActivity private lateinit var sharedPreferenceUtil: SharedPreferenceUtil + private lateinit var activityScenario: ActivityScenario + + 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 - override fun waitForIdle() { + fun waitForIdle() { + context = InstrumentationRegistry.getInstrumentation().targetContext UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply { if (TestUtils.isSystemUINotRespondingDialogVisible(this)) { TestUtils.closeSystemDialogs(context) } waitForIdle() } - sharedPreferenceUtil = SharedPreferenceUtil(context) - PreferenceManager.getDefaultSharedPreferences(context).edit { - putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false) - putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) - putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, true) - putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) + context?.let { + sharedPreferenceUtil = SharedPreferenceUtil(it).apply { + setIntroShown() + putPrefWifiOnly(false) + setIsPlayStoreBuildType(true) + prefIsTest = true + } } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) @@ -70,20 +94,50 @@ class ZimHostFragmentTest : BaseActivityTest() { @Test fun testZimHostFragment() { - activityScenario.onActivity { - kiwixMainActivity = it - kiwixMainActivity.navigate(R.id.libraryFragment) + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { + activityScenario.onActivity { + 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) { @@ -106,9 +160,9 @@ class ZimHostFragmentTest : BaseActivityTest() { @After fun setIsTestPreference() { - PreferenceManager.getDefaultSharedPreferences(context).edit { - putBoolean(SharedPreferenceUtil.PREF_IS_TEST, false) - putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, false) + sharedPreferenceUtil.apply { + setIsPlayStoreBuildType(false) + prefIsTest = false } } } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostRobot.kt index f3d672f80..47d9ff2f5 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostRobot.kt @@ -18,12 +18,19 @@ package org.kiwix.kiwixmobile.webserver +import androidx.test.espresso.matcher.ViewMatchers.assertThat import applyWithViewHierarchyPrinting +import com.adevinta.android.barista.interaction.BaristaSleepInteractions import com.adevinta.android.barista.interaction.BaristaSwipeRefreshInteractions.refresh +import org.hamcrest.CoreMatchers import org.kiwix.kiwixmobile.BaseRobot -import org.kiwix.kiwixmobile.Findable.Text 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.testutils.TestUtils +import org.kiwix.kiwixmobile.utils.RecyclerViewSelectedCheckBoxCountAssertion +import org.kiwix.kiwixmobile.utils.StandardActions.openDrawer fun zimHost(func: ZimHostRobot.() -> Unit) = ZimHostRobot().applyWithViewHierarchyPrinting(func) @@ -34,10 +41,55 @@ class ZimHostRobot : BaseRobot() { } fun refreshLibraryList() { + pauseForBetterTestPerformance() refresh(R.id.zim_swiperefresh) } fun assertZimFilesLoaded() { + pauseForBetterTestPerformance() 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()) + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 06d239224..d2025385c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -60,8 +60,11 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { val prefIsFirstRun: Boolean get() = sharedPreferences.getBoolean(PREF_IS_FIRST_RUN, true) - val prefIsTest: Boolean + var prefIsTest: Boolean get() = sharedPreferences.getBoolean(PREF_IS_TEST, false) + set(prefIsTest) { + sharedPreferences.edit { putBoolean(PREF_IS_TEST, prefIsTest) } + } val prefShowShowCaseToUser: Boolean get() = sharedPreferences.getBoolean(PREF_SHOW_SHOWCASE, true)