aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskjnldsv <skjnldsv@protonmail.com>2025-01-24 14:08:48 +0100
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2025-02-07 10:07:00 +0100
commit73e4a13a1c4aeb2cad2ee5488e60078706c0978d (patch)
treec2b15d475df83fd3440b050fe44fbffdec8cdbc5
parente10947a548e63965c0e5892491e0d7eb932e4c75 (diff)
downloadnextcloud-server-73e4a13a1c4aeb2cad2ee5488e60078706c0978d.tar.gz
nextcloud-server-73e4a13a1c4aeb2cad2ee5488e60078706c0978d.zip
chore(files): add actions cypress tests
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
-rw-r--r--apps/files/src/actions/moveOrCopyAction.ts2
-rw-r--r--cypress/e2e/files/FilesUtils.ts34
-rw-r--r--cypress/e2e/files/files-actions.cy.ts214
-rw-r--r--cypress/e2e/files_sharing/files-copy-move.cy.ts5
-rw-r--r--cypress/e2e/systemtags/files-bulk-action.cy.ts1
5 files changed, 244 insertions, 12 deletions
diff --git a/apps/files/src/actions/moveOrCopyAction.ts b/apps/files/src/actions/moveOrCopyAction.ts
index 667f82aba0c..724b65fa515 100644
--- a/apps/files/src/actions/moveOrCopyAction.ts
+++ b/apps/files/src/actions/moveOrCopyAction.ts
@@ -294,7 +294,7 @@ async function openFilePickerForAction(
return promise
}
-export const ACTION_COPY_MOVE = 'copy-move'
+export const ACTION_COPY_MOVE = 'move-copy'
export const action = new FileAction({
id: ACTION_COPY_MOVE,
displayName(nodes: Node[]) {
diff --git a/cypress/e2e/files/FilesUtils.ts b/cypress/e2e/files/FilesUtils.ts
index 2c7e2666d0c..e4bd807c4b6 100644
--- a/cypress/e2e/files/FilesUtils.ts
+++ b/cypress/e2e/files/FilesUtils.ts
@@ -4,6 +4,7 @@
*/
import type { User } from '@nextcloud/cypress'
+import { ACTION_COPY_MOVE } from "../../../apps/files/src/actions/moveOrCopyAction"
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)}"]`)
@@ -14,16 +15,25 @@ 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' })
+export const getActionEntryForFileId = (fileid: number, actionId: string) => {
+ return cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`)
+}
+export const getActionEntryForFile = (filename: string, actionId: string) => {
+ return cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`)
+}
+
export const triggerActionForFileId = (fileid: number, actionId: string) => {
+ // Even if it's inline, we open the action menu to get all actions visible
getActionButtonForFileId(fileid).click({ force: true })
- // Getting the last button to avoid the one from popup fading out
- cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"] > button`).last()
+ getActionEntryForFileId(fileid, actionId)
+ .find('button').last()
.should('exist').click({ force: true })
}
export const triggerActionForFile = (filename: string, actionId: string) => {
+ // Even if it's inline, we open the action menu to get all actions visible
getActionButtonForFile(filename).click({ force: true })
- // Getting the last button to avoid the one from popup fading out
- cy.get(`[data-cy-files-list-row-action="${CSS.escape(actionId)}"] > button`).last()
+ getActionEntryForFile(filename, actionId)
+ .find('button').last()
.should('exist').click({ force: true })
}
@@ -31,7 +41,7 @@ export const triggerInlineActionForFileId = (fileid: number, actionId: string) =
getActionsForFileId(fileid).find(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click()
}
export const triggerInlineActionForFile = (filename: string, actionId: string) => {
- getActionsForFile(filename).get(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click()
+ getActionsForFile(filename).find(`button[data-cy-files-list-row-action="${CSS.escape(actionId)}"]`).should('exist').click()
}
export const selectAllFiles = () => {
@@ -58,13 +68,21 @@ export const selectRowForFile = (filename: string, options: Partial<Cypress.Clic
}
+export const getSelectionActionButton = () => cy.get('[data-cy-files-list-selection-actions]').findByRole('button', { name: 'Actions' })
+export const getSelectionActionEntry = (actionId: string) => cy.get(`[data-cy-files-list-selection-action="${CSS.escape(actionId)}"]`)
export const triggerSelectionAction = (actionId: string) => {
- cy.get(`button[data-cy-files-list-selection-action="${CSS.escape(actionId)}"]`).should('exist').click()
+ // Even if it's inline, we open the action menu to get all actions visible
+ getSelectionActionButton().click({ force: true })
+ // the entry might already be a button or a button might its child
+ getSelectionActionEntry(actionId)
+ .then($el => $el.is('button') ? cy.wrap($el) : cy.wrap($el).findByRole('button').last())
+ .should('exist')
+ .click()
}
export const moveFile = (fileName: string, dirPath: string) => {
getRowForFile(fileName).should('be.visible')
- triggerActionForFile(fileName, 'move-copy')
+ triggerActionForFile(fileName, ACTION_COPY_MOVE)
cy.get('.file-picker').within(() => {
// intercept the copy so we can wait for it
@@ -95,7 +113,7 @@ export const moveFile = (fileName: string, dirPath: string) => {
export const copyFile = (fileName: string, dirPath: string) => {
getRowForFile(fileName).should('be.visible')
- triggerActionForFile(fileName, 'move-copy')
+ triggerActionForFile(fileName, ACTION_COPY_MOVE)
cy.get('.file-picker').within(() => {
// intercept the copy so we can wait for it
diff --git a/cypress/e2e/files/files-actions.cy.ts b/cypress/e2e/files/files-actions.cy.ts
new file mode 100644
index 00000000000..8b0057e3042
--- /dev/null
+++ b/cypress/e2e/files/files-actions.cy.ts
@@ -0,0 +1,214 @@
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import type { User } from '@nextcloud/cypress'
+import { FileAction } from '@nextcloud/files'
+
+import { getActionButtonForFileId, getActionEntryForFileId, getRowForFile, getSelectionActionButton, getSelectionActionEntry, selectRowForFile, triggerActionForFile, triggerActionForFileId } from './FilesUtils'
+import { ACTION_COPY_MOVE } from '../../../apps/files/src/actions/moveOrCopyAction'
+import { ACTION_DELETE } from '../../../apps/files/src/actions/deleteAction'
+import { ACTION_DETAILS } from '../../../apps/files/src/actions/sidebarAction'
+import { ACTION_SHARING_STATUS } from '../../../apps/files_sharing/src/files_actions/sharingStatusAction'
+
+declare global {
+ interface Window {
+ _nc_fileactions: FileAction[]
+ }
+}
+
+// Those two arrays doesn't represent the full list of actions
+// the goal is to test a few, we're not trying to match the full feature set
+const expectedDefaultActionsIDs = [
+ ACTION_COPY_MOVE,
+ ACTION_DELETE,
+ ACTION_DETAILS,
+ ACTION_SHARING_STATUS,
+]
+const expectedDefaultSelectionActionsIDs = [
+ ACTION_COPY_MOVE,
+ ACTION_DELETE,
+]
+
+describe('Files: Actions', { testIsolation: true }, () => {
+ let user: User
+ let fileId: number = 0
+
+ beforeEach(() => cy.createRandomUser().then(($user) => {
+ user = $user
+
+ cy.uploadContent(user, new Blob([]), 'image/jpeg', '/image.jpg').then((response) => {
+ fileId = Number.parseInt(response.headers['oc-fileid'] ?? '0')
+ })
+ cy.login(user)
+ }))
+
+ it('Show some standard actions', () => {
+ cy.visit('/apps/files')
+ getRowForFile('image.jpg').should('be.visible')
+
+ expectedDefaultActionsIDs.forEach((actionId) => {
+ // Open the menu
+ getActionButtonForFileId(fileId).click({ force: true })
+ // Check the action is visible
+ getActionEntryForFileId(fileId, actionId).should('be.visible')
+ })
+ })
+
+ it('Show some nested actions', () => {
+ const parent = new FileAction({
+ id: 'nested-action',
+ displayName: () => 'Nested Action',
+ exec: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ })
+
+ const child1 = new FileAction({
+ id: 'nested-child-1',
+ displayName: () => 'Nested Child 1',
+ exec: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ parent: 'nested-action',
+ })
+
+ const child2 = new FileAction({
+ id: 'nested-child-2',
+ displayName: () => 'Nested Child 2',
+ exec: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ parent: 'nested-action',
+ })
+
+ cy.visit('/apps/files', {
+ // Cannot use registerFileAction here
+ onBeforeLoad: (win) => {
+ if (!win._nc_fileactions) win._nc_fileactions = []
+ // Cannot use registerFileAction here
+ win._nc_fileactions.push(parent)
+ win._nc_fileactions.push(child1)
+ win._nc_fileactions.push(child2)
+ }
+ })
+
+ // Open the menu
+ getActionButtonForFileId(fileId).click({ force: true })
+
+ // Check we have the parent action but not the children
+ getActionEntryForFileId(fileId, 'nested-action').should('be.visible')
+ getActionEntryForFileId(fileId, 'menu-back').should('not.exist')
+ getActionEntryForFileId(fileId, 'nested-child-1').should('not.exist')
+ getActionEntryForFileId(fileId, 'nested-child-2').should('not.exist')
+
+ // Click on the parent action
+ getActionEntryForFileId(fileId, 'nested-action')
+ .find('button').last()
+ .should('exist').click({ force: true })
+
+ // Check we have the children and the back button but not the parent
+ getActionEntryForFileId(fileId, 'nested-action').should('not.exist')
+ getActionEntryForFileId(fileId, 'menu-back').should('be.visible')
+ getActionEntryForFileId(fileId, 'nested-child-1').should('be.visible')
+ getActionEntryForFileId(fileId, 'nested-child-2').should('be.visible')
+
+ // Click on the back button
+ getActionEntryForFileId(fileId, 'menu-back')
+ .find('button').last()
+ .should('exist').click({ force: true })
+
+ // Check we have the parent action but not the children
+ getActionEntryForFileId(fileId, 'nested-action').should('be.visible')
+ getActionEntryForFileId(fileId, 'menu-back').should('not.exist')
+ getActionEntryForFileId(fileId, 'nested-child-1').should('not.exist')
+ getActionEntryForFileId(fileId, 'nested-child-2').should('not.exist')
+ })
+
+ it('Show some actions for a selection', () => {
+ cy.visit('/apps/files')
+ getRowForFile('image.jpg').should('be.visible')
+
+ selectRowForFile('image.jpg')
+
+ cy.get('[data-cy-files-list-selection-actions]').should('be.visible')
+ getSelectionActionButton().should('be.visible')
+
+ // Open the menu
+ getSelectionActionButton().click({ force: true })
+
+ // Check the action is visible
+ expectedDefaultSelectionActionsIDs.forEach((actionId) => {
+ getSelectionActionEntry(actionId).should('be.visible')
+ })
+ })
+
+ it('Show some nested actions for a selection', () => {
+ const parent = new FileAction({
+ id: 'nested-action',
+ displayName: () => 'Nested Action',
+ exec: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ })
+
+ const child1 = new FileAction({
+ id: 'nested-child-1',
+ displayName: () => 'Nested Child 1',
+ exec: cy.spy(),
+ execBatch: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ parent: 'nested-action',
+ })
+
+ const child2 = new FileAction({
+ id: 'nested-child-2',
+ displayName: () => 'Nested Child 2',
+ exec: cy.spy(),
+ execBatch: cy.spy(),
+ iconSvgInline: () => '<svg></svg>',
+ parent: 'nested-action',
+ })
+
+ cy.visit('/apps/files', {
+ // Cannot use registerFileAction here
+ onBeforeLoad: (win) => {
+ if (!win._nc_fileactions) win._nc_fileactions = []
+ // Cannot use registerFileAction here
+ win._nc_fileactions.push(parent)
+ win._nc_fileactions.push(child1)
+ win._nc_fileactions.push(child2)
+ }
+ })
+
+ selectRowForFile('image.jpg')
+
+ // Open the menu
+ getSelectionActionButton().click({ force: true })
+
+ // Check we have the parent action but not the children
+ getSelectionActionEntry('nested-action').should('be.visible')
+ getSelectionActionEntry('menu-back').should('not.exist')
+ getSelectionActionEntry('nested-child-1').should('not.exist')
+ getSelectionActionEntry('nested-child-2').should('not.exist')
+
+ // Click on the parent action
+ getSelectionActionEntry('nested-action')
+ .find('button').last()
+ .should('exist').click({ force: true })
+
+ // Check we have the children and the back button but not the parent
+ getSelectionActionEntry('nested-action').should('not.exist')
+ getSelectionActionEntry('menu-back').should('be.visible')
+ getSelectionActionEntry('nested-child-1').should('be.visible')
+ getSelectionActionEntry('nested-child-2').should('be.visible')
+
+ // Click on the back button
+ getSelectionActionEntry('menu-back')
+ .find('button').last()
+ .should('exist').click({ force: true })
+
+ // Check we have the parent action but not the children
+ getSelectionActionEntry('nested-action').should('be.visible')
+ getSelectionActionEntry('menu-back').should('not.exist')
+ getSelectionActionEntry('nested-child-1').should('not.exist')
+ getSelectionActionEntry('nested-child-2').should('not.exist')
+ })
+})
diff --git a/cypress/e2e/files_sharing/files-copy-move.cy.ts b/cypress/e2e/files_sharing/files-copy-move.cy.ts
index fc8a04c3ab1..6ad01cb2219 100644
--- a/cypress/e2e/files_sharing/files-copy-move.cy.ts
+++ b/cypress/e2e/files_sharing/files-copy-move.cy.ts
@@ -10,10 +10,11 @@ import {
navigateToFolder,
triggerActionForFile,
} from '../files/FilesUtils.ts'
+import { ACTION_COPY_MOVE } from '../../../apps/files/src/actions/moveOrCopyAction.ts'
export const copyFileForbidden = (fileName: string, dirPath: string) => {
getRowForFile(fileName).should('be.visible')
- triggerActionForFile(fileName, 'move-copy')
+ triggerActionForFile(fileName, ACTION_COPY_MOVE)
cy.get('.file-picker').within(() => {
// intercept the copy so we can wait for it
@@ -32,7 +33,7 @@ export const copyFileForbidden = (fileName: string, dirPath: string) => {
export const moveFileForbidden = (fileName: string, dirPath: string) => {
getRowForFile(fileName).should('be.visible')
- triggerActionForFile(fileName, 'move-copy')
+ triggerActionForFile(fileName, ACTION_COPY_MOVE)
cy.get('.file-picker').within(() => {
// intercept the copy so we can wait for it
diff --git a/cypress/e2e/systemtags/files-bulk-action.cy.ts b/cypress/e2e/systemtags/files-bulk-action.cy.ts
index 9614b445f9b..7e70e1d2836 100644
--- a/cypress/e2e/systemtags/files-bulk-action.cy.ts
+++ b/cypress/e2e/systemtags/files-bulk-action.cy.ts
@@ -57,7 +57,6 @@ function triggerTagManagementDialogAction() {
}
describe('Systemtags: Files bulk action', { testIsolation: false }, () => {
- let snapshot: string
let user1: User
let user2: User