Skip to content

Commit 680784b

Browse files
PhilLabalperozturk96
authored andcommitted
Added automated test for renaming during file upload
The UI only allows to proceed with the upload, if the permissions are given accordingly, so the test setup needs access to the corresponding permission keys. Instead of only making one of the keys available, all were made public. Because why should these magic strings be private after all? This only invites duplication and hinders maintenance. Signed-off-by: Philipp Hasper <vcs@hasper.info>
1 parent ddff250 commit 680784b

3 files changed

Lines changed: 128 additions & 10 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2025 Philipp Hasper <vcs@hasper.info>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.test
9+
10+
import android.view.View
11+
import android.widget.TextView
12+
import org.hamcrest.Description
13+
import org.hamcrest.Matcher
14+
import org.hamcrest.TypeSafeMatcher
15+
16+
fun withSelectedText(expected: String): Matcher<View> = object : TypeSafeMatcher<View>() {
17+
override fun describeTo(description: Description) {
18+
description.appendText("with selected text \"$expected\"")
19+
}
20+
21+
override fun matchesSafely(view: View): Boolean {
22+
if (view !is TextView) return false
23+
val text = view.text?.toString() ?: ""
24+
val s = view.selectionStart
25+
val e = view.selectionEnd
26+
if (s < 0 || e < 0 || s > e || e > text.length) return false
27+
return text.substring(s, e) == expected
28+
}
29+
}

app/src/androidTest/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivityIT.kt

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,46 @@
11
/*
22
* Nextcloud - Android Client
33
*
4+
* SPDX-FileCopyrightText: 2025 Philipp Hasper <vcs@hasper.info>
45
* SPDX-FileCopyrightText: 2025 Alper Ozturk <alper.ozturk@nextcloud.com>
56
* SPDX-FileCopyrightText: 2022 Tobias Kaminsky <tobias@kaminsky.me>
67
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH
78
* SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
89
*/
910
package com.owncloud.android.ui.activity
1011

12+
import android.content.Intent
13+
import android.net.Uri
14+
import android.view.KeyEvent
1115
import androidx.test.core.app.launchActivity
1216
import androidx.test.espresso.Espresso.onView
17+
import androidx.test.espresso.action.ViewActions
1318
import androidx.test.espresso.assertion.ViewAssertions.matches
19+
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
1420
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
21+
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
1522
import androidx.test.espresso.matcher.ViewMatchers.isRoot
23+
import androidx.test.espresso.matcher.ViewMatchers.withId
24+
import androidx.test.espresso.matcher.ViewMatchers.withText
1625
import com.facebook.testing.screenshot.internal.TestNameDetector
26+
import com.nextcloud.client.preferences.AppPreferencesImpl
27+
import com.nextcloud.test.GrantStoragePermissionRule
28+
import com.nextcloud.test.withSelectedText
29+
import com.nextcloud.utils.extensions.removeFileExtension
1730
import com.owncloud.android.AbstractIT
31+
import com.owncloud.android.R
32+
import com.owncloud.android.datamodel.OCFile
1833
import com.owncloud.android.utils.ScreenshotTest
34+
import org.junit.Rule
1935
import org.junit.Test
36+
import org.junit.rules.TestRule
37+
import java.io.File
2038

