nextcloud/tests/playwright/support/sections/CopyMoveDialogPage.ts
Peter Ringelmann 0884927470 test(files): migrate copy/move e2e from Cypress to Playwright
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
2026-06-16 11:58:16 +02:00

96 lines
3.4 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Locator, Page } from '@playwright/test'
import { escapeAttributeValue } from '../utils/css.ts'
/**
* The file-picker dialog opened by the files "Move or copy" action
* (the NcDialog-based FilePicker from @nextcloud/dialogs).
*
* The confirm buttons carry the visible label the user sees: "Copy" / "Move"
* for the current folder, "Copy to <name>" / "Move to <name>" once a
* destination is selected. Navigation and confirmation each own their own
* DAV wait so callers don't have to repeat the register-before/await-after dance.
*/
export class CopyMoveDialogPage {
constructor(private readonly page: Page) {}
/** The open file-picker dialog. */
dialog(): Locator {
return this.page.getByRole('dialog')
}
/**
* A destination row inside the picker. The picker tags each row with the
* library-owned `data-filename`; rows have no per-folder accessible name to
* navigate by (their role name is a generic "Select the row for …").
*/
getDestination(name: string): Locator {
return this.dialog().locator(`[data-filename="${escapeAttributeValue(name)}"]`)
}
/**
* The breadcrumb path. The picker has two navs — the left view list (labelled
* like the dialog, holding the All files / Recent / Favorites shortcuts) and
* the breadcrumb. Only the breadcrumb changes the destination folder, so
* select it as the nav without the view shortcuts.
*/
private breadcrumbs(): Locator {
return this.dialog()
.getByRole('navigation')
.filter({ hasNot: this.page.getByRole('button', { name: 'Favorites' }) })
}
/** A confirm button by its exact visible label, e.g. "Copy" or "Move to docs". */
confirmButton(label: string): Locator {
return this.dialog().getByRole('button', { name: label, exact: true })
}
/** Navigate into a (possibly nested) folder inside the picker; returns the leaf folder name. */
async navigateTo(dirPath: string): Promise<string | undefined> {
const segments = dirPath.split('/').filter(Boolean)
for (const dir of segments) {
await this.getDestination(dir).click()
}
return segments.at(-1)
}
/** Navigate the destination back to the user's root via the breadcrumb. */
async goToAllFiles(): Promise<void> {
await this.breadcrumbs().getByRole('button', { name: 'All files' }).click()
}
private async confirm(label: string, method: 'COPY' | 'MOVE'): Promise<void> {
const done = this.page.waitForResponse(
(r) => r.request().method() === method
&& /\/(remote|public)\.php\/dav\/files\//.test(r.url()),
)
await this.confirmButton(label).click()
await done
}
/** Copy into the folder currently shown in the picker. */
async copyToCurrentFolder(): Promise<void> {
await this.confirm('Copy', 'COPY')
}
/** Move into the folder currently shown in the picker. */
async moveToCurrentFolder(): Promise<void> {
await this.confirm('Move', 'MOVE')
}
/** Navigate into the destination folder and copy there. */
async copyToFolder(dirPath: string): Promise<void> {
const target = await this.navigateTo(dirPath)
await this.confirm(`Copy to ${target}`, 'COPY')
}
/** Navigate into the destination folder and move there. */
async moveToFolder(dirPath: string): Promise<void> {
const target = await this.navigateTo(dirPath)
await this.confirm(`Move to ${target}`, 'MOVE')
}
}