Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ configurations.configureEach {
useVersion(libs.versions.jacoco.get())
} else if (requested.group == "commons-logging" && requested.name == "commons-logging") {
useTarget(libs.slfj)
} else if (requested.group == "org.hamcrest") {
useVersion("2.2")
because(
"Align hamcrest on compile and runtime. 1.3 (via junit) exposes fixed-arity " +
"anyOf/allOf overloads that 2.2 (via androidx.test) removed, causing NoSuchMethodError."
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ class FileDisplayActivityScreenshotIT : AbstractIT() {

@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
Manifest.permission.WRITE_EXTERNAL_STORAGE
)

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package com.nextcloud.client

import android.content.Intent
import android.os.Looper
import androidx.test.core.app.launchActivity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
Expand Down Expand Up @@ -61,12 +60,8 @@ class SettingsActivityIT : AbstractIT() {
}
}

@Suppress("DEPRECATION")
@Test
fun showMnemonic() {
if (Looper.myLooper() == null) {
Looper.prepare()
}
val intent = Intent().apply {
putExtra(RequestCredentialsActivity.KEY_CHECK_RESULT, RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE)
}
Expand All @@ -76,13 +71,10 @@ class SettingsActivityIT : AbstractIT() {
}

launchActivity<SettingsActivity>().use { scenario ->
onView(isRoot()).check(matches(isDisplayed()))

scenario.onActivity { sut ->
sut.handleMnemonicRequest(intent)
}

Looper.myLooper()?.quitSafely()
Assert.assertTrue(true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,13 @@ class AssistantRepositoryTests : AbstractOnServerIT() {

testCreateTask()

sleep(120)

runBlocking {
val taskList = sut?.getTaskList("assistant")
assertTrue(taskList != null)

sleep(120)

assert((taskList?.size ?: 0) > 0)
val taskId = taskList?.firstOrNull()?.id ?: return@runBlocking

val result = sut?.deleteTask(taskList!!.first().id)
val result = sut?.deleteTask(taskId)
assertTrue(result?.isSuccess == true)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.test

import android.os.ParcelFileDescriptor
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

/**
* Disables system animations for the duration of the test.
*
* Espresso requires that no view keeps requesting layout while it waits for the root window. Animated components such
* as [com.google.android.material.snackbar.Snackbar] keep animating unless the global animation scales are set to zero.
* The Gradle `animationsDisabled` option only passes `--no-window-animation`, which leaves `animator_duration_scale`
* untouched, so animations still run on CI. This rule zeroes all scales directly via a shell command.
*/
class DisableAnimationsRule : TestRule {

override fun apply(base: Statement, description: Description): Statement = object : Statement() {
override fun evaluate() {
setAnimationScale("0.0")
try {
base.evaluate()
} finally {
setAnimationScale("1.0")
}
}
}

private fun setAnimationScale(scale: String) {
val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
SCALE_SETTINGS.forEach { setting ->
val descriptor = uiAutomation.executeShellCommand("settings put global $setting $scale")
ParcelFileDescriptor.AutoCloseInputStream(descriptor).use { it.readBytes() }
}
}

companion object {
private val SCALE_SETTINGS = listOf(
"window_animation_scale",
"transition_animation_scale",
"animator_duration_scale"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package com.nextcloud.test
import androidx.test.espresso.IdlingResource
import com.owncloud.android.datamodel.FileDataStorageManager
import com.owncloud.android.datamodel.OCFile
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicReference

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.nextcloud.test

import android.Manifest
import android.os.Build
import androidx.test.rule.GrantPermissionRule

/**
* Grants the runtime permissions UI tests need. POST_NOTIFICATIONS only exists on API 33+, so
* granting it on lower API levels fails with "Unknown permission" and it is therefore requested only
* when supported.
*/
object GrantTestPermissionRule {

fun grantStorageAndNotification(): GrantPermissionRule = if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.TIRAMISU
) {
GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
)
} else {
GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ import org.junit.Test
class SetOnlineStatusBottomSheetIT : AbstractIT() {

@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
)
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)

@Test
fun open() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
*/
package com.nextcloud.ui

import android.Manifest
import androidx.test.core.app.launchActivity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.rule.GrantPermissionRule
import com.nextcloud.test.GrantTestPermissionRule
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import com.owncloud.android.lib.resources.users.ClearAt
Expand All @@ -28,10 +28,7 @@ import org.junit.Test

class SetStatusMessageBottomSheetIT : AbstractIT() {
@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
)
val permissionRule: GrantPermissionRule = GrantTestPermissionRule.grantStorageAndNotification()

@Test
fun open() {
Expand Down
34 changes: 11 additions & 23 deletions app/src/androidTest/java/com/nmc/android/ui/LauncherActivityIT.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,12 @@
*/
package com.nmc.android.ui

import android.view.View
import androidx.test.core.app.launchActivity
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.owncloud.android.AbstractIT
import com.owncloud.android.R
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

Expand All @@ -24,29 +20,21 @@ class LauncherActivityIT : AbstractIT() {

@Test
fun testSplashScreenWithEmptyTitlesShouldHideTitles() {
launchActivity<LauncherActivity>().use { scenario ->
onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed()))
onView(
withId(R.id.splashScreenBold)
).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)))
onView(
withId(R.id.splashScreenNormal)
).check(matches(withEffectiveVisibility(ViewMatchers.Visibility.GONE)))
launchActivity<LauncherActivity>().onActivity { activity ->
assertEquals(View.VISIBLE, activity.findViewById<View>(R.id.ivSplash).visibility)
assertEquals(View.GONE, activity.findViewById<View>(R.id.splashScreenBold).visibility)
assertEquals(View.GONE, activity.findViewById<View>(R.id.splashScreenNormal).visibility)
}
}

@Test
fun testSplashScreenWithTitlesShouldShowTitles() {
launchActivity<LauncherActivity>().use { scenario ->
onView(withId(R.id.ivSplash)).check(matches(isCompletelyDisplayed()))
launchActivity<LauncherActivity>().onActivity { activity ->
activity.setSplashTitles("Example", "Cloud")

scenario.onActivity {
it.setSplashTitles("Example", "Cloud")
}

val onePercentArea = ViewMatchers.isDisplayingAtLeast(1)
onView(withId(R.id.splashScreenBold)).check(matches(onePercentArea))
onView(withId(R.id.splashScreenNormal)).check(matches(onePercentArea))
assertEquals(View.VISIBLE, activity.findViewById<View>(R.id.ivSplash).visibility)
assertEquals(View.VISIBLE, activity.findViewById<View>(R.id.splashScreenBold).visibility)
assertEquals(View.VISIBLE, activity.findViewById<View>(R.id.splashScreenNormal).visibility)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ class ScreenshotsIT : AbstractIT() {

@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
Manifest.permission.WRITE_EXTERNAL_STORAGE
)

@Test
Expand Down
Loading
Loading