diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationDialogTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationDialogTest.kt new file mode 100644 index 000000000..12f20ef83 --- /dev/null +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationDialogTest.kt @@ -0,0 +1,229 @@ +/* + * Kiwix Android + * Copyright (c) 2025 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.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) + } +} diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationRobot.kt new file mode 100644 index 000000000..c8c6eedff --- /dev/null +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/DonationRobot.kt @@ -0,0 +1,45 @@ +/* + * Kiwix Android + * Copyright (c) 2025 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.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() + } + } +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/DonationLayout.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/DonationLayout.kt index 564ceedb6..7c835c0f3 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/DonationLayout.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/DonationLayout.kt @@ -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 ) } } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt index 88556f6bc..735ffba25 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandlerTest.kt @@ -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()