Files
caddy-proxy-manager/tests/e2e/groups.spec.ts
fuomag9 7f4a268cf7 Fix flaky E2E tests: strict mode violations, OAuth redirect, parallelism
- Set workers: 1 to eliminate parallelism race conditions
- Fix groups test: use .first() for "0 members" assertion
- Fix access-control helper: match by name instead of generic "Delete List"
- Fix forward-auth-oauth: target Dex button specifically, handle /login in Dex URL
- Add comprehensive API security E2E tests (316 tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:17:49 +02:00

111 lines
4.4 KiB
TypeScript

/**
* E2E tests: Groups management page.
*
* Verifies group creation, member management, and deletion.
* Runs as admin (testadmin) — the page requires admin role.
*/
import { test, expect } from '@playwright/test';
test.describe('Groups page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/groups');
});
test('page loads with Groups heading', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Groups' })).toBeVisible();
await expect(page.getByText('Organize users into groups for forward auth access control.')).toBeVisible();
});
test('New Group button is visible', async ({ page }) => {
await expect(page.getByRole('button', { name: /new group/i })).toBeVisible();
});
test('clicking New Group toggles create form', async ({ page }) => {
await page.getByRole('button', { name: /new group/i }).click();
// Form fields should appear
await expect(page.getByLabel('Name')).toBeVisible();
await expect(page.getByLabel('Description')).toBeVisible();
await expect(page.getByRole('button', { name: 'Create' })).toBeVisible();
await expect(page.getByRole('button', { name: 'Cancel' })).toBeVisible();
});
test('clicking Cancel hides the create form', async ({ page }) => {
await page.getByRole('button', { name: /new group/i }).click();
await expect(page.getByLabel('Name')).toBeVisible();
await page.getByRole('button', { name: 'Cancel' }).click();
await expect(page.getByLabel('Name')).not.toBeVisible();
});
test('create a new group', async ({ page }) => {
await page.getByRole('button', { name: /new group/i }).click();
await page.getByLabel('Name').fill('E2E Test Group');
await page.getByLabel('Description').fill('Created by E2E test');
await page.getByRole('button', { name: 'Create' }).click();
// Group should appear in the list
await expect(page.getByText('E2E Test Group')).toBeVisible({ timeout: 10_000 });
await expect(page.getByText('Created by E2E test')).toBeVisible();
await expect(page.getByText('0 members').first()).toBeVisible();
});
test('add member to group', async ({ page }) => {
// Ensure the group exists
await expect(page.getByText('E2E Test Group')).toBeVisible({ timeout: 5_000 });
// Click add member button
await page.getByTitle('Add member').first().click();
await expect(page.getByText('Add a user to this group')).toBeVisible();
// Click the first available user in the add-member list to add them.
// The add-member list items are full-width buttons inside a bordered container.
const memberList = page.locator('.border.rounded-md');
const firstUser = memberList.locator('button').first();
if (await firstUser.isVisible({ timeout: 3_000 }).catch(() => false)) {
await firstUser.click();
// Member should now appear in the group
await expect(page.getByText('1 member')).toBeVisible({ timeout: 10_000 });
}
});
test('remove member from group', async ({ page }) => {
// If the group has a member, remove it
const removeMemberBtn = page.getByTitle('Remove member').first();
if (await removeMemberBtn.isVisible({ timeout: 3_000 }).catch(() => false)) {
await removeMemberBtn.click();
await expect(page.getByText('0 members')).toBeVisible({ timeout: 10_000 });
}
});
test('delete group via confirm dialog', async ({ page }) => {
await expect(page.getByText('E2E Test Group')).toBeVisible({ timeout: 5_000 });
// Accept the confirm dialog
page.on('dialog', (dialog) => dialog.accept());
await page.getByTitle('Delete group').first().click();
// Group should be removed
await expect(page.getByText('E2E Test Group')).not.toBeVisible({ timeout: 10_000 });
});
test('shows empty state when no groups exist', async ({ page }) => {
// If there are no groups, the empty state text should be visible
// (This may or may not show depending on existing data)
const newGroupBtn = page.getByRole('button', { name: /new group/i });
// At minimum the button should always be visible
await expect(newGroupBtn).toBeVisible();
});
});
test.describe('Groups page — unauthenticated access', () => {
test.use({ storageState: { cookies: [], origins: [] } });
test('unauthenticated access to /groups redirects to /login', async ({ page }) => {
await page.goto('/groups');
await expect(page).toHaveURL(/\/login/);
});
});