- Replaced static paths with dynamic locale subpaths (`/[locale]/*`) in imports, URLs, and assertions across tests. - Updated `next-intl` mocks for improved compatibility with `locale`-aware components. - Standardized `page.goto` and navigation tests with `/en` as the base locale for consistency.
252 lines
8.9 KiB
TypeScript
252 lines
8.9 KiB
TypeScript
/**
|
|
* E2E Tests for Admin Organization Members Management
|
|
* Tests AddMemberDialog Select interactions (excluded from unit tests with istanbul ignore)
|
|
* and basic navigation to organization members page
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { setupSuperuserMocks } from './helpers/auth';
|
|
|
|
test.describe('Admin Organization Members - Navigation from Organizations List', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await setupSuperuserMocks(page);
|
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
|
await page.goto('/en/admin/organizations');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should navigate to members page when clicking view members in action menu', async ({
|
|
page,
|
|
}) => {
|
|
// Click first organization's action menu
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
// Click "View Members"
|
|
await Promise.all([
|
|
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
|
page.getByText('View Members').click(),
|
|
]);
|
|
|
|
// Should be on members page
|
|
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
|
});
|
|
|
|
test('should navigate to members page when clicking member count', async ({ page }) => {
|
|
// Find first organization row with members
|
|
const firstRow = page.locator('table tbody tr').first();
|
|
const memberButton = firstRow.locator('button').filter({ hasText: /^\d+$/ });
|
|
|
|
// Click on member count
|
|
await Promise.all([
|
|
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
|
memberButton.click(),
|
|
]);
|
|
|
|
// Should be on members page
|
|
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
|
});
|
|
});
|
|
|
|
test.describe('Admin Organization Members - Page Structure', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await setupSuperuserMocks(page);
|
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
|
await page.goto('/en/admin/organizations');
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Navigate to members page
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
await Promise.all([
|
|
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
|
page.getByText('View Members').click(),
|
|
]);
|
|
});
|
|
|
|
test('should display organization members page', async ({ page }) => {
|
|
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
|
|
|
// Wait for page to load
|
|
await page.waitForSelector('table');
|
|
|
|
// Should show organization name in heading
|
|
await expect(page.getByRole('heading', { name: /Members/i })).toBeVisible();
|
|
});
|
|
|
|
test('should display page description', async ({ page }) => {
|
|
await expect(
|
|
page.getByText('Manage members and their roles within the organization')
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('should display add member button', async ({ page }) => {
|
|
const addButton = page.getByRole('button', { name: /Add Member/i });
|
|
await expect(addButton).toBeVisible();
|
|
});
|
|
|
|
test('should display back to organizations button', async ({ page }) => {
|
|
const backButton = page.getByRole('link', { name: /Back to Organizations/i });
|
|
await expect(backButton).toBeVisible();
|
|
});
|
|
|
|
test('should have proper heading hierarchy', async ({ page }) => {
|
|
// Wait for page to load
|
|
await page.waitForSelector('table');
|
|
|
|
// Page should have h2 with organization name
|
|
const heading = page.getByRole('heading', { name: /Members/i });
|
|
await expect(heading).toBeVisible();
|
|
});
|
|
|
|
test('should have proper table structure', async ({ page }) => {
|
|
await page.waitForSelector('table');
|
|
|
|
// Table should have thead and tbody
|
|
const table = page.locator('table');
|
|
await expect(table.locator('thead')).toBeVisible();
|
|
await expect(table.locator('tbody')).toBeVisible();
|
|
});
|
|
|
|
test('should have accessible back button', async ({ page }) => {
|
|
const backButton = page.getByRole('link', { name: /Back to Organizations/i });
|
|
await expect(backButton).toBeVisible();
|
|
|
|
// Should have an icon
|
|
const icon = backButton.locator('svg');
|
|
await expect(icon).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Admin Organization Members - AddMemberDialog E2E Tests', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await setupSuperuserMocks(page);
|
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
|
await page.goto('/en/admin/organizations');
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Navigate to members page
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
await Promise.all([
|
|
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
|
page.getByText('View Members').click(),
|
|
]);
|
|
|
|
// Open Add Member dialog
|
|
const addButton = page.getByRole('button', { name: /Add Member/i });
|
|
await addButton.click();
|
|
|
|
// Wait for dialog to be visible
|
|
await page.waitForSelector('[role="dialog"]');
|
|
});
|
|
|
|
test('should open add member dialog when clicking add member button', async ({ page }) => {
|
|
// Dialog should be visible
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toBeVisible();
|
|
|
|
// Should have dialog title
|
|
await expect(page.getByRole('heading', { name: /Add Member/i })).toBeVisible();
|
|
});
|
|
|
|
test('should display dialog description', async ({ page }) => {
|
|
await expect(
|
|
page.getByText(/Add a user to this organization and assign them a role/i)
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('should display user email select field', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog.getByText('User Email *')).toBeVisible();
|
|
});
|
|
|
|
test('should display role select field', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog.getByText('Role *')).toBeVisible();
|
|
});
|
|
|
|
test('should display add member and cancel buttons', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog.getByRole('button', { name: /^Add Member$/i })).toBeVisible();
|
|
await expect(dialog.getByRole('button', { name: /Cancel/i })).toBeVisible();
|
|
});
|
|
|
|
test('should close dialog when clicking cancel', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
const cancelButton = dialog.getByRole('button', { name: /Cancel/i });
|
|
|
|
await cancelButton.click();
|
|
|
|
// Dialog should be closed
|
|
await expect(dialog).not.toBeVisible();
|
|
});
|
|
|
|
test('should open user email select dropdown when clicked', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
|
|
// Click user email select trigger
|
|
const userSelect = dialog.getByRole('combobox').first();
|
|
await userSelect.click();
|
|
|
|
// Dropdown should be visible with mock user options
|
|
await expect(page.getByRole('option', { name: /test@example.com/i })).toBeVisible();
|
|
await expect(page.getByRole('option', { name: /admin@example.com/i })).toBeVisible();
|
|
});
|
|
|
|
test('should select user email from dropdown', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
|
|
// Click user email select trigger
|
|
const userSelect = dialog.getByRole('combobox').first();
|
|
await userSelect.click();
|
|
|
|
// Select first user
|
|
await page.getByRole('option', { name: /test@example.com/i }).click();
|
|
|
|
// Selected value should be visible
|
|
await expect(userSelect).toContainText('test@example.com');
|
|
});
|
|
|
|
test('should open role select dropdown when clicked', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
|
|
// Click role select trigger (second combobox)
|
|
const roleSelects = dialog.getByRole('combobox');
|
|
const roleSelect = roleSelects.nth(1);
|
|
await roleSelect.click();
|
|
|
|
// Dropdown should show role options
|
|
await expect(page.getByRole('option', { name: /^Owner$/i })).toBeVisible();
|
|
await expect(page.getByRole('option', { name: /^Admin$/i })).toBeVisible();
|
|
await expect(page.getByRole('option', { name: /^Member$/i })).toBeVisible();
|
|
await expect(page.getByRole('option', { name: /^Guest$/i })).toBeVisible();
|
|
});
|
|
|
|
test('should select role from dropdown', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
|
|
// Click role select trigger
|
|
const roleSelects = dialog.getByRole('combobox');
|
|
const roleSelect = roleSelects.nth(1);
|
|
await roleSelect.click();
|
|
|
|
// Select admin role
|
|
await page.getByRole('option', { name: /^Admin$/i }).click();
|
|
|
|
// Selected value should be visible
|
|
await expect(roleSelect).toContainText('Admin');
|
|
});
|
|
|
|
test('should have default role as Member', async ({ page }) => {
|
|
const dialog = page.locator('[role="dialog"]');
|
|
const roleSelects = dialog.getByRole('combobox');
|
|
const roleSelect = roleSelects.nth(1);
|
|
|
|
// Default role should be Member
|
|
await expect(roleSelect).toContainText('Member');
|
|
});
|
|
});
|