From 710cb117076a33ce4004094a7256f5074e320b55 Mon Sep 17 00:00:00 2001 From: Ferdinand Thiessen Date: Mon, 22 Jun 2026 22:28:39 +0200 Subject: [PATCH] chore(tests): adjust PlayWright tests to follow ESLint rules Signed-off-by: Ferdinand Thiessen --- core/src/OC/msg.ts | 1 - eslint.config.js | 13 +++++- package.json | 2 +- .../e2e/appstore/admin-settings-apps.spec.ts | 38 ++++++++---------- tests/playwright/e2e/core/404-error.spec.ts | 2 +- .../e2e/core/header-access-levels.spec.ts | 2 +- .../e2e/core/header-app-menu.spec.ts | 4 +- .../e2e/core/header-contacts-menu.spec.ts | 1 + tests/playwright/e2e/dav/availability.spec.ts | 14 ++----- .../files/duplicated-node-regression.spec.ts | 6 +-- .../e2e/files/files-actions.spec.ts | 2 +- .../e2e/files/files-copy-move.spec.ts | 2 +- .../playwright/e2e/files/files-delete.spec.ts | 14 +++---- .../e2e/files/files-download.spec.ts | 7 ++-- .../e2e/files/files-favorites.spec.ts | 9 ++--- .../e2e/files/files-navigation.spec.ts | 2 +- .../e2e/files/files-renaming.spec.ts | 20 ++++------ .../e2e/files/files-sidebar.spec.ts | 8 ++-- .../e2e/files/files-xml-regression.spec.ts | 10 ++--- .../playwright/e2e/files/recent-view.spec.ts | 6 +-- .../e2e/files_sharing/files-copy-move.spec.ts | 7 ++-- .../e2e/login/login-redirect.spec.ts | 9 +++-- tests/playwright/e2e/login/login.spec.ts | 7 ++-- tests/playwright/e2e/login/webauth.spec.ts | 40 ++++++------------- .../admin-settings-restrictions.spec.ts | 2 +- .../e2e/systemtags/admin-settings.spec.ts | 15 ++----- .../e2e/systemtags/files-bulk-action.spec.ts | 4 +- .../systemtags/files-inline-action.spec.ts | 2 +- .../e2e/systemtags/files-sidebar.spec.ts | 2 +- .../e2e/systemtags/files-view.spec.ts | 2 +- .../e2e/theming/a11y-color-contrast.spec.ts | 2 +- .../theming/admin-settings-background.spec.ts | 8 ++-- .../theming/admin-settings-branding.spec.ts | 7 ++-- .../admin-settings-default-app.spec.ts | 4 +- .../support/fixtures/admin-appstore-page.ts | 2 +- .../support/fixtures/admin-theming-page.ts | 2 +- .../playwright/support/fixtures/files-page.ts | 3 +- .../support/fixtures/files-sharing-page.ts | 5 ++- .../support/fixtures/systemtags-files-page.ts | 2 +- tests/playwright/support/matchers.ts | 8 ++-- .../support/sections/AdminThemingPage.ts | 4 +- .../support/sections/ContactsMenuPage.ts | 8 ++-- .../support/sections/CopyMoveDialogPage.ts | 7 ++-- .../support/sections/FilesListPage.ts | 5 +-- .../sections/SystemTagsFilesListPage.ts | 16 +++----- tests/playwright/support/utils/dav.ts | 2 +- .../support/utils/password-confirmation.ts | 10 ++--- tests/playwright/support/utils/sharing.ts | 3 +- tests/playwright/support/utils/systemtags.ts | 2 +- tests/playwright/support/utils/zip.ts | 3 +- 50 files changed, 157 insertions(+), 199 deletions(-) diff --git a/core/src/OC/msg.ts b/core/src/OC/msg.ts index 1e78291702954..0bc6fa851b6eb 100644 --- a/core/src/OC/msg.ts +++ b/core/src/OC/msg.ts @@ -97,7 +97,6 @@ export default { return } - // eslint-disable-next-line @stylistic/exp-list-style const animation = el.animate?.( [ { opacity: 1 }, diff --git a/eslint.config.js b/eslint.config.js index ab6b15b605377..d30081e12102d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -73,10 +73,19 @@ export default defineConfig([ }, }, + // Playwright tests setup + { + name: 'server/playwright', + files: ['tests/playwright/**'], + rules: { + 'no-empty-pattern': 'off', // PW needs the destructuring syntax {} for fixtures! + }, + }, + // Forbid commiting .only in test files (skipping tests is very unexpected) { name: 'server/no-only-in-tests', - files: ['cypress/**', 'apps/**/*.spec.*', 'core/**/*.spec.*'], + files: ['cypress/**', 'tests/playwright/**', 'apps/**/*.spec.*', 'core/**/*.spec.*'], plugins: { 'no-only-tests': noOnlyTests, }, @@ -96,7 +105,7 @@ export default defineConfig([ 'composer.json', '**/*.php', '3rdparty/', - 'tests/', // PHP tests + 'tests/!(playwright)/', // PHP tests, but not Playwright tests '**/js/', '**/l10n/', // all translations (config only ignored in root) '**/vendor/', // different vendors diff --git a/package.json b/package.json index 55217a96da026..51aa525815629 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "postinstall": "build/demi.sh ci", "lint": "eslint --suppressions-location build/eslint-baseline.json --no-error-on-unmatched-pattern ./cypress ./tests/playwright", "postlint": "build/demi.sh lint", - "lint:fix": "build/demi.sh lint:fix", + "lint:fix": "concurrently 'npm run lint -- --fix' 'build/demi.sh lint:fix'", "playwright": "playwright test", "playwright:install": "playwright install chromium-headless-shell", "sass": "sass --style compressed --load-path core/css core/css/ $(for cssdir in $(find apps -mindepth 2 -maxdepth 2 -name \"css\"); do if ! $(git check-ignore -q $cssdir); then printf \"$cssdir \"; fi; done)", diff --git a/tests/playwright/e2e/appstore/admin-settings-apps.spec.ts b/tests/playwright/e2e/appstore/admin-settings-apps.spec.ts index 64b83208192cd..b7211fad28551 100644 --- a/tests/playwright/e2e/appstore/admin-settings-apps.spec.ts +++ b/tests/playwright/e2e/appstore/admin-settings-apps.spec.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import { runOcc } from '@nextcloud/e2e-test-server' import { expect } from '@playwright/test' import { test } from '../../support/fixtures/admin-appstore-page.ts' import { handlePasswordConfirmation } from '../../support/utils/password-confirmation.ts' -import { runOcc } from '@nextcloud/e2e-test-server' test.describe('Settings: App management', () => { - test.beforeEach(async ({ page, appstorePage }) => { + test.beforeEach(async ({ appstorePage }) => { // Disable QA testing app if already enabled expect(await runOcc(['app:disable', 'testing'])) .toMatch(/(No such app enabled|testing .+ disabled)/) @@ -19,22 +19,20 @@ test.describe('Settings: App management', () => { // Open the installed apps page await appstorePage.openInstalledApps() - + // Wait for the apps table to load await appstorePage.appsTable().waitFor({ state: 'visible', timeout: 10000 }) }) test('Can enable an installed app', async ({ page, appstorePage }) => { // Intercept the enable app request - const enableRequest = page.waitForResponse( - (response) => response.url().includes('/ocs/v2.php/apps/appstore/api/v1/apps/enable'), - ) + const enableRequest = page.waitForResponse((response) => response.url().includes('/ocs/v2.php/apps/appstore/api/v1/apps/enable')) // Find and click the enable button for the QA testing app await expect(appstorePage.appsTable()).toBeVisible() const qaTestingRow = appstorePage.appRow('QA testing') await expect(qaTestingRow).toBeVisible({ timeout: 10000 }) - + await appstorePage.enableButton('QA testing').click({ force: true }) // Handle password confirmation if needed @@ -57,15 +55,13 @@ test.describe('Settings: App management', () => { test('Can disable an installed app', async ({ page, appstorePage }) => { // Intercept the disable app request - const disableRequest = page.waitForResponse( - (response) => response.url().includes('/ocs/v2.php/apps/appstore/api/v1/apps/disable'), - ) + const disableRequest = page.waitForResponse((response) => response.url().includes('/ocs/v2.php/apps/appstore/api/v1/apps/disable')) // Find and click the disable button for the Update notification app await expect(appstorePage.appsTable()).toBeVisible() const updateRow = appstorePage.appRow('Update notification') await expect(updateRow).toBeVisible({ timeout: 10000 }) - + await appstorePage.disableButton('Update notification').click({ force: true }) // Handle password confirmation if needed @@ -95,15 +91,15 @@ test.describe('Settings: App management', () => { // Verify that there are only enabled apps (all have "Disable" button, no "Enable" button) await expect(appstorePage.appsTable()).toBeVisible() - + // Get all rows and verify each has a disable button and no enable button const rows = appstorePage.appsTable().locator('tr') const rowCount = await rows.count() - - for (let i = 1; i < rowCount; i++) { // Skip header row + + for (let i = 1; i < rowCount; i++) { // Skip header row const row = rows.nth(i) const enableButton = row.getByRole('button', { name: 'Enable' }) - + // Enabled apps should not have an "Enable" button await expect(enableButton).not.toBeVisible() } @@ -118,15 +114,15 @@ test.describe('Settings: App management', () => { // Verify that there are only disabled apps (all have "Enable" button, no "Disable" button) await expect(appstorePage.appsTable()).toBeVisible() - + // Get all rows and verify each has an enable button and no disable button const rows = appstorePage.appsTable().locator('tr') const rowCount = await rows.count() - - for (let i = 1; i < rowCount; i++) { // Skip header row + + for (let i = 1; i < rowCount; i++) { // Skip header row const row = rows.nth(i) const disableButton = row.getByRole('button', { name: 'Disable' }) - + // Disabled apps should not have a "Disable" button await expect(disableButton).not.toBeVisible() } @@ -152,12 +148,12 @@ test.describe('Settings: App management', () => { const sidebar = appstorePage.appSidebar() await expect(sidebar).toBeVisible() await expect(appstorePage.appSidebarHeader()).toContainText('QA testing') - + // Verify the sidebar contains expected elements await expect(appstorePage.viewInStoreLink()).toBeVisible() await expect(appstorePage.appSidebarEnableButton()).toBeVisible() await expect(appstorePage.removeButton()).toBeVisible() - + // Verify version information is displayed await expect(appstorePage.versionText()).toBeVisible() }) diff --git a/tests/playwright/e2e/core/404-error.spec.ts b/tests/playwright/e2e/core/404-error.spec.ts index 9d345e76c59cf..18a5b4a22294c 100644 --- a/tests/playwright/e2e/core/404-error.spec.ts +++ b/tests/playwright/e2e/core/404-error.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '@playwright/test' +import { expect, test } from '@playwright/test' test.describe('404 error page', () => { test('renders 404 page with a link back to login', async ({ page }) => { diff --git a/tests/playwright/e2e/core/header-access-levels.spec.ts b/tests/playwright/e2e/core/header-access-levels.spec.ts index daf03bda43f9f..44bfee8543cbb 100644 --- a/tests/playwright/e2e/core/header-access-levels.spec.ts +++ b/tests/playwright/e2e/core/header-access-levels.spec.ts @@ -4,8 +4,8 @@ */ import { expect } from '@playwright/test' -import { test as userTest } from '../../support/fixtures/random-user-session.ts' import { test as adminTest } from '../../support/fixtures/admin-session.ts' +import { test as userTest } from '../../support/fixtures/random-user-session.ts' import { AccountMenuPage } from '../../support/sections/AccountMenuPage.ts' // Regular user tests — the page fixture is logged in as a fresh random user. diff --git a/tests/playwright/e2e/core/header-app-menu.spec.ts b/tests/playwright/e2e/core/header-app-menu.spec.ts index 11902eb0e8917..13794269581b2 100644 --- a/tests/playwright/e2e/core/header-app-menu.spec.ts +++ b/tests/playwright/e2e/core/header-app-menu.spec.ts @@ -4,8 +4,8 @@ */ import { expect } from '@playwright/test' -import { test as userTest } from '../../support/fixtures/random-user-session.ts' import { test as adminTest } from '../../support/fixtures/admin-session.ts' +import { test as userTest } from '../../support/fixtures/random-user-session.ts' import { NavigationHeaderPage } from '../../support/sections/NavigationHeaderPage.ts' // Regular-user tests — logged in as a fresh random user. @@ -65,7 +65,7 @@ adminTest.describe('Header: App menu (waffle launcher) – admin', () => { */ async function expectWaffleMenuContainsApps( navigationHeader: NavigationHeaderPage, - apps: Array<{ name: string; href: string }>, + apps: Array<{ name: string, href: string }>, ): Promise { await navigationHeader.openMenu() await expect(navigationHeader.popover()).toBeVisible() diff --git a/tests/playwright/e2e/core/header-contacts-menu.spec.ts b/tests/playwright/e2e/core/header-contacts-menu.spec.ts index 04eedcb9492eb..d951c76fa43db 100644 --- a/tests/playwright/e2e/core/header-contacts-menu.spec.ts +++ b/tests/playwright/e2e/core/header-contacts-menu.spec.ts @@ -4,6 +4,7 @@ */ import type { User } from '@nextcloud/e2e-test-server' + import { runOcc } from '@nextcloud/e2e-test-server/docker' import { createRandomUser } from '@nextcloud/e2e-test-server/playwright' import { expect } from '@playwright/test' diff --git a/tests/playwright/e2e/dav/availability.spec.ts b/tests/playwright/e2e/dav/availability.spec.ts index 8d44e609c06d5..82bbf221604ed 100644 --- a/tests/playwright/e2e/dav/availability.spec.ts +++ b/tests/playwright/e2e/dav/availability.spec.ts @@ -3,9 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect } from '@playwright/test' import { User } from '@nextcloud/e2e-test-server' import { addUser, runOcc } from '@nextcloud/e2e-test-server/docker' +import { expect } from '@playwright/test' import { test } from '../../support/fixtures/random-user-session.ts' test.describe('Calendar: Availability', () => { @@ -35,9 +35,7 @@ test.describe('Calendar: Availability', () => { await fridayItem.getByLabel('Pick a end time for Friday').fill('18:00') // Wait for the PROPPATCH save request before clicking - const saveResponse = page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/calendars/') && r.url().includes('/inbox') && r.request().method() === 'PROPPATCH', - ) + const saveResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/calendars/') && r.url().includes('/inbox') && r.request().method() === 'PROPPATCH') await page.locator('#availability').getByRole('button', { name: 'Save' }).click() await saveResponse @@ -70,9 +68,7 @@ test.describe('Calendar: Availability', () => { // Search for the replacement user via NcSelectUsers const userSearchInput = absenceSection.getByLabel('Out of office replacement (optional)') - const searchResponse = page.waitForResponse( - (r) => r.url().includes('/apps/files_sharing/api/v1/sharees') && r.url().includes('search=replacement'), - ) + const searchResponse = page.waitForResponse((r) => r.url().includes('/apps/files_sharing/api/v1/sharees') && r.url().includes('search=replacement')) await userSearchInput.click() await userSearchInput.fill('replacement') await searchResponse @@ -80,9 +76,7 @@ test.describe('Calendar: Availability', () => { await page.getByRole('option', { name: 'replacement-user' }).click() // Save and wait for the OCS POST - const saveResponse = page.waitForResponse( - (r) => r.url().includes('/apps/dav/api/v1/outOfOffice/') && r.request().method() === 'POST', - ) + const saveResponse = page.waitForResponse((r) => r.url().includes('/apps/dav/api/v1/outOfOffice/') && r.request().method() === 'POST') await absenceSection.getByRole('button', { name: 'Save' }).click() await saveResponse diff --git a/tests/playwright/e2e/files/duplicated-node-regression.spec.ts b/tests/playwright/e2e/files/duplicated-node-regression.spec.ts index 29cc27dbfa258..e412c0ea5b40e 100644 --- a/tests/playwright/e2e/files/duplicated-node-regression.spec.ts +++ b/tests/playwright/e2e/files/duplicated-node-regression.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir } from '../../support/utils/dav.ts' test.describe('Files: Duplicated node regression', () => { @@ -19,9 +19,7 @@ test.describe('Files: Duplicated node regression', () => { test('does not duplicate a node after delete and recreate', async ({ page, filesListPage }) => { await expect(filesListPage.getRowForFile('only once')).toBeVisible() - const deleted = page.waitForResponse( - (r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/'), - ) + const deleted = page.waitForResponse((r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/')) await filesListPage.triggerActionForFile('only once', 'delete') await deleted await expect(filesListPage.getRowForFile('only once')).toHaveCount(0) diff --git a/tests/playwright/e2e/files/files-actions.spec.ts b/tests/playwright/e2e/files/files-actions.spec.ts index ed26cdede8951..d761ee8f326aa 100644 --- a/tests/playwright/e2e/files/files-actions.spec.ts +++ b/tests/playwright/e2e/files/files-actions.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } 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. diff --git a/tests/playwright/e2e/files/files-copy-move.spec.ts b/tests/playwright/e2e/files/files-copy-move.spec.ts index ff571e4aece0e..5bf4636e5abac 100644 --- a/tests/playwright/e2e/files/files-copy-move.spec.ts +++ b/tests/playwright/e2e/files/files-copy-move.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, uploadContent } from '../../support/utils/dav.ts' const EMPTY = Buffer.alloc(0) diff --git a/tests/playwright/e2e/files/files-delete.spec.ts b/tests/playwright/e2e/files/files-delete.spec.ts index f62bff950efda..3aa6370b4ab43 100644 --- a/tests/playwright/e2e/files/files-delete.spec.ts +++ b/tests/playwright/e2e/files/files-delete.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, uploadContent } from '../../support/utils/dav.ts' test.describe('Files: Delete', () => { @@ -36,14 +36,10 @@ test.describe('Files: Delete', () => { await expect(page.locator('.files-list__row-icon-preview--loaded')).toHaveCount(5) // Set up listeners for all 5 DELETE responses before triggering the action - const deleteResponses = Promise.all( - Array.from({ length: 5 }, () => - page.waitForResponse( - (r) => r.url().includes(`/remote.php/dav/files/${user.userId}/root/`) && r.request().method() === 'DELETE', - { timeout: 15000 }, - ), - ), - ) + const deleteResponses = Promise.all(Array.from({ length: 5 }, () => page.waitForResponse( + (r) => r.url().includes(`/remote.php/dav/files/${user.userId}/root/`) && r.request().method() === 'DELETE', + { timeout: 15000 }, + ))) await filesListPage.selectAll() await filesListPage.triggerSelectionAction('delete') diff --git a/tests/playwright/e2e/files/files-download.spec.ts b/tests/playwright/e2e/files/files-download.spec.ts index 0847f516730e2..fb290822dc851 100644 --- a/tests/playwright/e2e/files/files-download.spec.ts +++ b/tests/playwright/e2e/files/files-download.spec.ts @@ -4,11 +4,12 @@ */ import type { Download, Page } from '@playwright/test' -import { readFile } from 'node:fs/promises' + +import { User } from '@nextcloud/e2e-test-server' import { addUser, runOcc } from '@nextcloud/e2e-test-server/docker' import { login } from '@nextcloud/e2e-test-server/playwright' -import { User } from '@nextcloud/e2e-test-server' -import { test, expect } from '../../support/fixtures/files-page.ts' +import { readFile } from 'node:fs/promises' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, uploadContent } from '../../support/utils/dav.ts' import { getZipEntries } from '../../support/utils/zip.ts' diff --git a/tests/playwright/e2e/files/files-favorites.spec.ts b/tests/playwright/e2e/files/files-favorites.spec.ts index da123222d1fac..e9c2ce5d3802d 100644 --- a/tests/playwright/e2e/files/files-favorites.spec.ts +++ b/tests/playwright/e2e/files/files-favorites.spec.ts @@ -4,7 +4,8 @@ */ import type { Page } from '@playwright/test' -import { test, expect } from '../../support/fixtures/files-page.ts' + +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, rm, uploadContent } from '../../support/utils/dav.ts' /** @@ -14,10 +15,8 @@ import { mkdir, rm, uploadContent } from '../../support/utils/dav.ts' */ async function toggleFavorite(page: Page, path: string, action: () => Promise): Promise { const encoded = path.split('/').map(encodeURIComponent).join('/') - const response = page.waitForResponse( - (r) => r.url().includes(`/apps/files/api/v1/files/${encoded}`) - && r.request().method() === 'POST', - ) + const response = page.waitForResponse((r) => r.url().includes(`/apps/files/api/v1/files/${encoded}`) + && r.request().method() === 'POST') await action() await response } diff --git a/tests/playwright/e2e/files/files-navigation.spec.ts b/tests/playwright/e2e/files/files-navigation.spec.ts index 6a867404951bd..b2114d00cd728 100644 --- a/tests/playwright/e2e/files/files-navigation.spec.ts +++ b/tests/playwright/e2e/files/files-navigation.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir } from '../../support/utils/dav.ts' test.describe('Files: Navigation', () => { diff --git a/tests/playwright/e2e/files/files-renaming.spec.ts b/tests/playwright/e2e/files/files-renaming.spec.ts index ffe9d1ef82a18..e7260381757db 100644 --- a/tests/playwright/e2e/files/files-renaming.spec.ts +++ b/tests/playwright/e2e/files/files-renaming.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, rm, uploadContent } from '../../support/utils/dav.ts' test.describe('Files: Rename nodes', () => { @@ -40,9 +40,7 @@ test.describe('Files: Rename nodes', () => { const input = filesListPage.getRenameInputForFile('file.txt') await expect(input).toBeVisible() - const { selectionStart, selectionEnd } = await input.evaluate( - (el) => ({ selectionStart: (el as HTMLInputElement).selectionStart, selectionEnd: (el as HTMLInputElement).selectionEnd }), - ) + const { selectionStart, selectionEnd } = await input.evaluate((el) => ({ selectionStart: (el as HTMLInputElement).selectionStart, selectionEnd: (el as HTMLInputElement).selectionEnd })) expect(selectionStart).toBe(0) expect(selectionEnd).toBe('file'.length) }) @@ -64,7 +62,9 @@ test.describe('Files: Rename nodes', () => { // Hold MOVE requests until we explicitly release them let resolveMove!: () => void - const moveAllowed = new Promise(resolve => { resolveMove = resolve }) + const moveAllowed = new Promise((resolve) => { + resolveMove = resolve + }) await page.route(/remote\.php\/dav\/files\//, async (route) => { if (route.request().method() === 'MOVE') { await moveAllowed @@ -83,9 +83,7 @@ test.describe('Files: Rename nodes', () => { await expect(loadingRow.getByRole('checkbox', { name: /Toggle selection/ })).not.toBeVisible() // Release the MOVE and wait for it to complete - const moveResponse = page.waitForResponse( - r => r.url().includes('/remote.php/dav/files/') && r.request().method() === 'MOVE', - ) + const moveResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/files/') && r.request().method() === 'MOVE') resolveMove() await moveResponse await page.unroute(/remote\.php\/dav\/files\//) @@ -159,9 +157,7 @@ test.describe('Files: Rename nodes', () => { // Rename to 'zzz.txt' — sorts last, scrolls out of the visible area await filesListPage.triggerActionForFile('file.txt', 'rename') const input = filesListPage.getRenameInputForFile('file.txt') - const moveResponse = page.waitForResponse( - r => r.url().includes('/remote.php/dav/files/') && r.request().method() === 'MOVE', - ) + const moveResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/files/') && r.request().method() === 'MOVE') await input.fill('zzz.txt') await input.press('Enter') await moveResponse @@ -170,7 +166,7 @@ test.describe('Files: Rename nodes', () => { await expect(filesListPage.getRowForFile('zzz.txt')).toHaveCount(0) // Scroll to the bottom to bring zzz.txt into view - await page.locator('[data-cy-files-list]').evaluate(el => el.scrollTo(0, el.scrollHeight)) + await page.locator('[data-cy-files-list]').evaluate((el) => el.scrollTo(0, el.scrollHeight)) // Row must be visible and NOT in rename state await expect(filesListPage.getRowForFile('zzz.txt')).toBeVisible() diff --git a/tests/playwright/e2e/files/files-sidebar.spec.ts b/tests/playwright/e2e/files/files-sidebar.spec.ts index e94f57c0b8957..5e82f3959df21 100644 --- a/tests/playwright/e2e/files/files-sidebar.spec.ts +++ b/tests/playwright/e2e/files/files-sidebar.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { mkdir, uploadContent } from '../../support/utils/dav.ts' test.describe('Files: Sidebar', () => { @@ -70,8 +70,7 @@ test.describe('Files: Sidebar', () => { await expect(filesSidebar.heading('file')).toBeVisible() const deleteResponse = page.waitForResponse( - (response) => - response.url().includes(`/remote.php/dav/files/${user.userId}/file`) + (response) => response.url().includes(`/remote.php/dav/files/${user.userId}/file`) && response.request().method() === 'DELETE', { timeout: 10000 }, ) @@ -97,8 +96,7 @@ test.describe('Files: Sidebar', () => { await expect(filesSidebar.heading('other')).toBeVisible() const deleteResponse = page.waitForResponse( - (response) => - response.url().includes(`/remote.php/dav/files/${user.userId}/folder/other`) + (response) => response.url().includes(`/remote.php/dav/files/${user.userId}/folder/other`) && response.request().method() === 'DELETE', { timeout: 10000 }, ) diff --git a/tests/playwright/e2e/files/files-xml-regression.spec.ts b/tests/playwright/e2e/files/files-xml-regression.spec.ts index 4682dee1d16bc..61c16f8506027 100644 --- a/tests/playwright/e2e/files/files-xml-regression.spec.ts +++ b/tests/playwright/e2e/files/files-xml-regression.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { uploadContent } from '../../support/utils/dav.ts' /** @@ -20,9 +20,7 @@ test.describe('Files: XML entities in file names', () => { const input = filesListPage.getRenameInputForFile('and.txt') await expect(input).toBeVisible() - const renamed = page.waitForResponse( - (r) => r.request().method() === 'MOVE' && r.url().includes('/remote.php/dav/files/'), - ) + const renamed = page.waitForResponse((r) => r.request().method() === 'MOVE' && r.url().includes('/remote.php/dav/files/')) await input.fill('&.txt') await input.press('Enter') await renamed @@ -42,9 +40,7 @@ test.describe('Files: XML entities in file names', () => { await expect(filesListPage.getRowForFile('&.txt')).toBeVisible() - const deleted = page.waitForResponse( - (r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/'), - ) + const deleted = page.waitForResponse((r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/')) await filesListPage.triggerActionForFile('&.txt', 'delete') await deleted diff --git a/tests/playwright/e2e/files/recent-view.spec.ts b/tests/playwright/e2e/files/recent-view.spec.ts index 18466aa079759..e6e069519ced6 100644 --- a/tests/playwright/e2e/files/recent-view.spec.ts +++ b/tests/playwright/e2e/files/recent-view.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/files-page.ts' +import { expect, test } from '../../support/fixtures/files-page.ts' import { uploadContent } from '../../support/utils/dav.ts' test.describe('Files: Recent view', () => { @@ -25,9 +25,7 @@ test.describe('Files: Recent view', () => { await filesListPage.open('recent') await expect(filesListPage.getRowForFile('file.txt')).toBeVisible() - const deleted = page.waitForResponse( - (r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/'), - ) + const deleted = page.waitForResponse((r) => r.request().method() === 'DELETE' && r.url().includes('/remote.php/dav/files/')) await filesListPage.triggerActionForFile('file.txt', 'delete') await deleted diff --git a/tests/playwright/e2e/files_sharing/files-copy-move.spec.ts b/tests/playwright/e2e/files_sharing/files-copy-move.spec.ts index 0cefb48c4f45d..351ae937cd07b 100644 --- a/tests/playwright/e2e/files_sharing/files-copy-move.spec.ts +++ b/tests/playwright/e2e/files_sharing/files-copy-move.spec.ts @@ -3,11 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { APIRequestContext } from '@playwright/test' import type { User } from '@nextcloud/e2e-test-server' -import { test, expect } from '../../support/fixtures/files-sharing-page.ts' +import type { APIRequestContext } from '@playwright/test' + +import { expect, test } from '../../support/fixtures/files-sharing-page.ts' import { getChildPermissions, mkdir, uploadContent } from '../../support/utils/dav.ts' -import { ALL_PERMISSIONS, SharePermission, createShare } from '../../support/utils/sharing.ts' +import { ALL_PERMISSIONS, createShare, SharePermission } from '../../support/utils/sharing.ts' const EMPTY = Buffer.alloc(0) diff --git a/tests/playwright/e2e/login/login-redirect.spec.ts b/tests/playwright/e2e/login/login-redirect.spec.ts index eb493a70c4447..51b3afe59cf84 100644 --- a/tests/playwright/e2e/login/login-redirect.spec.ts +++ b/tests/playwright/e2e/login/login-redirect.spec.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect, test } from '@playwright/test' -import { User } from '@nextcloud/e2e-test-server' -import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import type { User } from '@nextcloud/e2e-test-server' + import { runOcc } from '@nextcloud/e2e-test-server/docker' +import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import { expect, test } from '@playwright/test' import { LoginPage } from '../../support/sections/LoginPage.ts' test.describe('Login: Redirect', () => { @@ -40,7 +41,7 @@ test.describe('Login: Redirect', () => { test('redirect_url parameter redirects to the original page after login', async ({ page }) => { const redirectTarget = 'settings/user#profile' await page.goto(redirectTarget) - await expect(page).toHaveURL(new RegExp(`/login\\?redirect_url=(\/index.php\/)?${redirectTarget}`)) + await expect(page).toHaveURL(new RegExp(`/login\\?redirect_url=(/index.php/)?${redirectTarget}`)) const loginPage = new LoginPage(page) await expect(loginPage.usernameInput()).toBeVisible() diff --git a/tests/playwright/e2e/login/login.spec.ts b/tests/playwright/e2e/login/login.spec.ts index ed95febabc57e..6101e80f2e009 100644 --- a/tests/playwright/e2e/login/login.spec.ts +++ b/tests/playwright/e2e/login/login.spec.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect, test as baseTest } from '@playwright/test' -import { User } from '@nextcloud/e2e-test-server' -import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import type { User } from '@nextcloud/e2e-test-server' + import { runOcc } from '@nextcloud/e2e-test-server/docker' +import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import { test as baseTest, expect } from '@playwright/test' import { AccountMenuPage } from '../../support/sections/AccountMenuPage.ts' import { LoginPage } from '../../support/sections/LoginPage.ts' diff --git a/tests/playwright/e2e/login/webauth.spec.ts b/tests/playwright/e2e/login/webauth.spec.ts index ff16027bc441d..7b3267aec761c 100644 --- a/tests/playwright/e2e/login/webauth.spec.ts +++ b/tests/playwright/e2e/login/webauth.spec.ts @@ -3,10 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect, test, type BrowserContext } from '@playwright/test' -import { User } from '@nextcloud/e2e-test-server' -import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import type { User } from '@nextcloud/e2e-test-server' +import type { BrowserContext } from '@playwright/test' + import { runOcc } from '@nextcloud/e2e-test-server/docker' +import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' +import { expect, test } from '@playwright/test' import { handlePasswordConfirmation } from '../../support/utils/password-confirmation.ts' test.describe('Login: WebAuthn', () => { @@ -41,15 +43,11 @@ test.describe('Login: WebAuthn', () => { }) test('add and delete a WebAuthn device', async ({ page }) => { - const registrationChallenge = page.waitForResponse( - (r) => r.url().includes('/settings/api/personal/webauthn/registration'), - ) + const registrationChallenge = page.waitForResponse((r) => r.url().includes('/settings/api/personal/webauthn/registration')) await page.goto('/settings/user/security') const securitySection = page.locator('#security-webauthn') - await expect( - securitySection.getByRole('note').filter({ hasText: /No devices configured/i }), - ).toBeVisible() + await expect(securitySection.getByRole('note').filter({ hasText: /No devices configured/i })).toBeVisible() await page.getByRole('button', { name: /Add WebAuthn device/i }).click() await handlePasswordConfirmation(page, user.password) @@ -58,9 +56,7 @@ test.describe('Login: WebAuthn', () => { const deviceNameInput = page.getByLabel('Device name') await expect(deviceNameInput).toBeVisible() - const registrationComplete = page.waitForResponse( - (r) => r.url().includes('/settings/api/personal/webauthn/registration'), - ) + const registrationComplete = page.waitForResponse((r) => r.url().includes('/settings/api/personal/webauthn/registration')) await deviceNameInput.fill('test device') await deviceNameInput.press('Enter') await registrationComplete @@ -75,30 +71,22 @@ test.describe('Login: WebAuthn', () => { await page.getByRole('menuitem', { name: 'Delete' }).click() await handlePasswordConfirmation(page, user.password) - await expect( - securitySection.getByRole('note').filter({ hasText: /No devices configured/i }), - ).toBeVisible() + await expect(securitySection.getByRole('note').filter({ hasText: /No devices configured/i })).toBeVisible() await expect(deviceList).toHaveCount(0) await page.reload() - await expect( - securitySection.getByRole('note').filter({ hasText: /No devices configured/i }), - ).toBeVisible() + await expect(securitySection.getByRole('note').filter({ hasText: /No devices configured/i })).toBeVisible() }) test('add a WebAuthn device and use it to log in', async ({ page, context }) => { - const registrationChallenge = page.waitForResponse( - (r) => r.url().includes('/settings/api/personal/webauthn/registration') && r.request().method() === 'GET', - ) + const registrationChallenge = page.waitForResponse((r) => r.url().includes('/settings/api/personal/webauthn/registration') && r.request().method() === 'GET') await page.goto('/settings/user/security') await page.getByRole('button', { name: /Add WebAuthn device/i }).click() await handlePasswordConfirmation(page, user.password) await registrationChallenge - const registrationComplete = page.waitForResponse( - (r) => r.url().includes('/settings/api/personal/webauthn/registration') && r.request().method() === 'POST', - ) + const registrationComplete = page.waitForResponse((r) => r.url().includes('/settings/api/personal/webauthn/registration') && r.request().method() === 'POST') const deviceNameInput = page.getByLabel('Device name') await deviceNameInput.fill('test device') await deviceNameInput.press('Enter') @@ -119,9 +107,7 @@ test.describe('Login: WebAuthn', () => { await passwordlessForm.getByLabel('Login or email').fill(user.userId) - const webauthnLogin = page.waitForResponse( - (r) => r.url().includes('/login/webauthn/start') && r.request().method() === 'POST', - ) + const webauthnLogin = page.waitForResponse((r) => r.url().includes('/login/webauthn/start') && r.request().method() === 'POST') await page.getByRole('button', { name: 'Log in' }).click() await webauthnLogin diff --git a/tests/playwright/e2e/systemtags/admin-settings-restrictions.spec.ts b/tests/playwright/e2e/systemtags/admin-settings-restrictions.spec.ts index 4e3a2c62d477d..0cc1eea013b3b 100644 --- a/tests/playwright/e2e/systemtags/admin-settings-restrictions.spec.ts +++ b/tests/playwright/e2e/systemtags/admin-settings-restrictions.spec.ts @@ -45,4 +45,4 @@ test('Cannot create tag if restriction is in place', async ({ filesListPage }) = await picker.getByRole('checkbox', { name: tag }).click({ force: true }) await filesListPage.applyTagPicker() await filesListPage.expectInlineTagsForFile('file1.txt', [tag]) -}) \ No newline at end of file +}) diff --git a/tests/playwright/e2e/systemtags/admin-settings.spec.ts b/tests/playwright/e2e/systemtags/admin-settings.spec.ts index a2bac5db80066..cf796fe127002 100644 --- a/tests/playwright/e2e/systemtags/admin-settings.spec.ts +++ b/tests/playwright/e2e/systemtags/admin-settings.spec.ts @@ -4,7 +4,6 @@ */ import { expect } from '@playwright/test' -import { runOcc } from '@nextcloud/e2e-test-server/docker' import { test } from '../../support/fixtures/admin-session.ts' import { createTag, deleteTag, listTags } from '../../support/utils/systemtags.ts' @@ -29,9 +28,7 @@ test.describe('System tags admin settings', () => { await expect(tagNameInput).toHaveValue('') // Create the tag and intercept the DAV POST - const createResponse = page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags') && r.request().method() === 'POST', - ) + const createResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags') && r.request().method() === 'POST') await tagNameInput.fill(tagName) await page.getByRole('button', { name: 'Create' }).click() expect((await createResponse).status()).toBe(201) @@ -42,7 +39,7 @@ test.describe('System tags admin settings', () => { }) test('Can update a tag', async ({ page }) => { - const tag = await createTag(tagName) + await createTag(tagName) await page.goto('settings/admin/server') await page.getByRole('heading', { name: 'Collaborative tags' }).scrollIntoViewIfNeeded() @@ -63,9 +60,7 @@ test.describe('System tags admin settings', () => { await page.locator('#system-tag-level').click() await page.getByRole('option', { name: 'Invisible' }).click() - const updateResponse = page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPPATCH', - ) + const updateResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPPATCH') await page.getByRole('button', { name: 'Update' }).click() expect((await updateResponse).status()).toBe(207) @@ -89,9 +84,7 @@ test.describe('System tags admin settings', () => { // Verify the form reflects the selected tag await expect(page.getByLabel('Tag name')).toHaveValue(tagName) - const deleteResponse = page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'DELETE', - ) + const deleteResponse = page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'DELETE') await page.locator('.system-tag-form__row').getByRole('button', { name: 'Delete' }).click() expect((await deleteResponse).status()).toBe(204) diff --git a/tests/playwright/e2e/systemtags/files-bulk-action.spec.ts b/tests/playwright/e2e/systemtags/files-bulk-action.spec.ts index 127b60494b930..2f44ba4b63b85 100644 --- a/tests/playwright/e2e/systemtags/files-bulk-action.spec.ts +++ b/tests/playwright/e2e/systemtags/files-bulk-action.spec.ts @@ -20,10 +20,8 @@ const test = baseTest.extend<{ fileIds: [string, string] }>({ test.describe('Systemtags: Files bulk action', () => { test.afterAll(async () => await clearTags()) - test.beforeEach(async ({ fileIds }) => { - console.debug('Created files with IDs', fileIds) - }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- Needed to execute the upload by PlayWright test('Can assign tag to selection', async ({ filesListPage, fileIds }) => { const tag = crypto.randomUUID() diff --git a/tests/playwright/e2e/systemtags/files-inline-action.spec.ts b/tests/playwright/e2e/systemtags/files-inline-action.spec.ts index d82568ce9db1b..a2383af3f15e7 100644 --- a/tests/playwright/e2e/systemtags/files-inline-action.spec.ts +++ b/tests/playwright/e2e/systemtags/files-inline-action.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/systemtags-files-page.ts' +import { expect, test } from '../../support/fixtures/systemtags-files-page.ts' import { uploadContent } from '../../support/utils/dav.ts' import { clearTags } from '../../support/utils/systemtags.ts' diff --git a/tests/playwright/e2e/systemtags/files-sidebar.spec.ts b/tests/playwright/e2e/systemtags/files-sidebar.spec.ts index cd0740f47b955..3decfaf866d0d 100644 --- a/tests/playwright/e2e/systemtags/files-sidebar.spec.ts +++ b/tests/playwright/e2e/systemtags/files-sidebar.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/systemtags-files-page.ts' +import { expect, test } from '../../support/fixtures/systemtags-files-page.ts' import { uploadContent } from '../../support/utils/dav.ts' import { clearTags } from '../../support/utils/systemtags.ts' diff --git a/tests/playwright/e2e/systemtags/files-view.spec.ts b/tests/playwright/e2e/systemtags/files-view.spec.ts index bcbd654b6cf32..dc2fe681c8da3 100644 --- a/tests/playwright/e2e/systemtags/files-view.spec.ts +++ b/tests/playwright/e2e/systemtags/files-view.spec.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test, expect } from '../../support/fixtures/systemtags-files-page.ts' +import { expect, test } from '../../support/fixtures/systemtags-files-page.ts' import { mkdir, uploadContent } from '../../support/utils/dav.ts' import { clearTags } from '../../support/utils/systemtags.ts' diff --git a/tests/playwright/e2e/theming/a11y-color-contrast.spec.ts b/tests/playwright/e2e/theming/a11y-color-contrast.spec.ts index 576ba6a96fbe4..00a861fb10f43 100644 --- a/tests/playwright/e2e/theming/a11y-color-contrast.spec.ts +++ b/tests/playwright/e2e/theming/a11y-color-contrast.spec.ts @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { resolve } from 'node:path' import { runOcc } from '@nextcloud/e2e-test-server/docker' import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' import { expect, test } from '@playwright/test' +import { resolve } from 'node:path' const themesToTest = ['light', 'dark', 'light-highcontrast', 'dark-highcontrast'] diff --git a/tests/playwright/e2e/theming/admin-settings-background.spec.ts b/tests/playwright/e2e/theming/admin-settings-background.spec.ts index 535946b6a0a31..f82092cf3514f 100644 --- a/tests/playwright/e2e/theming/admin-settings-background.spec.ts +++ b/tests/playwright/e2e/theming/admin-settings-background.spec.ts @@ -3,11 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' import { runOcc } from '@nextcloud/e2e-test-server/docker' +import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' import { expect } from '@playwright/test' -import { test } from '../../support/fixtures/admin-theming-page.ts' import { resolve } from 'node:path' +import { test } from '../../support/fixtures/admin-theming-page.ts' import { getBodyThemingSnapshot, pickColor } from '../../support/utils/theming.ts' test.describe('Admin theming background settings', () => { @@ -64,7 +64,7 @@ test.describe('Admin theming background settings', () => { } }) - test('Remove default background with custom color', async ({ adminThemingPage, page, context }) => { + test('Remove default background with custom color', async ({ adminThemingPage, page }) => { await expect(adminThemingPage.backgroundAndColorHeading()).toBeVisible() const backgroundColorButton = page.getByRole('button', { name: /Background color/ }) const selectedColor = await pickColor(page, backgroundColorButton, 2) @@ -80,7 +80,7 @@ test.describe('Admin theming background settings', () => { await expect.poll(async () => (await getBodyThemingSnapshot(page)).backgroundImage).toBe('none') }) - test('User default background reflects admin custom background and color', async ({ adminThemingPage, page, context }) => { + test('User default background reflects admin custom background and color', async ({ page, context }) => { const imagePath = resolve(process.cwd(), 'cypress/fixtures/image.jpg') await page.locator('input[type="file"][name="background"]').setInputFiles(imagePath) diff --git a/tests/playwright/e2e/theming/admin-settings-branding.spec.ts b/tests/playwright/e2e/theming/admin-settings-branding.spec.ts index fe6196bc832dc..f5d1aede68776 100644 --- a/tests/playwright/e2e/theming/admin-settings-branding.spec.ts +++ b/tests/playwright/e2e/theming/admin-settings-branding.spec.ts @@ -3,12 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { User } from '@nextcloud/e2e-test-server' +import type { Locator, Page } from '@playwright/test' + import { expect } from '@playwright/test' import { test } from '../../support/fixtures/admin-theming-page.ts' -const admin = new User('admin', 'admin') - test.describe('Admin theming branding settings', () => { test.beforeEach(async ({ adminThemingPage }) => { await adminThemingPage.reset() @@ -93,7 +92,7 @@ test.describe('Admin theming branding settings', () => { }) }) -async function setUrlFieldAndWait(page: import('@playwright/test').Page, locator: import('@playwright/test').Locator, value: string) { +async function setUrlFieldAndWait(page: Page, locator: Locator, value: string) { await locator.fill(value) await Promise.all([ page.waitForResponse((response) => response.url().includes('/apps/theming/ajax/updateStylesheet') && response.request().method() === 'POST'), diff --git a/tests/playwright/e2e/theming/admin-settings-default-app.spec.ts b/tests/playwright/e2e/theming/admin-settings-default-app.spec.ts index 30807476704fb..35777d5e0a431 100644 --- a/tests/playwright/e2e/theming/admin-settings-default-app.spec.ts +++ b/tests/playwright/e2e/theming/admin-settings-default-app.spec.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect } from '@playwright/test' import { runOcc } from '@nextcloud/e2e-test-server/docker' +import { expect } from '@playwright/test' import { test } from '../../support/fixtures/admin-theming-page.ts' import { NavigationHeaderPage } from '../../support/sections/NavigationHeaderPage.ts' @@ -14,7 +14,7 @@ test.describe('Admin theming set default apps', () => { // Otherwise, the tests would influence each other and lead to random failures (race condition when run in parallel). test.describe.configure({ mode: 'serial' }) - test.beforeEach(async ({ adminThemingPage, page, context }) => { + test.beforeEach(async ({ adminThemingPage, page }) => { await runOcc(['config:system:set', 'defaultapp', '--value', 'dashboard']) await adminThemingPage.reset() await page.goto('') diff --git a/tests/playwright/support/fixtures/admin-appstore-page.ts b/tests/playwright/support/fixtures/admin-appstore-page.ts index b4fea5fac0c7f..41f12c9017ffd 100644 --- a/tests/playwright/support/fixtures/admin-appstore-page.ts +++ b/tests/playwright/support/fixtures/admin-appstore-page.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test as adminSessionTest } from './admin-session.ts' import { AppstorePage } from '../sections/AppstorePage.ts' +import { test as adminSessionTest } from './admin-session.ts' export const test = adminSessionTest.extend<{ appstorePage: AppstorePage }>({ appstorePage: async ({ page }, use) => { diff --git a/tests/playwright/support/fixtures/admin-theming-page.ts b/tests/playwright/support/fixtures/admin-theming-page.ts index f2d232915e721..e7a59b46bf312 100644 --- a/tests/playwright/support/fixtures/admin-theming-page.ts +++ b/tests/playwright/support/fixtures/admin-theming-page.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test as adminSessionTest } from './admin-session.ts' import { AdminThemingPage } from '../sections/AdminThemingPage.ts' +import { test as adminSessionTest } from './admin-session.ts' export const test = adminSessionTest.extend<{ adminThemingPage: AdminThemingPage }>({ adminThemingPage: async ({ page }, use) => { diff --git a/tests/playwright/support/fixtures/files-page.ts b/tests/playwright/support/fixtures/files-page.ts index 46b9073c1db91..988ea8a34d00e 100644 --- a/tests/playwright/support/fixtures/files-page.ts +++ b/tests/playwright/support/fixtures/files-page.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { User } from '@nextcloud/e2e-test-server' + import { runOcc } from '@nextcloud/e2e-test-server/docker' import { createRandomUser, login } from '@nextcloud/e2e-test-server/playwright' import { test as baseTest } from '@playwright/test' -import type { User } from '@nextcloud/e2e-test-server' import { CopyMoveDialogPage } from '../sections/CopyMoveDialogPage.ts' import { FilesListPage } from '../sections/FilesListPage.ts' import { FilesNavigationPage } from '../sections/FilesNavigationPage.ts' diff --git a/tests/playwright/support/fixtures/files-sharing-page.ts b/tests/playwright/support/fixtures/files-sharing-page.ts index b41947bdbfb54..ebf2eed440ed5 100644 --- a/tests/playwright/support/fixtures/files-sharing-page.ts +++ b/tests/playwright/support/fixtures/files-sharing-page.ts @@ -3,10 +3,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { User } from '@nextcloud/e2e-test-server' +import type { APIRequestContext } from '@playwright/test' + import { runOcc } from '@nextcloud/e2e-test-server/docker' import { createRandomUser } from '@nextcloud/e2e-test-server/playwright' -import type { APIRequestContext } from '@playwright/test' -import type { User } from '@nextcloud/e2e-test-server' import { test as filesTest } from './files-page.ts' type SharingFixtures = { diff --git a/tests/playwright/support/fixtures/systemtags-files-page.ts b/tests/playwright/support/fixtures/systemtags-files-page.ts index c1afb45a4f7ae..894ea1c029989 100644 --- a/tests/playwright/support/fixtures/systemtags-files-page.ts +++ b/tests/playwright/support/fixtures/systemtags-files-page.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { test as filesTest } from './files-page.ts' import { SystemTagsFilesListPage } from '../sections/SystemTagsFilesListPage.ts' +import { test as filesTest } from './files-page.ts' type SystemTagsFixtures = { filesListPage: SystemTagsFilesListPage diff --git a/tests/playwright/support/matchers.ts b/tests/playwright/support/matchers.ts index 491cf431ec983..2eda10cf16f01 100644 --- a/tests/playwright/support/matchers.ts +++ b/tests/playwright/support/matchers.ts @@ -3,7 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect as baseExpect, type Locator } from '@playwright/test' +import type { Locator } from '@playwright/test' + +import { expect as baseExpect } from '@playwright/test' export const expect = baseExpect.extend({ /** @@ -23,8 +25,8 @@ export const expect = baseExpect.extend({ } return { message: () => pass - ? `Expected row not to have class 'files-list__row--active'` - : failMessage ?? `Expected row to have class 'files-list__row--active'`, + ? 'Expected row not to have class \'files-list__row--active\'' + : failMessage ?? 'Expected row to have class \'files-list__row--active\'', pass, } }, diff --git a/tests/playwright/support/sections/AdminThemingPage.ts b/tests/playwright/support/sections/AdminThemingPage.ts index 7e445f3fb7755..6da1e624ad465 100644 --- a/tests/playwright/support/sections/AdminThemingPage.ts +++ b/tests/playwright/support/sections/AdminThemingPage.ts @@ -23,13 +23,13 @@ export class AdminThemingPage { failOnStatusCode: true, }) const requestToken = (await tokenResponse.json()).token - + const response = await this.page.request.post('./apps/theming/ajax/undoAllChanges', { headers: { requesttoken: requestToken, }, }) - + if (!response.ok) { throw new Error(`Failed to reset theming settings (${response.status})`) } diff --git a/tests/playwright/support/sections/ContactsMenuPage.ts b/tests/playwright/support/sections/ContactsMenuPage.ts index ec5346e0c513b..f13856636f7ed 100644 --- a/tests/playwright/support/sections/ContactsMenuPage.ts +++ b/tests/playwright/support/sections/ContactsMenuPage.ts @@ -36,9 +36,7 @@ export class ContactsMenuPage { async open(): Promise { // Register waitForResponse BEFORE clicking to avoid the race condition // described in the migration context. - const loaded = this.page.waitForResponse( - (r) => r.url().includes('/contactsmenu/contacts') && r.request().method() === 'POST', - ) + const loaded = this.page.waitForResponse((r) => r.url().includes('/contactsmenu/contacts') && r.request().method() === 'POST') await this.trigger().click() await loaded } @@ -46,7 +44,9 @@ export class ContactsMenuPage { /** Close the menu. */ async close(): Promise { const isOpen = await this.trigger().getAttribute('aria-expanded') === 'true' - if (isOpen) await this.trigger().click() + if (isOpen) { + await this.trigger().click() + } await this.panel().waitFor({ state: 'hidden' }) } diff --git a/tests/playwright/support/sections/CopyMoveDialogPage.ts b/tests/playwright/support/sections/CopyMoveDialogPage.ts index f65940660c202..1b008a0225865 100644 --- a/tests/playwright/support/sections/CopyMoveDialogPage.ts +++ b/tests/playwright/support/sections/CopyMoveDialogPage.ts @@ -4,6 +4,7 @@ */ import type { Locator, Page } from '@playwright/test' + import { escapeAttributeValue } from '../utils/css.ts' /** @@ -64,10 +65,8 @@ export class CopyMoveDialogPage { } private async confirm(label: string, method: 'COPY' | 'MOVE'): Promise { - const done = this.page.waitForResponse( - (r) => r.request().method() === method - && /\/(remote|public)\.php\/dav\/files\//.test(r.url()), - ) + 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 } diff --git a/tests/playwright/support/sections/FilesListPage.ts b/tests/playwright/support/sections/FilesListPage.ts index c01eae7eb0daf..0c1eacd6cbb14 100644 --- a/tests/playwright/support/sections/FilesListPage.ts +++ b/tests/playwright/support/sections/FilesListPage.ts @@ -4,6 +4,7 @@ */ import type { Locator, Page } from '@playwright/test' + import { escapeAttributeValue } from '../utils/css.ts' export class FilesListPage { @@ -186,9 +187,7 @@ export class FilesListPage { * data-cy attributes (no stable accessible name to target by role). */ async createFolder(folderName: string): Promise { - const created = this.page.waitForResponse( - (r) => r.request().method() === 'MKCOL' && r.url().includes('/remote.php/dav/files/'), - ) + const created = this.page.waitForResponse((r) => r.request().method() === 'MKCOL' && r.url().includes('/remote.php/dav/files/')) await this.page.locator('[data-cy-upload-picker]') .getByRole('button', { name: 'New' }) diff --git a/tests/playwright/support/sections/SystemTagsFilesListPage.ts b/tests/playwright/support/sections/SystemTagsFilesListPage.ts index 4fa4a20dde133..9be745a0a92f2 100644 --- a/tests/playwright/support/sections/SystemTagsFilesListPage.ts +++ b/tests/playwright/support/sections/SystemTagsFilesListPage.ts @@ -3,8 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { expect } from '@playwright/test' import type { Locator } from '@playwright/test' + +import { expect } from '@playwright/test' import { FilesListPage } from './FilesListPage.ts' /** @@ -13,7 +14,6 @@ import { FilesListPage } from './FilesListPage.ts' * and the inline collaborative-tags column. */ export class SystemTagsFilesListPage extends FilesListPage { - /** * The "Manage tags" dialog (SystemTagPicker). */ @@ -36,9 +36,7 @@ export class SystemTagsFilesListPage extends FilesListPage { * for the tags PROPFIND to complete before returning the picker locator. */ async openTagPickerForFile(filename: string): Promise { - const tagsListLoaded = this.page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPFIND' && !r.url().includes('/files'), - ) + const tagsListLoaded = this.page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPFIND' && !r.url().includes('/files')) await this.triggerActionForFile(filename, 'systemtags:bulk') await tagsListLoaded const picker = this.getTagPicker() @@ -52,9 +50,7 @@ export class SystemTagsFilesListPage extends FilesListPage { * the picker locator. */ async openTagPickerForSelection(): Promise { - const tagsListLoaded = this.page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPFIND' && !r.url().includes('/files'), - ) + const tagsListLoaded = this.page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags/') && r.request().method() === 'PROPFIND' && !r.url().includes('/files')) await this.triggerSelectionAction('systemtags:bulk') await tagsListLoaded const picker = this.getTagPicker() @@ -73,9 +69,7 @@ export class SystemTagsFilesListPage extends FilesListPage { await picker.getByLabel(/Search.*tag/i).fill(tagName) await expect(picker.getByRole('checkbox')).toHaveCount(0) - const createTagResponse = this.page.waitForResponse( - (r) => r.url().includes('/remote.php/dav/systemtags') && !r.url().includes('/files') && r.request().method() === 'POST', - ) + const createTagResponse = this.page.waitForResponse((r) => r.url().includes('/remote.php/dav/systemtags') && !r.url().includes('/files') && r.request().method() === 'POST') await picker.getByRole('button', { name: /Create new tag/i }).click() await createTagResponse diff --git a/tests/playwright/support/utils/dav.ts b/tests/playwright/support/utils/dav.ts index 2d657c2d8fc86..af7c4efa292e4 100644 --- a/tests/playwright/support/utils/dav.ts +++ b/tests/playwright/support/utils/dav.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { APIRequestContext } from '@playwright/test' import type { User } from '@nextcloud/e2e-test-server' +import type { APIRequestContext } from '@playwright/test' /** * Make a MKCOL request to create a directory at the given path for the given user. diff --git a/tests/playwright/support/utils/password-confirmation.ts b/tests/playwright/support/utils/password-confirmation.ts index 75f26b8536684..9524f1f5094f2 100644 --- a/tests/playwright/support/utils/password-confirmation.ts +++ b/tests/playwright/support/utils/password-confirmation.ts @@ -13,22 +13,22 @@ import type { Page } from '@playwright/test' */ export async function handlePasswordConfirmation(page: Page, password = 'admin') { const dialog = page.locator('.modal-container:has-text("Authentication required")') - + try { // Check if the dialog exists within a short timeout const dialogVisible = await dialog.isVisible({ timeout: 500 }).catch(() => false) - + if (dialogVisible) { // Fill the password field await dialog.locator('input[type="password"]').fill(password) - + // Click the confirm button await dialog.getByRole('button', { name: 'Confirm' }).click() - + // Wait for the dialog to disappear await dialog.waitFor({ state: 'hidden' }) } - } catch (error) { + } catch { // Dialog didn't appear, which is fine - some operations might not require confirmation } } diff --git a/tests/playwright/support/utils/sharing.ts b/tests/playwright/support/utils/sharing.ts index d2523082aaea0..6b7cb73a033ba 100644 --- a/tests/playwright/support/utils/sharing.ts +++ b/tests/playwright/support/utils/sharing.ts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { Permission } from '@nextcloud/files' import type { APIRequestContext } from '@playwright/test' // we cannot import the enum directly from the files app. @@ -14,7 +15,7 @@ export const SharePermission = { CREATE: 4, DELETE: 8, SHARE: 16, -} as const satisfies Partial +} as const satisfies Partial /** All permissions a user share can grant. */ export const ALL_PERMISSIONS = SharePermission.READ diff --git a/tests/playwright/support/utils/systemtags.ts b/tests/playwright/support/utils/systemtags.ts index b83bdaf2d28b6..2b5fab5cd5779 100644 --- a/tests/playwright/support/utils/systemtags.ts +++ b/tests/playwright/support/utils/systemtags.ts @@ -58,4 +58,4 @@ export async function clearTags(): Promise { */ export async function assignTagsToFile(fileId: string, tags: string[]): Promise { await runOcc(['tag:files:add', fileId, tags.join(','), 'public']) -} \ No newline at end of file +} diff --git a/tests/playwright/support/utils/zip.ts b/tests/playwright/support/utils/zip.ts index a6c2b2c16eae2..f9c3e85184023 100644 --- a/tests/playwright/support/utils/zip.ts +++ b/tests/playwright/support/utils/zip.ts @@ -4,8 +4,9 @@ */ import type { Download } from '@playwright/test' -import { readFile } from 'node:fs/promises' + import { Uint8ArrayReader, ZipReader } from '@zip.js/zip.js' +import { readFile } from 'node:fs/promises' /** * Read a downloaded zip and return its entry names, sorted.