2139
class ReceiveExternalFilesActivityIT : AbstractIT() {
2240

41+
@get:Rule
42+
var storagePermissionRule: TestRule = GrantStoragePermissionRule.grant()
43+
2344
@Test
2445
@ScreenshotTest
2546
fun open() {
@@ -41,4 +62,72 @@ class ReceiveExternalFilesActivityIT : AbstractIT() {
4162
open()
4263
removeAccount(secondAccount)
4364
}
65+
66+
67+
fun createSendIntent(file: File): Intent = Intent(targetContext, ReceiveExternalFilesActivity::class.java).apply {
68+
action = Intent.ACTION_SEND
69+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
70+
putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file))
71+
}
72+
73+
@Test
74+
fun renameSingleFileUpload() {
75+
val imageFile = getDummyFile("image.jpg")
76+
val intent = createSendIntent(imageFile)
77+
78+
// Create folders with the necessary permissions
79+
val mainFolder = OCFile("/folder/").apply {
80+
permissions = OCFile.PERMISSION_CAN_CREATE_FILE_AND_FOLDER
81+
setFolder()
82+
fileDataStorageManager.saveNewFile(this)
83+
}
84+
val subFolder = OCFile("${mainFolder.remotePath}sub folder/").apply {
85+
permissions = OCFile.PERMISSION_CAN_CREATE_FILE_AND_FOLDER
86+
setFolder()
87+
fileDataStorageManager.saveNewFile(this)
88+
}
89+
90+
// Store the folder in preferences, so the activity starts from there.
91+
@Suppress("DEPRECATION")
92+
val preferences = AppPreferencesImpl.fromContext(targetContext)
93+
preferences.setLastUploadPath(mainFolder.remotePath)
94+
95+
launchActivity<ReceiveExternalFilesActivity>(intent).use {
96+
val expectedMainFolderTitle = (getCurrentActivity() as ToolbarActivity).getActionBarTitle(mainFolder, false)
97+
// Verify that the test starts in the expected folder. If this fails, change the setup calls above
98+
onView(withId(R.id.toolbar))
99+
.check(matches(hasDescendant(withText(expectedMainFolderTitle))))
100+
101+
onView(withText(R.string.uploader_btn_upload_text))
102+
.check(matches(isDisplayed()))
103+
.check(matches(isEnabled()))
104+
105+
// Test the pre-selection behavior (filename, but without extension, shall be selected)
106+
onView(withId(R.id.user_input))
107+
.check(matches(withText(imageFile.name)))
108+
.perform(ViewActions.click())
109+
.check(matches(withSelectedText(imageFile.name.removeFileExtension())))
110+
111+
// Set a new file name
112+
val secondFileName = "New filename.jpg"
113+
onView(withId(R.id.user_input))
114+
.perform(ViewActions.typeTextIntoFocusedView(secondFileName.removeFileExtension()))
115+
.check(matches(withText(secondFileName)))
116+
// Leave the field and come back to verify the pre-selection behavior correctly handles the new name
117+
.perform(ViewActions.pressKey(KeyEvent.KEYCODE_TAB))
118+
.perform(ViewActions.click())
119+
.check(matches(withSelectedText(secondFileName.removeFileExtension())))
120+
121+
// Set a file name without file extension
122+
val thirdFileName = "No extension"
123+
onView(withId(R.id.user_input))
124+
.perform(ViewActions.clearText())
125+
.perform(ViewActions.typeTextIntoFocusedView(thirdFileName))
126+
.check(matches(withText(thirdFileName)))
127+
// Leave the field and come back to verify the pre-selection behavior correctly handles the new name
128+
.perform(ViewActions.pressKey(KeyEvent.KEYCODE_TAB))
129+
.perform(ViewActions.click())
130+
.check(matches(withSelectedText(thirdFileName)))
131+
}
132+
}
44133
}

app/src/main/java/com/owncloud/android/datamodel/OCFile.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,16 @@
4747
public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterface {
4848

4949
public final static String PERMISSION_CAN_RESHARE = "R";
50-
private final static String PERMISSION_SHARED = "S";
51-
private final static String PERMISSION_MOUNTED = "M";
52-
private final static String PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER = "C";
53-
private final static String PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER = "K";
54-
private final static String PERMISSION_CAN_READ = "G";
55-
private final static String PERMISSION_CAN_WRITE = "W";
56-
private final static String PERMISSION_CAN_DELETE_OR_LEAVE_SHARE = "D";
57-
private final static String PERMISSION_CAN_RENAME = "N";
58-
private final static String PERMISSION_CAN_MOVE = "V";
59-
private final static String PERMISSION_CAN_CREATE_FILE_AND_FOLDER = PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER + PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER;
50+
public final static String PERMISSION_SHARED = "S";
51+
public final static String PERMISSION_MOUNTED = "M";
52+
public final static String PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER = "C";
53+
public final static String PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER = "K";
54+
public final static String PERMISSION_CAN_READ = "G";
55+
public final static String PERMISSION_CAN_WRITE = "W";
56+
public final static String PERMISSION_CAN_DELETE_OR_LEAVE_SHARE = "D";
57+
public final static String PERMISSION_CAN_RENAME = "N";
58+
public final static String PERMISSION_CAN_MOVE = "V";
59+
public final static String PERMISSION_CAN_CREATE_FILE_AND_FOLDER = PERMISSION_CAN_CREATE_FILE_INSIDE_FOLDER + PERMISSION_CAN_CREATE_FOLDER_INSIDE_FOLDER;
6060

6161
private final static int MAX_FILE_SIZE_FOR_IMMEDIATE_PREVIEW_BYTES = 1024000;
6262

0 commit comments

Comments
 (0)