From 8b36b915febf76ade3e00c2d0563a370e8ba9f62 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Fri, 28 Feb 2025 18:13:24 +0100 Subject: [PATCH] fix(files_trashbin): disable bulk download for trashbin The backend does not allow bulk download within the trashbin, so we need to disable this also on the frontend. Signed-off-by: Ferdinand Thiessen --- apps/files/src/actions/downloadAction.ts | 10 +++- cypress/e2e/files/FilesUtils.ts | 10 +++- cypress/e2e/files_trashbin/files.cy.ts | 70 ++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 cypress/e2e/files_trashbin/files.cy.ts diff --git a/apps/files/src/actions/downloadAction.ts b/apps/files/src/actions/downloadAction.ts index 19e0b3502fa19..b4c04d2970c19 100644 --- a/apps/files/src/actions/downloadAction.ts +++ b/apps/files/src/actions/downloadAction.ts @@ -2,7 +2,8 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { FileAction, Node, FileType, DefaultType } from '@nextcloud/files' +import type { Node, View } from '@nextcloud/files' +import { FileAction, FileType, DefaultType } from '@nextcloud/files' import { t } from '@nextcloud/l10n' import { isDownloadable } from '../utils/permissions' @@ -75,7 +76,7 @@ export const action = new FileAction({ displayName: () => t('files', 'Download'), iconSvgInline: () => ArrowDownSvg, - enabled(nodes: Node[]) { + enabled(nodes: Node[], view: View) { if (nodes.length === 0) { return false } @@ -85,6 +86,11 @@ export const action = new FileAction({ return false } + // Trashbin does not allow batch download + if (nodes.length > 1 && view.id === 'trashbin') { + return false + } + return nodes.every(isDownloadable) }, diff --git a/cypress/e2e/files/FilesUtils.ts b/cypress/e2e/files/FilesUtils.ts index fce23fc146346..1534c1b9ddaa2 100644 --- a/cypress/e2e/files/FilesUtils.ts +++ b/cypress/e2e/files/FilesUtils.ts @@ -4,7 +4,7 @@ */ import type { User } from '@nextcloud/cypress' -import { ACTION_COPY_MOVE } from "../../../apps/files/src/actions/moveOrCopyAction" +import { ACTION_COPY_MOVE } from '../../../apps/files/src/actions/moveOrCopyAction.ts' export const getRowForFileId = (fileid: number) => cy.get(`[data-cy-files-list-row-fileid="${fileid}"]`) export const getRowForFile = (filename: string) => cy.get(`[data-cy-files-list-row-name="${CSS.escape(filename)}"]`) @@ -15,7 +15,7 @@ export const getActionsForFile = (filename: string) => getRowForFile(filename).f export const getActionButtonForFileId = (fileid: number) => getActionsForFileId(fileid).findByRole('button', { name: 'Actions' }) export const getActionButtonForFile = (filename: string) => getActionsForFile(filename).findByRole('button', { name: 'Actions' }) -const searchForActionInRow = (row: JQuery, actionId: string): Cypress.Chainable> => { +const searchForActionInRow = (row: JQuery, actionId: string): Cypress.Chainable> => { const action = row.find(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`) if (action.length > 0) { cy.log('Found action in row') @@ -229,7 +229,11 @@ export const deleteFileWithRequest = (user: User, path: string) => { const requestToken = body.token cy.request({ method: 'DELETE', - url: `${Cypress.env('baseUrl')}/remote.php/dav/files/${user.userId}` + path, + url: `${Cypress.env('baseUrl')}/remote.php/dav/files/${user.userId}${path}`, + auth: { + user: user.userId, + password: user.password, + }, headers: { requestToken, }, diff --git a/cypress/e2e/files_trashbin/files.cy.ts b/cypress/e2e/files_trashbin/files.cy.ts new file mode 100644 index 0000000000000..c3e33d1647de3 --- /dev/null +++ b/cypress/e2e/files_trashbin/files.cy.ts @@ -0,0 +1,70 @@ +/*! + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { User } from '@nextcloud/cypress' + +// @ts-expect-error package has wrong typings +import { deleteDownloadsFolderBeforeEach } from 'cypress-delete-downloads-folder' +import { deleteFileWithRequest, getRowForFileId, selectAllFiles, triggerActionForFileId } from '../files/FilesUtils.ts' + +describe('files_trashbin: download files', { testIsolation: true }, () => { + let user: User + const fileids: number[] = [] + + deleteDownloadsFolderBeforeEach() + + before(() => { + cy.createRandomUser().then(($user) => { + user = $user + + cy.uploadContent(user, new Blob(['']), 'text/plain', '/file.txt') + .then(({ headers }) => fileids.push(Number.parseInt(headers['oc-fileid']))) + .then(() => deleteFileWithRequest(user, '/file.txt')) + cy.uploadContent(user, new Blob(['']), 'text/plain', '/other-file.txt') + .then(({ headers }) => fileids.push(Number.parseInt(headers['oc-fileid']))) + .then(() => deleteFileWithRequest(user, '/other-file.txt')) + }) + }) + + beforeEach(() => { + cy.login(user) + cy.visit('/apps/files/trashbin') + }) + + it('can download file', () => { + getRowForFileId(fileids[0]).should('be.visible') + getRowForFileId(fileids[1]).should('be.visible') + + triggerActionForFileId(fileids[0], 'download') + + const downloadsFolder = Cypress.config('downloadsFolder') + cy.readFile(`${downloadsFolder}/file.txt`, { timeout: 15000 }) + .should('exist') + .and('have.length.gt', 8) + .and('equal', '') + }) + + it('can download a file using default action', () => { + getRowForFileId(fileids[0]) + .should('be.visible') + .findByRole('button', { name: 'Download' }) + .click({ force: true }) + + const downloadsFolder = Cypress.config('downloadsFolder') + cy.readFile(`${downloadsFolder}/file.txt`, { timeout: 15000 }) + .should('exist') + .and('have.length.gt', 8) + .and('equal', '') + }) + + // TODO: Fix this as this dependens on the webdav zip folder plugin not working for trashbin (and never worked with old NC legacy download ajax as well) + it('does not offer bulk download', () => { + cy.get('[data-cy-files-list-row-checkbox]').should('have.length', 2) + selectAllFiles() + cy.get('.files-list__selected').should('have.text', '2 selected') + cy.get('[data-cy-files-list-selection-action="restore"]').should('be.visible') + cy.get('[data-cy-files-list-selection-action="download"]').should('not.exist') + }) +})