Merge pull request #61152 from nextcloud/test/migrate-files-actions-playwright

test(files): migrate files actions e2e from Cypress to Playwright
This commit is contained in:
Louis 2026-06-10 17:45:27 +02:00 committed by GitHub
commit 954fc50274
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 78 additions and 74 deletions

View file

@ -1,70 +0,0 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/e2e-test-server/cypress'
import { getActionButtonForFileId, getActionEntryForFileId, getRowForFile, getSelectionActionButton, getSelectionActionEntry, selectRowForFile } from './FilesUtils.ts'
const ACTION_DELETE = 'delete'
const ACTION_COPY_MOVE = 'move-copy'
const ACTION_DETAILS = 'details'
// 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,
]
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')
// Close the menu
cy.get('body').click({ force: true })
})
})
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')
})
})
})

View file

@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { test, expect } from '../../support/fixtures/files-page.ts'
import { rm, uploadContent } from '../../support/utils/dav.ts'
// A representative subset of the default actions, not the full feature set.
const expectedRowActions = ['move-copy', 'delete', 'details']
const expectedSelectionActions = ['move-copy', 'delete']
test.describe('Files: Actions', () => {
test.beforeEach(async ({ page, user, filesListPage }) => {
// New users get welcome.txt — remove it so the list contains only our test file
await rm(page.request, user, '/welcome.txt')
await uploadContent(page.request, user, Buffer.alloc(0), 'image/jpeg', '/image.jpg')
await filesListPage.open()
})
test('shows the standard row actions', async ({ filesListPage }) => {
await expect(filesListPage.getRowForFile('image.jpg')).toBeVisible()
const menu = await filesListPage.openActionsMenuForFile('image.jpg')
for (const actionId of expectedRowActions) {
await expect(filesListPage.getActionButtonInMenu(menu, actionId)).toBeVisible()
}
})
test('shows the standard actions for a selection', async ({ filesListPage }) => {
await expect(filesListPage.getRowForFile('image.jpg')).toBeVisible()
await filesListPage.selectRowForFile('image.jpg')
await expect(filesListPage.getSelectionActionsToolbar()).toBeVisible()
await filesListPage.openSelectionActionsMenu()
for (const actionId of expectedSelectionActions) {
await expect(filesListPage.getSelectionActionEntry(actionId)).toBeVisible()
}
})
})

View file

@ -68,12 +68,45 @@ export class FilesListPage {
.click({ force: true })
}
async selectRowForFile(filename: string): Promise<void> {
// The checkbox is visually hidden inside NcCheckboxRadioSwitch, so force the check
await this.getRowForFile(filename)
.getByRole('checkbox', { name: /Toggle selection/ })
.check({ force: true })
}
/**
* The toolbar that replaces the list header once one or more rows are selected.
*/
getSelectionActionsToolbar(): Locator {
return this.page.locator('[data-cy-files-list-selection-actions]')
}
private getSelectionActionsButton(): Locator {
return this.getSelectionActionsToolbar().getByRole('button', { name: 'Actions' })
}
/**
* Open the bulk-selection actions menu. Pair with {@link getSelectionActionEntry}
* to inspect an entry (e.g. assert it is visible) before acting; for a plain
* "open and click" use {@link triggerSelectionAction}.
*/
async openSelectionActionsMenu(): Promise<void> {
await this.getSelectionActionsButton().click({ force: true })
}
/**
* A selection action entry. Matched at page level on the product-owned
* attribute because selection actions can render inline or inside the menu popover.
*/
getSelectionActionEntry(actionId: string): Locator {
return this.page.locator(`[data-cy-files-list-selection-action="${actionId}"]`)
}
async triggerSelectionAction(actionId: string): Promise<void> {
const actionsButton = this.page.locator('[data-cy-files-list-selection-actions]')
.getByRole('button', { name: 'Actions' })
await actionsButton.click({ force: true })
await this.openSelectionActionsMenu()
// NcActionButton renders as <li data-cy-...><button role="menuitem">
const actionButton = this.page.locator(`[data-cy-files-list-selection-action="${actionId}"] button`)
const actionButton = this.getSelectionActionEntry(actionId).locator('button')
await actionButton.waitFor({ state: 'visible' })
await actionButton.click()
}