226 lines
8.2 KiB
TypeScript
Executable file
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 ?? '');
|
|
});
|
|
});
|