}
export const selectAllFiles = () => {
- cy.get('[data-cy-files-list-selection-checkbox]').findByRole('checkbox').click({ force: true })
+ cy.get('[data-cy-files-list-selection-checkbox]')
+ .findByRole('checkbox', { checked: false })
+ .click({ force: true })
+}
+export const deselectAllFiles = () => {
+ cy.get('[data-cy-files-list-selection-checkbox]')
+ .findByRole('checkbox', { checked: true })
+ .click({ force: true })
}
-export const selectRowForFile = (filename: string) => {
+
+export const selectRowForFile = (filename: string, options: Partial<Cypress.ClickOptions> = {}) => {
getRowForFile(filename)
.find('[data-cy-files-list-row-checkbox]')
.findByRole('checkbox')
- .click({ force: true })
+ // don't use click to avoid triggering side effects events
+ .trigger('change', { ...options, force: true })
.should('be.checked')
cy.get('[data-cy-files-list-selection-checkbox]').findByRole('checkbox').should('satisfy', (elements) => {
return elements.length === 1 && (elements[0].checked === true || elements[0].indeterminate === true)
--- /dev/null
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { getRowForFile, moveFile, copyFile, navigateToFolder } from './FilesUtils.ts'
+
+describe('Files: Move or copy files', { testIsolation: true }, () => {
+ let currentUser
+ beforeEach(() => {
+ cy.createRandomUser().then((user) => {
+ currentUser = user
+ cy.login(user)
+ })
+ })
+ afterEach(() => {
+ // nice to have cleanup
+ cy.deleteUser(currentUser)
+ })
+
+
+ it('Can copy a file to new folder', () => {
+ // Prepare initial state
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ .mkdir(currentUser, '/new-folder')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('original.txt', 'new-folder')
+
+ navigateToFolder('new-folder')
+
+ cy.url().should('contain', 'dir=/new-folder')
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('new-folder').should('not.exist')
+ })
+
+ it('Can move a file to new folder', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ .mkdir(currentUser, '/new-folder')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ moveFile('original.txt', 'new-folder')
+
+ // wait until visible again
+ getRowForFile('new-folder').should('be.visible')
+
+ // original should be moved -> not exist anymore
+ getRowForFile('original.txt').should('not.exist')
+ navigateToFolder('new-folder')
+
+ cy.url().should('contain', 'dir=/new-folder')
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('new-folder').should('not.exist')
+ })
+
+ /**
+ * Test for https://github.com/nextcloud/server/issues/41768
+ */
+ it('Can move a file to folder with similar name', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original')
+ .mkdir(currentUser, '/original folder')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ moveFile('original', 'original folder')
+
+ // wait until visible again
+ getRowForFile('original folder').should('be.visible')
+
+ // original should be moved -> not exist anymore
+ getRowForFile('original').should('not.exist')
+ navigateToFolder('original folder')
+
+ cy.url().should('contain', 'dir=/original%20folder')
+ getRowForFile('original').should('be.visible')
+ getRowForFile('original folder').should('not.exist')
+ })
+
+ it('Can move a file to its parent folder', () => {
+ cy.mkdir(currentUser, '/new-folder')
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/new-folder/original.txt')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ navigateToFolder('new-folder')
+ cy.url().should('contain', 'dir=/new-folder')
+
+ moveFile('original.txt', '/')
+
+ // wait until visible again
+ cy.get('main').contains('No files in here').should('be.visible')
+
+ // original should be moved -> not exist anymore
+ getRowForFile('original.txt').should('not.exist')
+
+ cy.visit('/apps/files')
+ getRowForFile('new-folder').should('be.visible')
+ getRowForFile('original.txt').should('be.visible')
+ })
+
+ it('Can copy a file to same folder', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('original.txt', '.')
+
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('original (copy).txt').should('be.visible')
+ })
+
+ it('Can copy a file multiple times to same folder', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original (copy).txt')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('original.txt', '.')
+
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('original (copy 2).txt').should('be.visible')
+ })
+
+ /**
+ * Test that a copied folder with a dot will be renamed correctly ('foo.bar' -> 'foo.bar (copy)')
+ * Test for: https://github.com/nextcloud/server/issues/43843
+ */
+ it('Can copy a folder to same folder', () => {
+ cy.mkdir(currentUser, '/foo.bar')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('foo.bar', '.')
+
+ getRowForFile('foo.bar').should('be.visible')
+ getRowForFile('foo.bar (copy)').should('be.visible')
+ })
+
+ /** Test for https://github.com/nextcloud/server/issues/43329 */
+ context('escaping file and folder names', () => {
+ it('Can handle files with special characters', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ .mkdir(currentUser, '/can\'t say')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('original.txt', 'can\'t say')
+
+ navigateToFolder('can\'t say')
+
+ cy.url().should('contain', 'dir=/can%27t%20say')
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('can\'t say').should('not.exist')
+ })
+
+ /**
+ * If escape is set to false (required for test above) then "<a>foo" would result in "<a>foo</a>" if sanitizing is not disabled
+ * We should disable it as vue already escapes the text when using v-text
+ */
+ it('does not incorrectly sanitize file names', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
+ .mkdir(currentUser, '/<a href="#">foo')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ copyFile('original.txt', '<a href="#">foo')
+
+ navigateToFolder('<a href="#">foo')
+
+ cy.url().should('contain', 'dir=/%3Ca%20href%3D%22%23%22%3Efoo')
+ getRowForFile('original.txt').should('be.visible')
+ getRowForFile('<a href="#">foo').should('not.exist')
+ })
+ })
+})
--- /dev/null
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import type { User } from '@nextcloud/cypress'
+import { deselectAllFiles, selectAllFiles, selectRowForFile } from './FilesUtils'
+
+const files = {
+ 'image.jpg': 'image/jpeg',
+ 'document.pdf': 'application/pdf',
+ 'archive.zip': 'application/zip',
+ 'audio.mp3': 'audio/mpeg',
+ 'video.mp4': 'video/mp4',
+ 'readme.md': 'text/markdown',
+ 'welcome.txt': 'text/plain',
+}
+const filesCount = Object.keys(files).length
+
+describe('files: Select all files', { testIsolation: true }, () => {
+ let user: User
+
+ before(() => {
+ cy.createRandomUser().then(($user) => {
+ user = $user
+ Object.keys(files).forEach((file) => {
+ cy.uploadContent(user, new Blob(), files[file], '/' + file)
+ })
+ })
+ })
+
+ beforeEach(() => {
+ cy.login(user)
+ cy.visit('/apps/files')
+ })
+
+ it('Can select and unselect all files', () => {
+ cy.get('[data-cy-files-list-row-fileid]').should('have.length', filesCount)
+ cy.get('[data-cy-files-list-row-checkbox]').should('have.length', filesCount)
+
+ selectAllFiles()
+
+ cy.get('.files-list__selected').should('have.text', '7 selected')
+ cy.get('[data-cy-files-list-row-checkbox]').findByRole('checkbox').should('be.checked')
+
+ deselectAllFiles()
+
+ cy.get('.files-list__selected').should('not.exist')
+ cy.get('[data-cy-files-list-row-checkbox]').findByRole('checkbox').should('not.be.checked')
+ })
+
+ it('Can select some files randomly', () => {
+ const randomFiles = Object.keys(files).reduce((acc, file) => {
+ if (Math.random() > 0.1) {
+ acc.push(file)
+ }
+ return acc
+ }, [] as string[])
+
+ randomFiles.forEach(name => selectRowForFile(name))
+
+ cy.get('.files-list__selected').should('have.text', `${randomFiles.length} selected`)
+ cy.get('[data-cy-files-list-row-checkbox] input[type="checkbox"]:checked').should('have.length', randomFiles.length)
+ })
+
+ it('Can select range of files with shift key', () => {
+ cy.get('[data-cy-files-list-row-checkbox]').should('have.length', filesCount)
+ selectRowForFile('audio.mp3')
+ cy.window().trigger('keydown', { shiftKey: true })
+ selectRowForFile('readme.md', { shiftKey: true })
+ cy.window().trigger('keyup', { shiftKey: false })
+
+ cy.get('.files-list__selected').should('have.text', '4 selected')
+ cy.get('[data-cy-files-list-row-checkbox] input[type="checkbox"]:checked').should('have.length', 4)
+
+ })
+})
--- /dev/null
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+describe('Files: Sorting the file list', { testIsolation: true }, () => {
+ let currentUser
+ beforeEach(() => {
+ cy.createRandomUser().then((user) => {
+ currentUser = user
+ cy.login(user)
+ })
+ })
+
+ it('Files are sorted by name ascending by default', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 first.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/z last.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/A.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/Ä.txt')
+ .mkdir(currentUser, '/m')
+ .mkdir(currentUser, '/4')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('4')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('m')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 first.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('A.txt')
+ break
+ case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('Ä.txt')
+ break
+ case 5: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 6: expect($row.attr('data-cy-files-list-row-name')).to.eq('z last.txt')
+ break
+ }
+ })
+ })
+
+ /**
+ * Regression test of https://github.com/nextcloud/server/issues/45829
+ */
+ it('Filesnames with numbers are sorted by name ascending by default', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/name.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/name_03.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/name_02.txt')
+ .uploadContent(currentUser, new Blob(), 'text/plain', '/name_01.txt')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('name.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_01.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_02.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_03.txt')
+ break
+ }
+ })
+ })
+
+ it('Can sort by size', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 tiny.txt')
+ .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z big.txt')
+ .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a medium.txt')
+ .mkdir(currentUser, '/folder')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ // click sort button
+ cy.get('th').contains('button', 'Size').click()
+ // sorting is set
+ cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')
+ // Files are sorted
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
+ break
+ case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
+ break
+ }
+ })
+
+ // click sort button
+ cy.get('th').contains('button', 'Size').click()
+ // sorting is set
+ cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')
+ // Files are sorted
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
+ break
+ }
+ })
+ })
+
+ it('Can sort by mtime', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
+ .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
+ .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ // click sort button
+ cy.get('th').contains('button', 'Modified').click()
+ // sorting is set
+ cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')
+ // Files are sorted
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
+ break
+ }
+ })
+
+ // reverse order
+ cy.get('th').contains('button', 'Modified').click()
+ cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
+ break
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
+ break
+ }
+ })
+ })
+
+ it('Favorites are sorted first', () => {
+ cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
+ .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
+ .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
+ .setFileAsFavorite(currentUser, '/a.txt')
+ cy.login(currentUser)
+ cy.visit('/apps/files')
+
+ cy.log('By name - ascending')
+ cy.get('th').contains('button', 'Name').click()
+ cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'ascending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ }
+ })
+
+ cy.log('By name - descending')
+ cy.get('th').contains('button', 'Name').click()
+ cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'descending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ }
+ })
+
+ cy.log('By size - ascending')
+ cy.get('th').contains('button', 'Size').click()
+ cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ }
+ })
+
+ cy.log('By size - descending')
+ cy.get('th').contains('button', 'Size').click()
+ cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ }
+ })
+
+ cy.log('By mtime - ascending')
+ cy.get('th').contains('button', 'Modified').click()
+ cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ }
+ })
+
+ cy.log('By mtime - descending')
+ cy.get('th').contains('button', 'Modified').click()
+ cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')
+
+ cy.get('[data-cy-files-list-row]').each(($row, index) => {
+ switch (index) {
+ case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
+ break
+ case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
+ break
+ case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
+ break
+ case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
+ break
+ }
+ })
+ })
+})
+++ /dev/null
-/**
- * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-import { getRowForFile, moveFile, copyFile, navigateToFolder } from './FilesUtils.ts'
-
-describe('Files: Move or copy files', { testIsolation: true }, () => {
- let currentUser
- beforeEach(() => {
- cy.createRandomUser().then((user) => {
- currentUser = user
- cy.login(user)
- })
- })
- afterEach(() => {
- // nice to have cleanup
- cy.deleteUser(currentUser)
- })
-
-
- it('Can copy a file to new folder', () => {
- // Prepare initial state
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- .mkdir(currentUser, '/new-folder')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('original.txt', 'new-folder')
-
- navigateToFolder('new-folder')
-
- cy.url().should('contain', 'dir=/new-folder')
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('new-folder').should('not.exist')
- })
-
- it('Can move a file to new folder', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- .mkdir(currentUser, '/new-folder')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- moveFile('original.txt', 'new-folder')
-
- // wait until visible again
- getRowForFile('new-folder').should('be.visible')
-
- // original should be moved -> not exist anymore
- getRowForFile('original.txt').should('not.exist')
- navigateToFolder('new-folder')
-
- cy.url().should('contain', 'dir=/new-folder')
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('new-folder').should('not.exist')
- })
-
- /**
- * Test for https://github.com/nextcloud/server/issues/41768
- */
- it('Can move a file to folder with similar name', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original')
- .mkdir(currentUser, '/original folder')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- moveFile('original', 'original folder')
-
- // wait until visible again
- getRowForFile('original folder').should('be.visible')
-
- // original should be moved -> not exist anymore
- getRowForFile('original').should('not.exist')
- navigateToFolder('original folder')
-
- cy.url().should('contain', 'dir=/original%20folder')
- getRowForFile('original').should('be.visible')
- getRowForFile('original folder').should('not.exist')
- })
-
- it('Can move a file to its parent folder', () => {
- cy.mkdir(currentUser, '/new-folder')
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/new-folder/original.txt')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- navigateToFolder('new-folder')
- cy.url().should('contain', 'dir=/new-folder')
-
- moveFile('original.txt', '/')
-
- // wait until visible again
- cy.get('main').contains('No files in here').should('be.visible')
-
- // original should be moved -> not exist anymore
- getRowForFile('original.txt').should('not.exist')
-
- cy.visit('/apps/files')
- getRowForFile('new-folder').should('be.visible')
- getRowForFile('original.txt').should('be.visible')
- })
-
- it('Can copy a file to same folder', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('original.txt', '.')
-
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('original (copy).txt').should('be.visible')
- })
-
- it('Can copy a file multiple times to same folder', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original (copy).txt')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('original.txt', '.')
-
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('original (copy 2).txt').should('be.visible')
- })
-
- /**
- * Test that a copied folder with a dot will be renamed correctly ('foo.bar' -> 'foo.bar (copy)')
- * Test for: https://github.com/nextcloud/server/issues/43843
- */
- it('Can copy a folder to same folder', () => {
- cy.mkdir(currentUser, '/foo.bar')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('foo.bar', '.')
-
- getRowForFile('foo.bar').should('be.visible')
- getRowForFile('foo.bar (copy)').should('be.visible')
- })
-
- /** Test for https://github.com/nextcloud/server/issues/43329 */
- context('escaping file and folder names', () => {
- it('Can handle files with special characters', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- .mkdir(currentUser, '/can\'t say')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('original.txt', 'can\'t say')
-
- navigateToFolder('can\'t say')
-
- cy.url().should('contain', 'dir=/can%27t%20say')
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('can\'t say').should('not.exist')
- })
-
- /**
- * If escape is set to false (required for test above) then "<a>foo" would result in "<a>foo</a>" if sanitizing is not disabled
- * We should disable it as vue already escapes the text when using v-text
- */
- it('does not incorrectly sanitize file names', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/original.txt')
- .mkdir(currentUser, '/<a href="#">foo')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- copyFile('original.txt', '<a href="#">foo')
-
- navigateToFolder('<a href="#">foo')
-
- cy.url().should('contain', 'dir=/%3Ca%20href%3D%22%23%22%3Efoo')
- getRowForFile('original.txt').should('be.visible')
- getRowForFile('<a href="#">foo').should('not.exist')
- })
- })
-})
+++ /dev/null
-/**
- * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-describe('Files: Sorting the file list', { testIsolation: true }, () => {
- let currentUser
- beforeEach(() => {
- cy.createRandomUser().then((user) => {
- currentUser = user
- cy.login(user)
- })
- })
-
- it('Files are sorted by name ascending by default', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 first.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/z last.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/A.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/Ä.txt')
- .mkdir(currentUser, '/m')
- .mkdir(currentUser, '/4')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('4')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('m')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 first.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('A.txt')
- break
- case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('Ä.txt')
- break
- case 5: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 6: expect($row.attr('data-cy-files-list-row-name')).to.eq('z last.txt')
- break
- }
- })
- })
-
- /**
- * Regression test of https://github.com/nextcloud/server/issues/45829
- */
- it('Filesnames with numbers are sorted by name ascending by default', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/name.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/name_03.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/name_02.txt')
- .uploadContent(currentUser, new Blob(), 'text/plain', '/name_01.txt')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('name.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_01.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_02.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('name_03.txt')
- break
- }
- })
- })
-
- it('Can sort by size', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1 tiny.txt')
- .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z big.txt')
- .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a medium.txt')
- .mkdir(currentUser, '/folder')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- // click sort button
- cy.get('th').contains('button', 'Size').click()
- // sorting is set
- cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')
- // Files are sorted
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
- break
- case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
- break
- }
- })
-
- // click sort button
- cy.get('th').contains('button', 'Size').click()
- // sorting is set
- cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')
- // Files are sorted
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('folder')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z big.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('a medium.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 4: expect($row.attr('data-cy-files-list-row-name')).to.eq('1 tiny.txt')
- break
- }
- })
- })
-
- it('Can sort by mtime', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
- .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
- .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- // click sort button
- cy.get('th').contains('button', 'Modified').click()
- // sorting is set
- cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')
- // Files are sorted
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
- break
- }
- })
-
- // reverse order
- cy.get('th').contains('button', 'Modified').click()
- cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt') // uploaded right now
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt') // fake time of yesterday
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt') // fake time of yesterday and few minutes
- break
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt') // fake time of yesterday and ~15 minutes ago
- break
- }
- })
- })
-
- it('Favorites are sorted first', () => {
- cy.uploadContent(currentUser, new Blob(), 'text/plain', '/1.txt', Date.now() / 1000 - 86400 - 1000)
- .uploadContent(currentUser, new Blob(['a'.repeat(1024)]), 'text/plain', '/z.txt', Date.now() / 1000 - 86400)
- .uploadContent(currentUser, new Blob(['a'.repeat(512)]), 'text/plain', '/a.txt', Date.now() / 1000 - 86400 - 500)
- .setFileAsFavorite(currentUser, '/a.txt')
- cy.login(currentUser)
- cy.visit('/apps/files')
-
- cy.log('By name - ascending')
- cy.get('th').contains('button', 'Name').click()
- cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'ascending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- }
- })
-
- cy.log('By name - descending')
- cy.get('th').contains('button', 'Name').click()
- cy.contains('th', 'Name').should('have.attr', 'aria-sort', 'descending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- }
- })
-
- cy.log('By size - ascending')
- cy.get('th').contains('button', 'Size').click()
- cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'ascending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- }
- })
-
- cy.log('By size - descending')
- cy.get('th').contains('button', 'Size').click()
- cy.contains('th', 'Size').should('have.attr', 'aria-sort', 'descending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- }
- })
-
- cy.log('By mtime - ascending')
- cy.get('th').contains('button', 'Modified').click()
- cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'ascending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- }
- })
-
- cy.log('By mtime - descending')
- cy.get('th').contains('button', 'Modified').click()
- cy.contains('th', 'Modified').should('have.attr', 'aria-sort', 'descending')
-
- cy.get('[data-cy-files-list-row]').each(($row, index) => {
- switch (index) {
- case 0: expect($row.attr('data-cy-files-list-row-name')).to.eq('a.txt')
- break
- case 1: expect($row.attr('data-cy-files-list-row-name')).to.eq('1.txt')
- break
- case 2: expect($row.attr('data-cy-files-list-row-name')).to.eq('z.txt')
- break
- case 3: expect($row.attr('data-cy-files-list-row-name')).to.eq('welcome.txt')
- break
- }
- })
- })
-})