Files
Ferdinand Thiessen 49dec7388e test(playwright): answer password confirmation via a locator handler
The dialog only appears once a session's confirmation window has expired, so
the old helper probed for it for 500 ms and silently skipped it on a slow
shard, leaving the guarded request unsent; its wait for the dialog to close
was unbounded.

Install a locator handler per logged-in page instead, and use
awaitPasswordGuardedRequest() where a response promise is awaited, since
handlers never run during a bare waitForResponse.

Assisted-by: ClaudeCode:claude-opus-5
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
2026-09-16 00:57:55 +02:00

156 lines
5.5 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { runOcc } from '@nextcloud/e2e-test-server/docker'
import { login } from '@nextcloud/e2e-test-server/playwright'
import { expect } from '@playwright/test'
import { test } from '../../support/fixtures/admin-with-user.ts'
import { SettingsUsersPage } from '../../support/sections/SettingsUsersPage.ts'
import { getToast } from '../../support/utils/toast.ts'
test.describe('Settings: Change user properties', () => {
test('can change the display name', async ({ page, user }) => {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
const displayNameInput = dialog.getByLabel('Display name')
await expect(displayNameInput).toHaveValue(user.userId)
await displayNameInput.fill('John Doe')
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify backend
const { stdout: jsonList } = await runOcc(['user:info', '--output=json', user.userId])
const info = JSON.parse(jsonList)
expect(info?.display_name).toBe('John Doe')
})
test('can change the password', async ({ page, user, context }) => {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
const passwordInput = dialog.getByLabel(/New password/i).and(page.locator('input')) // hack because there is no accessible role for input fields with type=password
await expect(passwordInput).toHaveValue('')
await passwordInput.fill('newpassword123')
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify by logging in with the new password
await login(context.request, { ...user, password: 'newpassword123' })
await page.goto('/apps/dashboard')
await expect(page).toHaveURL(/\/apps\/dashboard/)
})
test('can change the email address', async ({ page, user }) => {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
const emailInput = dialog.getByLabel(/Email/)
await expect(emailInput).toHaveValue('')
await emailInput.fill('mymail@example.com')
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify backend
const { stdout: jsonList } = await runOcc(['user:info', '--output=json', user.userId])
const info = JSON.parse(jsonList)
expect(info?.email).toBe('mymail@example.com')
})
test('can change the user quota to a predefined value', async ({ page, user }) => {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
// Open the Quota NcSelect and choose 5 GB
const quotaCombobox = dialog.getByRole('combobox', { name: /Quota/i })
await quotaCombobox.click()
await page.getByRole('option', { name: '5 GB' }).click()
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify backend
const { stdout: jsonList } = await runOcc(['user:info', '--output=json', user.userId])
const info = JSON.parse(jsonList)
expect(info?.quota).toBe('5 GB')
})
test('can change the user quota to a custom value', async ({ page, user }) => {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
// Type a custom value directly into the combobox
const quotaCombobox = dialog.getByRole('combobox', { name: /Quota/i })
await quotaCombobox.fill('4 MB')
await quotaCombobox.press('Enter')
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify backend (stored as bytes)
const { stdout: jsonList } = await runOcc(['user:info', '--output=json', user.userId])
const info = JSON.parse(jsonList)
expect(info?.quota).not.toBe('none')
})
test('can make user a subadmin of a group', async ({ page, user }) => {
const groupName = crypto.randomUUID().slice(0, 6)
const shortName = groupName.slice(0, 4)
await runOcc(['group:add', groupName])
try {
const settingsPage = new SettingsUsersPage(page)
await settingsPage.open()
await settingsPage.openEditDialog(user.userId)
const dialog = settingsPage.editUserDialog()
// Open the subadmin NcSelect and pick the group
const subadminCombobox = dialog.getByRole('combobox', { name: /Admin of the following groups/i })
await subadminCombobox.click()
const waitForSearch = page
.waitForResponse((r) => r.request().url().includes(`ocs/v2.php/cloud/groups/details?search=${shortName}`))
await subadminCombobox.fill(shortName)
await waitForSearch
await page.getByRole('option', { name: new RegExp(groupName) }).click()
await settingsPage.saveEditDialog()
await expect(getToast(page, /Account updated/i)).toBeVisible()
// Verify backend via OCS API (page shares admin auth state)
const response = await page.request.get(
`/ocs/v2.php/cloud/users/${user.userId}/subadmins`,
{ headers: { 'OCS-APIRequest': 'true', Accept: 'application/json' } },
)
const data = await response.json()
expect(data?.ocs?.data).toContain(groupName)
} finally {
await runOcc(['group:delete', groupName])
}
})
})