Added DonationDialogTest UI test cases to thoroughly verify the functionality using Compose UI.

* Minor improvements to dark mode styling for the donation layout buttons.
This commit is contained in:
MohitMaliFtechiz 2025-06-28 20:11:13 +05:30
parent bea28a16a3
commit 1e9e634fca
4 changed files with 284 additions and 9 deletions

View File

@ -0,0 +1,229 @@
/*
* Kiwix Android
* Copyright (c) 2025 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.reader
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.core.content.edit
import androidx.lifecycle.Lifecycle
import androidx.preference.PreferenceManager
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.accessibility.AccessibilityChecks
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesCheck
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.anyOf
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.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.THREE_MONTHS_IN_MILLISECONDS
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.nav.destination.library.library
import org.kiwix.kiwixmobile.testutils.RetryRule
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.closeSystemDialogs
import org.kiwix.kiwixmobile.testutils.TestUtils.isSystemUINotRespondingDialogVisible
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
class DonationDialogTest : BaseActivityTest() {
@Rule(order = RETRY_RULE_ORDER)
@JvmField
val retryRule = RetryRule()
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
val composeTestRule = createComposeRule()
private lateinit var kiwixMainActivity: KiwixMainActivity
private lateinit var sharedPreferenceUtil: SharedPreferenceUtil
@Before
override fun waitForIdle() {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
if (isSystemUINotRespondingDialogVisible(this)) {
closeSystemDialogs(context, this)
}
waitForIdle()
}
PreferenceManager.getDefaultSharedPreferences(context).edit {
putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false)
putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false)
putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true)
putString(SharedPreferenceUtil.PREF_LANG, "en")
}
sharedPreferenceUtil = SharedPreferenceUtil(context)
activityScenario =
ActivityScenario.launch(KiwixMainActivity::class.java).apply {
moveToState(Lifecycle.State.RESUMED)
onActivity {
handleLocaleChange(
it,
"en",
sharedPreferenceUtil
)
}
}
}
init {
AccessibilityChecks.enable().apply {
setRunChecksFromRootView(true)
setSuppressingResultMatcher(
anyOf(
allOf(
matchesCheck(TouchTargetSizeCheck::class.java),
matchesViews(withContentDescription("More options"))
),
matchesCheck(SpeakableTextPresentCheck::class.java)
)
)
}
}
@Test
fun showDonationPopupWhenApplicationIsThreeMonthOldAndHaveAtleastOneZIMFile() {
loadZIMFileInApplication()
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds = 0L
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun shouldNotShowDonationPopupWhenApplicationIsThreeMonthOldAndDoNotHaveAnyZIMFile() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
activityScenario.onActivity {
kiwixMainActivity = it
kiwixMainActivity.navigate(R.id.libraryFragment)
}
deleteAllZIMFilesFromApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
@Test
fun shouldNotShowPopupIfTimeSinceLastPopupIsLessThanThreeMonth() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS / 2)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
@Test
fun shouldShowDonationPopupIfTimeSinceLastPopupExceedsThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS + 1000)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun testShouldShowDonationPopupWhenLaterClickedTimeExceedsThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds =
System.currentTimeMillis() - (THREE_MONTHS_IN_MILLISECONDS + 1000)
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogDisplayed(composeTestRule) }
}
@Test
fun testShouldNotShowPopupIfLaterClickedTimeIsLessThanThreeMonths() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = 0L
sharedPreferenceUtil.laterClickedMilliSeconds =
System.currentTimeMillis() - 10000L
loadZIMFileInApplication()
openReaderFragment()
donation { assertDonationDialogIsNotDisplayed(composeTestRule) }
}
private fun openReaderFragment() {
UiThreadStatement.runOnUiThread {
kiwixMainActivity.navigate(kiwixMainActivity.readerFragmentResId)
}
}
private fun loadZIMFileInApplication() {
openLocalLibraryScreen()
deleteAllZIMFilesFromApplication()
val loadFileStream =
DonationDialogTest::class.java.classLoader.getResourceAsStream("testzim.zim")
val zimFile =
File(
context.getExternalFilesDirs(null)[0],
"testzim.zim"
)
if (zimFile.exists()) zimFile.delete()
zimFile.createNewFile()
loadFileStream.use { inputStream ->
val outputStream: OutputStream = FileOutputStream(zimFile)
outputStream.use { it ->
val buffer = ByteArray(inputStream.available())
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
it.write(buffer, 0, length)
}
}
}
refreshZIMFilesList()
}
private fun openLocalLibraryScreen() {
activityScenario.onActivity {
kiwixMainActivity = it
kiwixMainActivity.navigate(R.id.libraryFragment)
}
}
private fun refreshZIMFilesList() {
library {
refreshList(composeTestRule)
waitUntilZimFilesRefreshing(composeTestRule)
}
}
private fun deleteAllZIMFilesFromApplication() {
refreshZIMFilesList()
library {
// delete all the ZIM files showing in the LocalLibrary
// screen to properly test the scenario.
deleteZimIfExists(composeTestRule)
}
}
@After
fun finish() {
TestUtils.deleteTemporaryFilesOfTestCases(context)
}
}

View File

@ -0,0 +1,45 @@
/*
* Kiwix Android
* Copyright (c) 2025 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.reader
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.onNodeWithTag
import applyWithViewHierarchyPrinting
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.core.main.reader.DONATION_LAYOUT_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
fun donation(func: DonationRobot.() -> Unit) = DonationRobot().applyWithViewHierarchyPrinting(func)
class DonationRobot : BaseRobot() {
fun assertDonationDialogDisplayed(composeTestRule: ComposeContentTestRule) {
composeTestRule.apply {
waitUntilTimeout()
onNodeWithTag(DONATION_LAYOUT_TESTING_TAG).isDisplayed()
}
}
fun assertDonationDialogIsNotDisplayed(composeTestRule: ComposeContentTestRule) {
composeTestRule.apply {
waitUntilTimeout()
onNodeWithTag(DONATION_LAYOUT_TESTING_TAG).isNotDisplayed()
}
}
}

View File

@ -40,16 +40,18 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.ui.theme.DenimBlue800
import org.kiwix.kiwixmobile.core.utils.ComposeDimens
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.DONATION_LAYOUT_MAXIMUM_WIDTH
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
const val DONATION_LAYOUT_TESTING_TAG = "donationLayoutTestingTag"
@Composable
fun DonationLayout(
appName: String,
@ -70,7 +72,7 @@ fun DonationLayout(
)
.padding(horizontal = SIXTEEN_DP),
) {
DonationDialogCard(
DonationLayoutCard(
appName,
onDonateButtonClick,
onLaterButtonClick
@ -79,7 +81,7 @@ fun DonationLayout(
}
@Composable
fun DonationDialogCard(
fun DonationLayoutCard(
appName: String,
onDonateButtonClick: () -> Unit,
onLaterButtonClick: () -> Unit
@ -88,7 +90,8 @@ fun DonationDialogCard(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(ComposeDimens.SIXTEEN_DP),
.padding(ComposeDimens.SIXTEEN_DP)
.testTag(DONATION_LAYOUT_TESTING_TAG),
shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(defaultElevation = ComposeDimens.SIX_DP),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainer)
@ -152,12 +155,10 @@ fun DonationDialogButton(
onButtonClick: () -> Unit,
@StringRes buttonText: Int
) {
TextButton(
onClick = onButtonClick
) {
TextButton(onClick = onButtonClick) {
Text(
text = stringResource(buttonText),
color = DenimBlue800
color = MaterialTheme.colorScheme.primary
)
}
}

View File

@ -154,7 +154,7 @@ class DonationDialogHandlerTest {
}
@Test
fun `test should show popup if later clicked time is less than three months`() =
fun `test should not show popup if later clicked time is less than three months`() =
runTest {
donationDialogHandler = spyk(donationDialogHandler)
val currentMilliSeconds = System.currentTimeMillis()