mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-29 20:19:04 +01:00
feat(frontend): add v3 compositions for pages, settings, tables and identity
This commit is contained in:
@@ -3,7 +3,7 @@ import { emailVerificationTokens, users } from '../data';
|
||||
import authUtil from '../utils/auth.util';
|
||||
import { cleanupBackend } from '../utils/cleanup.util';
|
||||
import passkeyUtil from '../utils/passkey.util';
|
||||
import { saveUnsavedChanges } from '../utils/unsaved-changes.util';
|
||||
import { saveUnsavedChanges, unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
test.beforeEach(async () => await cleanupBackend());
|
||||
|
||||
@@ -41,7 +41,9 @@ test('Failed account update remains dirty and can be retried', async ({ page })
|
||||
await displayName.fill('Retryable Account');
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Temporary account update failure', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Temporary account update failure', { exact: true })
|
||||
).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Save', exact: true })).toBeVisible();
|
||||
expect(failedUpdates).toBe(1);
|
||||
|
||||
@@ -57,7 +59,9 @@ test('Update account details fails with already taken email', async ({ page }) =
|
||||
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Email is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Email is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Update account details fails with already taken username', async ({ page }) => {
|
||||
@@ -67,7 +71,9 @@ test('Update account details fails with already taken username', async ({ page }
|
||||
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Username is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Username is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Update account details fails with already taken username in different casing', async ({
|
||||
@@ -79,7 +85,9 @@ test('Update account details fails with already taken username in different casi
|
||||
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Username is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Username is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Change Locale', async ({ page }) => {
|
||||
@@ -117,24 +125,32 @@ test('Add passkey to an account', async ({ page }) => {
|
||||
await page.getByLabel('Name', { exact: true }).fill('Test Passkey');
|
||||
await page.getByLabel('Name Passkey').getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByText('Test Passkey')).toBeVisible();
|
||||
await expect(page.getByText('Test Passkey', { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('Rename passkey', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Rename').first().click();
|
||||
await page
|
||||
.getByRole('button', { name: /^Actions for / })
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Rename' }).click();
|
||||
|
||||
await page.getByLabel('Name', { exact: true }).fill('Renamed Passkey');
|
||||
await page.getByLabel('Name Passkey').getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByText('Renamed Passkey')).toBeVisible();
|
||||
await expect(page.getByText('Renamed Passkey', { exact: true })).toBeVisible();
|
||||
});
|
||||
|
||||
test('Delete passkey from account', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Delete').first().click();
|
||||
await page
|
||||
.getByRole('button', { name: /^Actions for / })
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByLabel('Delete Passkey').getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await expect(page.locator('[data-type="success"]')).toHaveText('Passkey deleted successfully');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { cleanupBackend } from '../utils/cleanup.util';
|
||||
import { saveUnsavedChanges } from '../utils/unsaved-changes.util';
|
||||
import { saveUnsavedChanges, unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await cleanupBackend();
|
||||
@@ -116,13 +116,16 @@ test('Invalid hidden configuration prevents every section from being saved', asy
|
||||
|
||||
const emailTab = page.getByRole('tab', { name: 'Email' });
|
||||
const smtpPort = page.getByLabel('SMTP Port');
|
||||
await expect(page.getByText('Please fix the highlighted errors before saving')).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Couldn’t save. Check the highlighted fields.')
|
||||
).toBeVisible();
|
||||
await expect(emailTab).toHaveAttribute('aria-selected', 'true');
|
||||
await expect(smtpPort).toHaveAttribute('aria-invalid', 'true');
|
||||
await expect(smtpPort).toBeFocused();
|
||||
expect(updateRequests).toBe(0);
|
||||
|
||||
await smtpPort.fill('587');
|
||||
await expect(smtpPort).toHaveAttribute('aria-invalid', 'false');
|
||||
await saveUnsavedChanges(page);
|
||||
|
||||
await page.reload();
|
||||
@@ -300,7 +303,7 @@ test('Save LDAP configuration while LDAP remains disabled', async ({ page }) =>
|
||||
const softDeleteUsers = page.getByRole('switch', { name: 'Keep disabled users from LDAP' });
|
||||
const originalValue = await softDeleteUsers.isChecked();
|
||||
await softDeleteUsers.click();
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toBeVisible();
|
||||
await expect(unsavedChangesBar(page)).toContainText(/\d+ unsaved changes?/);
|
||||
|
||||
await page.getByRole('button', { name: 'Discard', exact: true }).click();
|
||||
await expect(softDeleteUsers).toBeChecked({ checked: originalValue });
|
||||
@@ -326,7 +329,7 @@ test.describe('Update application images', () => {
|
||||
await page
|
||||
.getByRole('button', { name: 'Reset to default Background Image', exact: true })
|
||||
.click();
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toBeVisible();
|
||||
await expect(unsavedChangesBar(page)).toContainText(/\d+ unsaved changes?/);
|
||||
|
||||
await page.getByRole('button', { name: 'Discard', exact: true }).click();
|
||||
await expect(
|
||||
@@ -415,6 +418,8 @@ test.describe('Update application images', () => {
|
||||
await emailLogoInput.setInputFiles('resources/images/cloud-logo.svg');
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('File must be of type PNG or JPEG', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('File must be of type PNG or JPEG', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,11 @@ test('approves the QR link after requester review and fresh reauthentication', a
|
||||
const expectedLoginCodeText = `${waiting.request.userCode.substring(0, 4)} - ${waiting.request.userCode.substring(4, 8)}`;
|
||||
try {
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
await expect(waiting.page.getByTestId('device-login-code')).toHaveText(expectedLoginCodeText);
|
||||
await expect(waiting.page.getByTestId('device-login-code')).toHaveText(
|
||||
new RegExp(
|
||||
`${waiting.request.userCode.substring(0, 4)}\\s*–\\s*${waiting.request.userCode.substring(4, 8)}`
|
||||
)
|
||||
);
|
||||
await expect(waiting.page.getByLabel('Device login QR code')).toBeVisible();
|
||||
|
||||
await page.goto(waiting.request.verificationUriComplete);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { cleanupBackend } from '../utils/cleanup.util';
|
||||
import { unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
test.beforeEach(async () => await cleanupBackend());
|
||||
|
||||
@@ -27,7 +28,7 @@ test('unsaved changes block navigation until they are discarded', async ({ page
|
||||
const displayName = page.getByLabel('Display Name');
|
||||
const originalDisplayName = await displayName.inputValue();
|
||||
await displayName.fill('Pending navigation');
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toBeVisible();
|
||||
await expect(unsavedChangesBar(page)).toContainText(/\d+ unsaved changes?/);
|
||||
|
||||
const myApps = page.getByRole('link', { name: 'My Apps' }).first();
|
||||
await myApps.click();
|
||||
@@ -35,9 +36,9 @@ test('unsaved changes block navigation until they are discarded', async ({ page
|
||||
|
||||
await page.getByRole('button', { name: 'Discard', exact: true }).click();
|
||||
await expect(displayName).toHaveValue(originalDisplayName);
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toHaveCount(0);
|
||||
await expect(unsavedChangesBar(page)).toHaveCount(0);
|
||||
|
||||
await myApps.click();
|
||||
await expect(page).toHaveURL(/\/settings\/apps$/);
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toHaveCount(0);
|
||||
await expect(unsavedChangesBar(page)).toHaveCount(0);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as jose from 'jose';
|
||||
import { oidcClients, userGroups } from '../data';
|
||||
import { cleanupBackend } from '../utils/cleanup.util';
|
||||
import * as oidcUtil from '../utils/oidc.util';
|
||||
import { saveUnsavedChanges } from '../utils/unsaved-changes.util';
|
||||
import { saveUnsavedChanges, unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
test.beforeEach(async () => await cleanupBackend());
|
||||
|
||||
@@ -556,7 +556,7 @@ test('Update OIDC client federated credentials with public keys', async ({ page
|
||||
const privateKeyUpdate = waitForClientUpdate();
|
||||
await saveButton.click();
|
||||
expect((await privateKeyUpdate).status()).toBe(400);
|
||||
await expect(page.getByText(/private key material/)).toBeVisible();
|
||||
await expect(unsavedChangesBar(page).getByText(/private key material/)).toBeVisible();
|
||||
await publicKeys.last().getByRole('button').click();
|
||||
await expect(publicKeys).toHaveCount(3);
|
||||
|
||||
@@ -568,7 +568,7 @@ test('Update OIDC client federated credentials with public keys', async ({ page
|
||||
const missingKeyIdUpdate = waitForClientUpdate();
|
||||
await saveButton.click();
|
||||
expect((await missingKeyIdUpdate).status()).toBe(400);
|
||||
await expect(page.getByText(/missing the "kid" property/)).toBeVisible();
|
||||
await expect(unsavedChangesBar(page).getByText(/missing the "kid" property/)).toBeVisible();
|
||||
await publicKeys.last().getByRole('button').click();
|
||||
await expect(publicKeys).toHaveCount(3);
|
||||
|
||||
@@ -703,7 +703,7 @@ test('Delete OIDC client', async ({ page }) => {
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: oidcClient.name })
|
||||
.getByRole('button', { name: 'Toggle menu' })
|
||||
.getByRole('button', { name: /^Actions for / })
|
||||
.click();
|
||||
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
@@ -760,9 +760,11 @@ test('Filter OIDC clients by PAR requirement', async ({ page, request }) => {
|
||||
await expect(page.getByRole('row', { name: parClient.name })).toBeVisible();
|
||||
await expect(page.getByRole('row', { name: oidcClients.nextcloud.name })).not.toBeVisible();
|
||||
|
||||
// Deselect "Yes" and select "No" to invert the filter
|
||||
await page.getByTestId('facet-par-option-true').click();
|
||||
// Reset the filter to "Any", then select "No" to invert it
|
||||
await page.getByTestId('facet-par-trigger').click();
|
||||
await page.getByTestId('facet-par-option-any').click();
|
||||
await expect(page.getByRole('row', { name: oidcClients.nextcloud.name })).toBeVisible();
|
||||
await page.getByTestId('facet-par-trigger').click();
|
||||
await page.getByTestId('facet-par-option-false').click();
|
||||
|
||||
// PAR client should be hidden, others visible
|
||||
|
||||
+19
-19
@@ -96,7 +96,7 @@ test('Authorize new client', async ({ page }) => {
|
||||
const urlParams = createUrlParams(oidcClient);
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
@@ -111,7 +111,7 @@ test('Authorize client requesting offline_access scope', async ({ page }) => {
|
||||
|
||||
// offline_access is a valid OIDC scope: the flow must reach the consent screen rather than
|
||||
// being rejected with invalid_scope (offline_access itself has no displayable scope item)
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
const callbackUrl = await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
@@ -125,10 +125,10 @@ test('Authorize new client with a deselected scope', async ({ page }) => {
|
||||
const urlParams = createUrlParams(oidcClient);
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await page.getByRole('checkbox', { name: 'Email' }).click();
|
||||
await expect(page.getByRole('checkbox', { name: 'Email' })).not.toBeChecked();
|
||||
await expect(page.getByRole('checkbox', { name: 'Profile' })).toBeChecked();
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
await page.getByRole('checkbox', { name: 'Email address' }).click();
|
||||
await expect(page.getByRole('checkbox', { name: 'Email address' })).not.toBeChecked();
|
||||
await expect(page.getByRole('checkbox', { name: 'Name, username and picture' })).toBeChecked();
|
||||
|
||||
const callbackUrl = await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
@@ -199,7 +199,7 @@ test('Authorize new client while not signed in', async ({ page }) => {
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
@@ -216,7 +216,7 @@ test('Authorize new client shows Pocket ID error when user group not allowed', a
|
||||
await (await passkeyUtil.init(page)).addPasskey('craig');
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
@@ -701,7 +701,7 @@ test('Authorize new client with device authorization flow', async ({ page }) =>
|
||||
await page.goto(`/device?user_code=${userCode}`);
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
@@ -724,10 +724,10 @@ test('Authorize new client with device authorization flow and a deselected scope
|
||||
await page.goto(`/device?user_code=${device.user_code}`);
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await page.getByRole('checkbox', { name: 'Email' }).click();
|
||||
await expect(page.getByRole('checkbox', { name: 'Email' })).not.toBeChecked();
|
||||
await expect(page.getByRole('checkbox', { name: 'Profile' })).toBeChecked();
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
await page.getByRole('checkbox', { name: 'Email address' }).click();
|
||||
await expect(page.getByRole('checkbox', { name: 'Email address' })).not.toBeChecked();
|
||||
await expect(page.getByRole('checkbox', { name: 'Name, username and picture' })).toBeChecked();
|
||||
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
await expect(
|
||||
@@ -763,7 +763,7 @@ test('Authorize new client with device authorization flow while not signed in',
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
@@ -834,7 +834,7 @@ for (const scenario of [
|
||||
} else {
|
||||
await page.getByRole('button', { name: 'Authorize', exact: true }).click();
|
||||
if (scenario.consentRequired) {
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
await page.getByRole('button', { name: 'Authorize', exact: true }).click();
|
||||
}
|
||||
await expect(
|
||||
@@ -962,7 +962,7 @@ test('Authorize new client with device authorization with user group not allowed
|
||||
await (await passkeyUtil.init(page)).addPasskey('craig');
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
await page.getByRole('button', { name: 'Authorize' }).click();
|
||||
|
||||
@@ -1241,7 +1241,7 @@ test.describe('OIDC prompt parameter', () => {
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
// Should show consent UI even though client was already authorized
|
||||
await expectScopes(page, ['Profile', 'Email']);
|
||||
await expectScopes(page, ['Name, username and picture', 'Email address']);
|
||||
|
||||
await expectCallbackRedirect(page, oidcClient.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
@@ -1532,7 +1532,7 @@ test.describe('Pushed Authorization Requests (PAR)', () => {
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
// Consent screen with the requested scope (resolved from the PAR) must be shown
|
||||
await expectScopes(page, ['Groups']);
|
||||
await expectScopes(page, ['Group membership']);
|
||||
|
||||
// Confirming proceeds with the authorization
|
||||
await expectCallbackRedirect(page, client.callbackUrl, () =>
|
||||
@@ -1685,7 +1685,7 @@ test.describe('OIDC skip consent', () => {
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
// prompt=consent overrides the client's skip-consent setting, so the scopes must still be shown
|
||||
await expectScopes(page, ['Email', 'Profile']);
|
||||
await expectScopes(page, ['Email address', 'Name, username and picture']);
|
||||
|
||||
const callbackUrl = await expectCallbackRedirect(page, client.callbackUrl, () =>
|
||||
page.getByRole('button', { name: 'Sign in' }).click()
|
||||
|
||||
@@ -23,7 +23,11 @@ test('Sign in with login code without a passkey shows passkey setup', async ({ p
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
await page.getByRole('button', { name: 'Authenticate' }).click();
|
||||
await page.waitForURL('/settings/account');
|
||||
await page.getByLabel('Delete').first().click();
|
||||
await page
|
||||
.getByRole('button', { name: /^Actions for / })
|
||||
.first()
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByLabel('Delete Passkey').getByRole('button', { name: 'Delete' }).click();
|
||||
await expect(page.locator('[data-type="success"]')).toHaveText('Passkey deleted successfully');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import test, { expect, type Page } from '@playwright/test';
|
||||
import { cleanupBackend, cleanupScimServiceProvider } from 'utils/cleanup.util';
|
||||
import { oidcClients, userGroups, users } from '../data';
|
||||
import { saveUnsavedChanges } from '../utils/unsaved-changes.util';
|
||||
import { saveUnsavedChanges, unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
async function configureOidcClient(page: Page) {
|
||||
await page.goto(`/settings/admin/oidc-clients/${oidcClients.scim.id}`);
|
||||
@@ -36,14 +36,14 @@ test.describe('SCIM Configuration', () => {
|
||||
|
||||
await page.getByLabel('SCIM Endpoint').fill('http://scim.provider/api');
|
||||
await page.getByLabel('SCIM Token').fill('supersecrettoken');
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toHaveCount(0);
|
||||
await expect(unsavedChangesBar(page)).toHaveCount(0);
|
||||
|
||||
await page.getByRole('button', { name: 'Enable' }).click();
|
||||
|
||||
await expect(page.locator('[data-type="success"]')).toHaveText('SCIM enabled successfully.');
|
||||
|
||||
await page.getByLabel('SCIM Endpoint').fill('http://edited.scim.provider/api');
|
||||
await expect(page.getByText('You have unsaved changes', { exact: true })).toBeVisible();
|
||||
await expect(unsavedChangesBar(page)).toContainText(/\d+ unsaved changes?/);
|
||||
await page.getByRole('button', { name: 'Discard', exact: true }).click();
|
||||
await expect(page.getByLabel('SCIM Endpoint')).toHaveValue('http://scim.provider/api');
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import test, { expect } from '@playwright/test';
|
||||
import { userGroups, users } from '../data';
|
||||
import authUtil from '../utils/auth.util';
|
||||
import { cleanupBackend } from '../utils/cleanup.util';
|
||||
import { saveUnsavedChanges } from '../utils/unsaved-changes.util';
|
||||
import { saveUnsavedChanges, unsavedChangesBar } from '../utils/unsaved-changes.util';
|
||||
|
||||
test.beforeEach(async () => await cleanupBackend());
|
||||
|
||||
@@ -177,7 +177,9 @@ test('Update user fails with already taken email', async ({ page }) => {
|
||||
await page.getByLabel('Email').fill(users.tim.email);
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Email is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Email is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Update user fails with already taken username', async ({ page }) => {
|
||||
@@ -194,7 +196,9 @@ test('Update user fails with already taken username', async ({ page }) => {
|
||||
await page.getByLabel('Username').fill(users.tim.username);
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Username is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Username is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Update user fails with already taken username in different casing', async ({ page }) => {
|
||||
@@ -211,7 +215,9 @@ test('Update user fails with already taken username in different casing', async
|
||||
await page.getByLabel('Username').fill(users.tim.username.toUpperCase());
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
|
||||
await expect(page.getByText('Username is already in use', { exact: true })).toBeVisible();
|
||||
await expect(
|
||||
unsavedChangesBar(page).getByText('Username is already in use', { exact: true })
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('Update user custom claims', async ({ page }) => {
|
||||
@@ -275,7 +281,7 @@ test('Admin can view another user passkeys', async ({ page }) => {
|
||||
await page.goto(`/settings/admin/users/${users.craig.id}`);
|
||||
await page.getByRole('tab', { name: 'Passkeys' }).click();
|
||||
|
||||
await expect(page.getByText('Passkey 2')).toBeVisible();
|
||||
await expect(page.getByText('Passkey 2', { exact: true })).toBeVisible();
|
||||
await expect(page.getByText(/Added on/)).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -283,15 +289,11 @@ test('Admin can delete another user passkey and audit log is created', async ({
|
||||
await page.goto(`/settings/admin/users/${users.craig.id}`);
|
||||
await page.getByRole('tab', { name: 'Passkeys' }).click();
|
||||
|
||||
await page
|
||||
.locator('[data-slot="item"]')
|
||||
.filter({ hasText: 'Passkey 2' })
|
||||
.getByLabel('Delete')
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'Delete Passkey 2' }).click();
|
||||
await page.getByRole('alertdialog').getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await expect(page.locator('[data-type="success"]')).toHaveText('Passkey deleted successfully');
|
||||
await expect(page.getByText('Passkey 2')).not.toBeVisible();
|
||||
await expect(page.getByText('Passkey 2', { exact: true })).not.toBeVisible();
|
||||
|
||||
const auditLogResponse = await page.request.get('/api/audit-logs/all');
|
||||
expect(auditLogResponse.ok()).toBeTruthy();
|
||||
|
||||
@@ -52,9 +52,9 @@ test.describe('Signup Token Creation', () => {
|
||||
|
||||
await page.getByRole('button', { name: 'Create options' }).click();
|
||||
await page.getByRole('menuitem', { name: 'View Active Signup Tokens' }).click();
|
||||
await page.getByLabel('Manage Signup Tokens').getByRole('button', { name: 'View' }).click();
|
||||
await page.getByLabel('Manage Signup Tokens').getByRole('button', { name: 'Display' }).click();
|
||||
|
||||
await page.getByRole('menuitemcheckbox', { name: 'User Groups' }).click();
|
||||
await page.getByRole('button', { name: 'User Groups', exact: true }).click();
|
||||
|
||||
const row = page.getByRole('row').last();
|
||||
await expect(row.getByRole('cell', { name: '0 of 8' })).toBeVisible();
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { expect, type Page } from '@playwright/test';
|
||||
|
||||
export function unsavedChangesBar(page: Page) {
|
||||
return page.getByRole('region', { name: 'Unsaved changes' });
|
||||
}
|
||||
|
||||
export async function saveUnsavedChanges(page: Page) {
|
||||
await page.getByRole('button', { name: 'Save', exact: true }).click();
|
||||
await expect(page.getByText('Changes saved successfully', { exact: true })).toBeVisible();
|
||||
await expect(unsavedChangesBar(page).getByText('Changes saved', { exact: true })).toBeVisible();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user