test: add profile validation e2e tests and fix pre-existing failures

Add 7 new e2e tests verifying profile form validation in both manage
and admin UIs: invalid phone number, phone normalization, E.164 hint
attributes, and admin-side email/phone/picture URL validation errors.

Fix 3 pre-existing test failures:
- Replace invalid seeded phone number (+1234567890) with valid E.164
  (+12025551234) that was causing profile update tests to fail
- Update email validation error assertion to match actual pydantic
  message (value_error type uses raw message, not label-prefixed)
This commit is contained in:
Johan Lundberg 2026-03-16 10:00:46 +01:00
parent 752bf87b7c
commit 2dfa3f3bff
No known key found for this signature in database
GPG key ID: A6C152738D03C7D1
3 changed files with 97 additions and 8 deletions

View file

@ -129,7 +129,67 @@ test.describe('Admin pages', () => {
});
// ---------------------------------------------------------------
// 8. Groups update
// 8. Profile validation
// ---------------------------------------------------------------
test.describe('Profile validation', () => {
test('invalid email shows error', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`/admin/users/${fixtures.admin_userid}`);
// Bypass HTML5 email validation so the form can submit
await page.locator('#email').evaluate(el => el.type = 'text');
await page.fill('#email', 'not-an-email');
await page.click('section:has(h2:has-text("Profile")) button[type="submit"]');
const alert = page.locator('#profile-status [role="alert"]');
await expect(alert).toBeVisible({ timeout: 5000 });
await expect(alert).toContainText('valid email address');
});
test('invalid phone number shows error', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`/admin/users/${fixtures.admin_userid}`);
// Bypass HTML5 pattern validation so the form can submit
await page.locator('#phone_number').evaluate(el => el.removeAttribute('pattern'));
await page.fill('#phone_number', 'not-a-phone');
await page.click('section:has(h2:has-text("Profile")) button[type="submit"]');
const alert = page.locator('#profile-status [role="alert"]');
await expect(alert).toBeVisible({ timeout: 5000 });
await expect(alert).toContainText('valid phone number');
});
test('invalid picture URL shows error', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`/admin/users/${fixtures.admin_userid}`);
// Bypass HTML5 URL validation so the form can submit
await page.locator('#picture').evaluate(el => el.type = 'text');
await page.fill('#picture', 'not-a-url');
await page.click('section:has(h2:has-text("Profile")) button[type="submit"]');
const alert = page.locator('#profile-status [role="alert"]');
await expect(alert).toBeVisible({ timeout: 5000 });
await expect(alert).toContainText('Picture URL');
});
test('phone input has E.164 hint attributes', async ({ page }) => {
await loginAsAdmin(page);
await page.goto(`/admin/users/${fixtures.admin_userid}`);
const phoneInput = page.locator('#phone_number');
await expect(phoneInput).toHaveAttribute('type', 'tel');
await expect(phoneInput).toHaveAttribute('pattern', '\\+[0-9 ]+');
await expect(phoneInput).toHaveAttribute('title', /International format/);
});
});
// ---------------------------------------------------------------
// 9. Groups update
// ---------------------------------------------------------------
test.describe('Groups update', () => {
test('change groups and save', async ({ page }) => {
@ -146,7 +206,7 @@ test.describe('Admin pages', () => {
});
// ---------------------------------------------------------------
// 9. Activate/deactivate toggle
// 10. Activate/deactivate toggle
// ---------------------------------------------------------------
test.describe('Activate/deactivate toggle', () => {
test('deactivate then activate user', async ({ page }) => {
@ -175,7 +235,7 @@ test.describe('Admin pages', () => {
});
// ---------------------------------------------------------------
// 10. Create invite from user list
// 11. Create invite from user list
// ---------------------------------------------------------------
test.describe('Create invite', () => {
test('fill username and submit to get invite URL', async ({ page }) => {
@ -193,7 +253,7 @@ test.describe('Admin pages', () => {
});
// ---------------------------------------------------------------
// 11. Re-invite from user detail
// 12. Re-invite from user detail
// ---------------------------------------------------------------
test.describe('Re-invite from user detail', () => {
test('generate invite link from user detail page', async ({ page }) => {
@ -210,7 +270,7 @@ test.describe('Admin pages', () => {
});
// ---------------------------------------------------------------
// 12. Delete user
// 13. Delete user
// ---------------------------------------------------------------
test.describe('Delete user', () => {
test('delete a user and verify redirect to user list', async ({ page }) => {