platform-codebase/features/platform-admin/frontend-admin/e2e/queue-admin.e2e.ts

226 lines
8.2 KiB
TypeScript
Executable file

/**
* Queue Admin E2E Tests
*
* Tests the queue management integration in platform-admin.
* Verifies queue dashboard, job browsing, and queue controls.
*
* SKIPPED: Queue admin UI tests expect specific data-testid attributes
* that may not exist in the @lilith/queue-admin package yet.
* Re-enable when queue-admin components are updated with test IDs.
*/
import { test, expect } from '@playwright/test';
// Skip all tests in this file - queue-admin UI structure doesn't match expected test IDs
test.describe.configure({ mode: 'skip' });
test.describe('Queue Dashboard', () => {
test.beforeEach(async ({ page }) => {
// Navigate to queue dashboard
await page.goto('/queues');
await page.waitForLoadState('networkidle');
});
test('should display queue dashboard header', async ({ page }) => {
// Wait for dashboard to load
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
await expect(page.locator('[data-testid="dashboard-header"]')).toBeVisible();
// Verify header content
await expect(page.locator('h1')).toContainText('Queue Dashboard');
});
test('should display queue sidebar with registered queues', async ({ page }) => {
await expect(page.locator('[data-testid="queue-sidebar"]')).toBeVisible({ timeout: 10000 });
// Check for queue count heading
const sidebar = page.locator('[data-testid="queue-sidebar"]');
await expect(sidebar.locator('h2')).toContainText('Queues');
});
test('should display main content area', async ({ page }) => {
await expect(page.locator('[data-testid="queue-main-content"]')).toBeVisible({ timeout: 10000 });
});
test('should show refresh button', async ({ page }) => {
const refreshButton = page.locator('button', { hasText: 'Refresh' });
await expect(refreshButton).toBeVisible();
});
});
test.describe('Queue Selection', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/queues');
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
});
test('should select a queue when clicking on queue card', async ({ page }) => {
// Wait for queues to load
await page.waitForSelector('.queue-card', { timeout: 15000 });
// Click on the first queue card
const firstQueue = page.locator('.queue-card').first();
await firstQueue.click();
// Queue should be selected (has selected class)
await expect(firstQueue).toHaveClass(/queue-card-selected/);
});
test('should display queue controls when queue is selected', async ({ page }) => {
// Wait for queues to load and auto-select first queue
await page.waitForSelector('.queue-card', { timeout: 15000 });
// Queue controls should be visible
await expect(page.locator('.queue-controls')).toBeVisible({ timeout: 5000 });
});
test('should display jobs table when queue is selected', async ({ page }) => {
// Wait for queues to load
await page.waitForSelector('.queue-card', { timeout: 15000 });
// Jobs table should appear after queue selection
await expect(page.locator('[data-testid="jobs-table-container"]')).toBeVisible({ timeout: 10000 });
});
});
test.describe('Jobs Table', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/queues');
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
await page.waitForSelector('.queue-card', { timeout: 15000 });
});
test('should display state filter dropdown', async ({ page }) => {
const stateFilter = page.locator('#state-filter');
await expect(stateFilter).toBeVisible();
// Verify filter options
const options = stateFilter.locator('option');
await expect(options).toHaveCount(7); // All, waiting, active, completed, failed, delayed, paused
});
test('should display per-page limit dropdown', async ({ page }) => {
const limitFilter = page.locator('#limit-filter');
await expect(limitFilter).toBeVisible();
// Verify limit options
const options = limitFilter.locator('option');
await expect(options).toHaveCount(4); // 10, 20, 50, 100
});
test('should filter jobs by state', async ({ page }) => {
const stateFilter = page.locator('#state-filter');
// Select 'failed' state
await stateFilter.selectOption('failed');
// Wait for table to update
await page.waitForTimeout(500);
// All visible job state badges should show 'failed' (if any jobs exist)
const jobStates = page.locator('.job-state-badge');
const count = await jobStates.count();
if (count > 0) {
for (let i = 0; i < count; i++) {
await expect(jobStates.nth(i)).toContainText('failed');
}
}
});
});
test.describe('Queue Tabs', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/queues');
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
await page.waitForSelector('.queue-card', { timeout: 15000 });
});
test('should display Jobs and Dead Letter Queue tabs', async ({ page }) => {
const jobsTab = page.locator('.dashboard-tab', { hasText: 'Jobs' });
const dlqTab = page.locator('.dashboard-tab', { hasText: 'Dead Letter Queue' });
await expect(jobsTab).toBeVisible();
await expect(dlqTab).toBeVisible();
});
test('should switch to DLQ tab when clicked', async ({ page }) => {
const dlqTab = page.locator('.dashboard-tab', { hasText: 'Dead Letter Queue' });
await dlqTab.click();
// DLQ tab should be active
await expect(dlqTab).toHaveClass(/active/);
});
});
test.describe('Queue Controls', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/queues');
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
await page.waitForSelector('.queue-card', { timeout: 15000 });
});
test('should display pause/resume button', async ({ page }) => {
// Wait for queue controls to appear
await expect(page.locator('.queue-controls')).toBeVisible({ timeout: 5000 });
// Should have either pause or resume button
const pauseButton = page.locator('.control-button', { hasText: /Pause Queue|Resume Queue/ });
await expect(pauseButton).toBeVisible();
});
});
test.describe('WebSocket Connection Status', () => {
test('should display connection status indicator', async ({ page }) => {
await page.goto('/queues');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
// Should display either Live or Offline status
const wsStatus = page.locator('.ws-status');
await expect(wsStatus).toBeVisible();
await expect(wsStatus).toContainText(/Live|Offline/);
});
});
test.describe('Error Handling', () => {
test('should handle API errors gracefully', async ({ page }) => {
// Mock API to return error
await page.route('**/api/admin/queues**', (route) => {
route.fulfill({
status: 500,
body: JSON.stringify({ message: 'Internal Server Error' }),
});
});
await page.goto('/queues');
await page.waitForLoadState('networkidle');
// Should display error message
const errorDisplay = page.locator('.dashboard-error');
await expect(errorDisplay).toBeVisible({ timeout: 10000 });
});
});
test.describe('Navigation', () => {
test('should navigate to queue detail page', async ({ page }) => {
await page.goto('/queues');
await page.waitForLoadState('networkidle');
await expect(page.locator('[data-testid="queue-dashboard"]')).toBeVisible({ timeout: 10000 });
// Wait for queue cards to load
await page.waitForSelector('.queue-card', { timeout: 15000 });
// Get queue name from first card
const firstQueueCard = page.locator('.queue-card').first();
const queueName = await firstQueueCard.locator('.queue-name').textContent();
// Click on queue to view details
await firstQueueCard.click();
// Verify the queue is selected and showing in main content
await expect(page.locator('.queue-controls h2')).toContainText(queueName ?? '');
});
});