diff options
author | skjnldsv <skjnldsv@protonmail.com> | 2025-01-24 14:08:48 +0100 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2025-02-07 10:07:00 +0100 |
commit | 73e4a13a1c4aeb2cad2ee5488e60078706c0978d (patch) | |
tree | c2b15d475df83fd3440b050fe44fbffdec8cdbc5 | |
parent | e10947a548e63965c0e5892491e0d7eb932e4c75 (diff) | |
download | nextcloud-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.ts | 2 | ||||
-rw-r--r-- | cypress/e2e/files/FilesUtils.ts | 34 | ||||
-rw-r--r-- | cypress/e2e/files/files-actions.cy.ts | 214 | ||||
-rw-r--r-- | cypress/e2e/files_sharing/files-copy-move.cy.ts | 5 | ||||
-rw-r--r-- | cypress/e2e/systemtags/files-bulk-action.cy.ts | 1 |
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 |