Merge pull request #13192 from nextcloud/fix-ss-tests

Fix ss tests
This commit is contained in:
Alper Öztürk 2024-08-08 13:01:00 +02:00 committed by GitHub
commit ffbff3f2f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 323 additions and 168 deletions

View file

@ -391,6 +391,7 @@ dependencies {
androidTestImplementation "androidx.test.espresso:espresso-web:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-accessibility:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
androidTestImplementation "androidx.test.espresso:espresso-idling-resource:$espressoVersion"
// Mocking support
androidTestImplementation 'com.github.tmurakami:dexopener:2.0.5' // required to allow mocking on API 27 and older

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View file

@ -18,14 +18,13 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.core.app.ActivityScenario;
public class AuthenticatorActivityIT extends AbstractIT {
private final String testClassName = "com.nextcloud.client.AuthenticatorActivityIT";
private static final String URL = "cloud.nextcloud.com";
@Rule public IntentsTestRule<AuthenticatorActivity> activityRule = new IntentsTestRule<>(AuthenticatorActivity.class,
true,
false);
@Rule
public final TestRule permissionRule = GrantStoragePermissionRule.grant();
@ -33,9 +32,13 @@ public class AuthenticatorActivityIT extends AbstractIT {
@Test
@ScreenshotTest
public void login() {
AuthenticatorActivity sut = activityRule.launchActivity(null);
try (ActivityScenario<AuthenticatorActivity> scenario = ActivityScenario.launch(AuthenticatorActivity.class)) {
scenario.onActivity(sut -> onIdleSync(() -> {
((TextView) sut.findViewById(R.id.host_url_input)).setText(URL);
sut.runOnUiThread(() -> sut.getAccountSetupBinding().hostUrlInput.clearFocus());
screenshot(sut);
String screenShotName = createName(testClassName + "_" + "login", "");
screenshotViaName(sut, screenShotName);
}));
}
}
}

View file

@ -16,6 +16,7 @@ import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.rule.GrantPermissionRule
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.activity.FileDisplayActivity
import com.owncloud.android.utils.ScreenshotTest
import org.junit.Assert
@ -31,13 +32,18 @@ class FileDisplayActivityScreenshotIT : AbstractIT() {
)
@get:Rule
val permissionRule = GrantPermissionRule.grant(
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
companion object {
private const val TAG = "FileDisplayActivityScreenshotIT"
}
@Test
@ScreenshotTest
fun open() {
try {
val sut = activityRule.launchActivity(null)
shortSleep()
@ -50,11 +56,15 @@ class FileDisplayActivityScreenshotIT : AbstractIT() {
shortSleep()
waitForIdleSync()
screenshot(sut)
} catch (e: SecurityException) {
Log_OC.e(TAG, "Error caught at open $e")
}
}
@Test
@ScreenshotTest
fun showMediaThenAllFiles() {
try {
val fileDisplayActivity = activityRule.launchActivity(null)
val sut = fileDisplayActivity.listOfFilesFragment
Assert.assertNotNull(sut)
@ -84,11 +94,15 @@ class FileDisplayActivityScreenshotIT : AbstractIT() {
sut.isLoading = false
shortSleep()
screenshot(fileDisplayActivity)
} catch (e: SecurityException) {
Log_OC.e(TAG, "Error caught at open $e")
}
}
@Test
@ScreenshotTest
fun drawer() {
try {
val sut = activityRule.launchActivity(null)
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open())
@ -103,5 +117,8 @@ class FileDisplayActivityScreenshotIT : AbstractIT() {
shortSleep()
waitForIdleSync()
screenshot(sut)
} catch (e: SecurityException) {
Log_OC.e(TAG, "Error caught at open $e")
}
}
}

View file

@ -301,6 +301,10 @@ public abstract class AbstractIT {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
protected void onIdleSync(Runnable recipient) {
InstrumentationRegistry.getInstrumentation().waitForIdle(recipient);
}
protected void openDrawer(IntentsTestRule activityRule) {
Activity sut = activityRule.launchActivity(null);
@ -453,6 +457,12 @@ public abstract class AbstractIT {
screenshot(view, "");
}
protected void screenshotViaName(Activity activity, String name) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Screenshot.snapActivity(activity).setName(name).record();
}
}
protected void screenshot(View view, String prefix) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Screenshot.snap(view).setName(createName(prefix)).record();
@ -473,9 +483,7 @@ public abstract class AbstractIT {
return createName("");
}
private String createName(String prefix) {
String name = TestNameDetector.getTestClass() + "_" + TestNameDetector.getTestName();
public String createName(String name, String prefix) {
if (!TextUtils.isEmpty(prefix)) {
name = name + "_" + prefix;
}
@ -491,6 +499,11 @@ public abstract class AbstractIT {
return name;
}
private String createName(String prefix) {
String name = TestNameDetector.getTestClass() + "_" + TestNameDetector.getTestName();
return createName(name, prefix);
}
public static String getUserId(User user) {
return AccountManager.get(targetContext).getUserData(user.toPlatformAccount(), KEY_USER_ID);
}

View file

@ -7,35 +7,57 @@
*/
package com.owncloud.android.ui.activity
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.annotation.UiThread
import androidx.test.core.app.launchActivity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import com.owncloud.android.utils.EspressoIdlingResource
import com.owncloud.android.AbstractIT
import com.owncloud.android.lib.resources.notifications.models.Action
import com.owncloud.android.lib.resources.notifications.models.Notification
import com.owncloud.android.lib.resources.notifications.models.RichObject
import com.owncloud.android.utils.ScreenshotTest
import org.junit.Rule
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.util.GregorianCalendar
class NotificationsActivityIT : AbstractIT() {
@get:Rule
var activityRule = IntentsTestRule(NotificationsActivity::class.java, true, false)
private val testClassName = "com.owncloud.android.ui.activity.NotificationsActivityIT"
@Test
@ScreenshotTest
fun empty() {
val sut: NotificationsActivity = activityRule.launchActivity(null)
@Before
fun registerIdlingResource() {
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
}
waitForIdleSync()
sut.runOnUiThread { sut.populateList(ArrayList<Notification>()) }
shortSleep()
screenshot(sut)
@After
fun unregisterIdlingResource() {
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
}
@Test
@UiThread
@ScreenshotTest
fun empty() {
launchActivity<NotificationsActivity>().use { scenario ->
scenario.onActivity { sut ->
onIdleSync {
EspressoIdlingResource.increment()
sut.populateList(ArrayList())
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "empty", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
@SuppressWarnings("MagicNumber")
fun showNotifications() {
@ -117,24 +139,35 @@ class NotificationsActivityIT : AbstractIT() {
)
)
activityRule.launchActivity(null).apply {
runOnUiThread {
populateList(notifications)
launchActivity<NotificationsActivity>().use { scenario ->
scenario.onActivity { sut ->
onIdleSync {
EspressoIdlingResource.increment()
sut.populateList(notifications)
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "showNotifications", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
shortSleep()
screenshot(binding.list)
}
}
@Test
@UiThread
@ScreenshotTest
fun error() {
val sut: NotificationsActivity = activityRule.launchActivity(null)
shortSleep()
sut.runOnUiThread { sut.setEmptyContent("Error", "Error! Please try again later!") }
screenshot(sut)
launchActivity<NotificationsActivity>().use { scenario ->
scenario.onActivity { sut ->
onIdleSync {
EspressoIdlingResource.increment()
sut.setEmptyContent("Error", "Error! Please try again later!")
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "error", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
}

View file

@ -10,130 +10,176 @@ package com.owncloud.android.ui.trashbin
import android.accounts.Account
import android.accounts.AccountManager
import android.content.Intent
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.annotation.UiThread
import androidx.test.core.app.launchActivity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import com.owncloud.android.utils.EspressoIdlingResource
import com.owncloud.android.AbstractIT
import com.owncloud.android.MainApp
import com.owncloud.android.lib.common.accounts.AccountUtils
import com.owncloud.android.utils.ScreenshotTest
import org.junit.Rule
import org.junit.After
import org.junit.Before
import org.junit.Test
class TrashbinActivityIT : AbstractIT() {
private val testClassName = "com.owncloud.android.ui.trashbin.TrashbinActivityIT"
enum class TestCase {
ERROR,
EMPTY,
FILES
}
@get:Rule
var activityRule = IntentsTestRule(TrashbinActivity::class.java, true, false)
@Before
fun registerIdlingResource() {
IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource)
}
@After
fun unregisterIdlingResource() {
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource)
}
@Test
@UiThread
@ScreenshotTest
fun error() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
launchActivity<TrashbinActivity>().use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.ERROR)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
shortSleep()
screenshot(sut)
onIdleSync {
EspressoIdlingResource.increment()
sut.loadFolder(
onComplete = { EspressoIdlingResource.decrement() },
onError = { EspressoIdlingResource.decrement() }
)
val screenShotName = createName(testClassName + "_" + "error", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
fun files() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
launchActivity<TrashbinActivity>().use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.FILES)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
waitForIdleSync()
shortSleep()
shortSleep()
screenshot(sut)
onIdleSync {
EspressoIdlingResource.increment()
sut.loadFolder(
onComplete = { EspressoIdlingResource.decrement() },
onError = { EspressoIdlingResource.decrement() }
)
onView(isRoot()).check(matches(isDisplayed()))
val screenShotName = createName(testClassName + "_" + "files", "")
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
fun empty() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
launchActivity<TrashbinActivity>().use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
shortSleep()
shortSleep()
waitForIdleSync()
screenshot(sut.binding.emptyList.emptyListView)
onIdleSync {
EspressoIdlingResource.increment()
sut.loadFolder(
onComplete = { EspressoIdlingResource.decrement() },
onError = { EspressoIdlingResource.decrement() }
)
onView(isRoot()).check(matches(isDisplayed()))
val screenShotName = createName(testClassName + "_" + "empty", "")
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
fun loading() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
launchActivity<TrashbinActivity>().use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.showInitialLoading() }
shortSleep()
screenshot(sut.binding.listFragmentLayout)
onIdleSync {
EspressoIdlingResource.increment()
sut.showInitialLoading()
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "loading", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
fun normalUser() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
launchActivity<TrashbinActivity>().use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.showUser() }
shortSleep()
screenshot(sut)
onIdleSync {
EspressoIdlingResource.increment()
sut.showUser()
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "normalUser", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
@Test
@UiThread
@ScreenshotTest
fun differentUser() {
val temp = Account("differentUser@https://nextcloud.localhost", MainApp.getAccountType(targetContext))
val platformAccountManager = AccountManager.get(targetContext)
platformAccountManager.addAccountExplicitly(temp, "password", null)
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://nextcloud.localhost")
platformAccountManager.setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "differentUser")
AccountManager.get(targetContext).apply {
addAccountExplicitly(temp, "password", null)
setUserData(temp, AccountUtils.Constants.KEY_OC_BASE_URL, "https://nextcloud.localhost")
setUserData(temp, AccountUtils.Constants.KEY_USER_ID, "differentUser")
}
val intent = Intent()
intent.putExtra(Intent.EXTRA_USER, "differentUser@https://nextcloud.localhost")
val sut: TrashbinActivity = activityRule.launchActivity(intent)
val intent = Intent(targetContext, TrashbinActivity::class.java).apply {
putExtra(Intent.EXTRA_USER, "differentUser@https://nextcloud.localhost")
}
launchActivity<TrashbinActivity>(intent).use { scenario ->
scenario.onActivity { sut ->
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.showUser() }
shortSleep()
screenshot(sut)
onIdleSync {
EspressoIdlingResource.increment()
sut.showUser()
EspressoIdlingResource.decrement()
val screenShotName = createName(testClassName + "_" + "differentUser", "")
onView(isRoot()).check(matches(isDisplayed()))
screenshotViaName(sut, screenShotName)
}
}
}
}
}

View file

@ -0,0 +1,27 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.utils
import androidx.test.espresso.idling.CountingIdlingResource
object EspressoIdlingResource {
private const val RESOURCE = "GLOBAL"
@JvmField val countingIdlingResource = CountingIdlingResource(RESOURCE)
fun increment() {
countingIdlingResource.increment()
}
fun decrement() {
if (!countingIdlingResource.isIdleNow) {
countingIdlingResource.decrement()
}
}
}

View file

@ -176,7 +176,7 @@ class TrashbinActivity :
)
}
fun loadFolder() {
fun loadFolder(onComplete: () -> Unit = {}, onError: () -> Unit = {}) {
trashbinListAdapter?.let {
if (it.itemCount > EMPTY_LIST_COUNT) {
binding.swipeContainingList.isRefreshing = true
@ -184,7 +184,7 @@ class TrashbinActivity :
showInitialLoading()
}
trashbinPresenter?.loadFolder()
trashbinPresenter?.loadFolder(onComplete, onError)
}
}

View file

@ -25,7 +25,7 @@ interface TrashbinContract {
interface Presenter {
val isRoot: Boolean
fun loadFolder()
fun loadFolder(onCompleted: () -> Unit = {}, onError: () -> Unit = {})
fun navigateUp()
fun enterFolder(folder: String?)
fun restoreTrashbinFile(file: TrashbinFile?)

View file

@ -43,16 +43,18 @@ class TrashbinPresenter(
}
}
override fun loadFolder() {
override fun loadFolder(onCompleted: () -> Unit, onError: () -> Unit) {
trashbinRepository.getFolder(
currentPath,
object : LoadFolderCallback {
override fun onSuccess(files: List<TrashbinFile?>?) {
trashbinView.showTrashbinFolder(files)
onCompleted()
}
override fun onError(error: Int) {
trashbinView.showError(error)
onError()
}
}
)

View file

@ -6384,6 +6384,14 @@
<sha256 value="e4bb54753c36a27a0e5d70154a5034fedd8feac4282295034bfd483d6c7aae78" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google.android.apps.common.testing.accessibility.framework" name="accessibility-test-framework" version="3.1">
<artifact name="accessibility-test-framework-3.1.aar">
<sha256 value="e641e2a2c7287afd41b85310dd8f1344a8668034bbbfc4b02f58a48fd9c05ec7" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
<artifact name="accessibility-test-framework-3.1.pom">
<sha256 value="80567228cdbd44d61e5320cd090883de7232dbc1ed7ebf5ab5c9810c11cd67e0" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.google.android.apps.common.testing.accessibility.framework" name="accessibility-test-framework" version="3.1.2">
<artifact name="accessibility-test-framework-3.1.2.aar">
<sha256 value="9b586dc8eeeb4f601038e23ef8ffd6a1deeca1163276d02797b0d2b8f9764b62" origin="Generated by Gradle" reason="Artifact is not signed"/>
@ -7567,6 +7575,11 @@
<sha256 value="47f0635b33c969e5f55ac9f1a4a8e10c120b8bab2cb3f06faaabc0e82057e276" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.h3xstream.findsecbugs" name="findsecbugs-root-pom" version="1.13.0">
<artifact name="findsecbugs-root-pom-1.13.0.pom">
<sha256 value="da8755645ba7ae39588598e548f62eb0be1d2f47a6eee57efc5eb2865a4a556b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.intellij" name="annotations" version="12.0">
<artifact name="annotations-12.0.jar">
<sha256 value="f8ab13b14be080fe2f617f90e55599760e4a1b4deeea5c595df63d0d6375ed6d" origin="Generated by Gradle"/>