Skip to content

Commit f204168

Browse files
Merge pull request #16298 from nextcloud/ph/file_upload_rename
When uploading a single file, offer input field to change file name
2 parents 4204e5a + 2b7a1d3 commit f204168

14 files changed

Lines changed: 596 additions & 84 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
@Suppress("ReturnCount")
22+
override fun matchesSafely(view: View): Boolean {
23+
if (view !is TextView) return false
24+
val text = view.text?.toString() ?: ""
25+
val s = view.selectionStart
26+
val e = view.selectionEnd
27+
@Suppress("ComplexCondition")
28+
if (s < 0 || e < 0 || s > e || e > text.length) return false
29+
return text.substring(s, e) == expected
30+
}
31+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.nextcloud.utils
9+
10+
import com.nextcloud.utils.fileNameValidator.FileNameValidator.isExtensionChanged
11+
import junit.framework.TestCase.assertFalse
12+
import junit.framework.TestCase.assertTrue
13+
import org.junit.Test
14+
15+
@Suppress("TooManyFunctions")
16+
class FileExtensionTest {
17+
18+
@Test
19+
fun sameExtensionReturnsFalse() {
20+
assertFalse(isExtensionChanged("file.txt", "other.txt"))
21+
}
22+
23+
@Test
24+
fun differentExtensionReturnsTrue() {
25+
assertTrue(isExtensionChanged("file.txt", "file.pdf"))
26+
}
27+
28+
@Test
29+
fun caseDifferenceDoesNotTriggerChange() {
30+
assertFalse(isExtensionChanged("file.JPG", "file.jpg"))
31+
}
32+
33+
@Test
34+
fun bothWithoutExtensionReturnsFalse() {
35+
assertFalse(isExtensionChanged("README", "LICENSE"))
36+
}
37+
38+
@Test
39+
fun noExtensionToExtensionReturnsTrue() {
40+
assertTrue(isExtensionChanged("README", "file.txt"))
41+
}
42+
43+
@Test
44+
fun extensionToNoExtensionReturnsTrue() {
45+
assertTrue(isExtensionChanged("file.txt", "README"))
46+
}
47+
48+
@Test
49+
fun hiddenFilesWithoutExtensionReturnFalse() {
50+
assertFalse(isExtensionChanged(".gitignore", ".env"))
51+
}
52+
53+
@Test
54+
fun hiddenFileToNormalExtensionReturnsTrue() {
55+
assertTrue(isExtensionChanged(".gitignore", "file.txt"))
56+
}
57+
58+
@Test
59+
fun multipleDotsSameLastExtensionReturnsFalse() {
60+
assertFalse(isExtensionChanged("archive.tar.gz", "backup.gz"))
61+
}
62+
63+
@Test
64+
fun multipleDotsDifferentLastExtensionReturnsTrue() {
65+
assertTrue(isExtensionChanged("archive.tar.gz", "archive.tar.zip"))
66+
}
67+
68+
@Test
69+
fun trailingDotTreatedAsNoExtensionReturnsTrue() {
70+
assertTrue(isExtensionChanged("file.", "file.txt"))
71+
}
72+
73+
@Test
74+
fun bothTrailingDotReturnFalse() {
75+
assertFalse(isExtensionChanged("file.", "another."))
76+
}
77+
78+
@Test
79+
fun emptyStringsReturnFalse() {
80+
assertFalse(isExtensionChanged("", ""))
81+
}
82+
83+
@Test
84+
fun emptyStringToExtensionReturnsTrue() {
85+
assertTrue(isExtensionChanged("", "file.txt"))
86+
}
87+
88+
@Test
89+
fun bothNullReturnFalse() {
90+
assertFalse(isExtensionChanged(null, null))
91+
}
92+
93+
@Test
94+
fun previousNullNewNotNullReturnsTrue() {
95+
assertTrue(isExtensionChanged(null, "file.txt"))
96+
}
97+
98+
@Test
99+
fun previousNotNullNewNullReturnsTrue() {
100+
assertTrue(isExtensionChanged("file.txt", null))
101+
}
102+
103+
@Test
104+
fun singleDotFilenameReturnsFalse() {
105+
assertFalse(isExtensionChanged(".", "."))
106+
}
107+
108+
@Test
109+
fun dotToExtensionReturnsTrue() {
110+
assertTrue(isExtensionChanged(".", "file.txt"))
111+
}
112+
113+
@Test
114+
fun filenamesEndingWithDotReturnFalse() {
115+
assertFalse(isExtensionChanged("test.", "another."))
116+
}
117+
}

app/src/androidTest/java/com/owncloud/android/AbstractIT.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
import java.io.FileWriter;
6868
import java.io.IOException;
6969
import java.io.InputStream;
70+
import java.util.Arrays;
7071
import java.util.Collection;
72+
import java.util.List;
7173
import java.util.Locale;
7274
import java.util.Objects;
7375
import java.util.Optional;
@@ -233,17 +235,19 @@ protected Account[] getAllAccounts() {
233235
return AccountManager.get(targetContext).getAccounts();
234236
}
235237

236-
protected static void createDummyFiles() throws IOException {
238+
protected static List<File> createDummyFiles() throws IOException {
237239
File tempPath = new File(FileStorageUtils.getTemporalPath(account.name));
238240
if (!tempPath.exists()) {
239241
assertTrue(tempPath.mkdirs());
240242
}
241243

242244
assertTrue(tempPath.exists());
243245

244-
createFile("empty.txt", 0);
245-
createFile("nonEmpty.txt", 100);
246-
createFile("chunkedFile.txt", 500000);
246+
return Arrays.asList(
247+
createFile("empty.txt", 0),
248+
createFile("nonEmpty.txt", 100),
249+
createFile("chunkedFile.txt", 500000)
250+
);
247251
}
248252

249253
protected static File getDummyFile(String name) throws IOException {

0 commit comments

Comments
 (0)