diff --git a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt index ad6c28f597ed..0805ef65e365 100644 --- a/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt +++ b/app/src/androidTest/java/com/nextcloud/client/FileDisplayActivityIT.kt @@ -1,6 +1,7 @@ /* * Nextcloud - Android Client * + * SPDX-FileCopyrightText: 2025 Philipp Hasper * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only @@ -14,12 +15,14 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.DrawerActions import androidx.test.espresso.contrib.NavigationViewActions import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.hasDescendant +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText @@ -36,6 +39,7 @@ import com.owncloud.android.operations.CreateFolderOperation import com.owncloud.android.ui.activity.FileDisplayActivity import com.owncloud.android.ui.adapter.OCFileListItemViewHolder import com.owncloud.android.utils.EspressoIdlingResource +import org.hamcrest.Matchers.allOf import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -44,6 +48,7 @@ import org.junit.Rule import org.junit.Test class FileDisplayActivityIT : AbstractOnServerIT() { + @Before fun registerIdlingResource() { IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) @@ -236,4 +241,53 @@ class FileDisplayActivityIT : AbstractOnServerIT() { } } } + + @Test + fun testShowAndDismissLoadingDialog() { + launchActivity().use { scenario -> + val loadingText = "Some text displayed while loading" + + // Test that display works + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + } + onView(withText(loadingText)) + .check(matches(isDisplayed())) + + // Test that hiding works + scenario.onActivity { sut -> + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + + // Test that there is no timing issue when hiding the dialog directly after. + // This timing issue was reproducible when testing RemoveFilesDialogFragment#removeFiles + // as well as sporadically "in the wild". + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + // Wait for a potential timing issue - dialog appearing belatedly + Thread.sleep(1000) + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + + // Test that multiple display calls after another don't cause a timing issue + scenario.onActivity { sut -> + sut.showLoadingDialog(loadingText) + sut.showLoadingDialog(loadingText) + sut.showLoadingDialog(loadingText) + sut.dismissLoadingDialog() + } + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + // Wait for a potential timing issue - dialog appearing belatedly + Thread.sleep(1000) + onView(allOf(withText(loadingText), isDisplayed())) + .check(doesNotExist()) + } + } } diff --git a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt index b310a897fa42..3bade62247c4 100644 --- a/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt +++ b/app/src/androidTest/java/com/nextcloud/test/GrantStoragePermissionRule.kt @@ -15,6 +15,10 @@ import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +/** + * Rule to automatically enable the test to write to the external storage. + * Depending on the SDK version, different approaches might be necessary to achieve the full access. + */ class GrantStoragePermissionRule private constructor() { companion object { @@ -30,6 +34,7 @@ class GrantStoragePermissionRule private constructor() { private class GrantManageExternalStoragePermissionRule : TestRule { override fun apply(base: Statement, description: Description): Statement = object : Statement() { override fun evaluate() { + // Refer to https://developer.android.com/training/data-storage/manage-all-files#enable-manage-external-storage-for-testing InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand( "appops set --uid ${InstrumentationRegistry.getInstrumentation().targetContext.packageName} " + "MANAGE_EXTERNAL_STORAGE allow" diff --git a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java index f7ab18fe76c1..58d6abef071c 100644 --- a/app/src/androidTest/java/com/owncloud/android/AbstractIT.java +++ b/app/src/androidTest/java/com/owncloud/android/AbstractIT.java @@ -6,6 +6,7 @@ */ package com.owncloud.android; +import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; @@ -78,6 +79,7 @@ import androidx.test.espresso.contrib.DrawerActions; import androidx.test.espresso.intent.rule.IntentsTestRule; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.rule.GrantPermissionRule; import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; import androidx.test.runner.lifecycle.Stage; @@ -93,7 +95,10 @@ */ public abstract class AbstractIT { @Rule - public final TestRule permissionRule = GrantStoragePermissionRule.grant(); + public final TestRule storagePermissionRule = GrantStoragePermissionRule.grant(); + + @Rule + public GrantPermissionRule notificationsPermissionRule = GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS); protected static OwnCloudClient client; protected static NextcloudClient nextcloudClient; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 1e1727283d18..2e87286462a2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -563,6 +563,7 @@ protected void updateFileFromDB(){ public void showLoadingDialog(String message) { runOnUiThread(() -> { FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.executePendingTransactions(); Fragment existingDialog = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); if (existingDialog instanceof LoadingDialog loadingDialog) { @@ -585,6 +586,7 @@ public void showLoadingDialog(String message) { public void dismissLoadingDialog() { runOnUiThread(() -> { FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.executePendingTransactions(); Fragment fragment = fragmentManager.findFragmentByTag(DIALOG_WAIT_TAG); if (fragment instanceof LoadingDialog loadingDialogFragment) {