237 lines
8.5 KiB
TypeScript
237 lines
8.5 KiB
TypeScript
/**
|
|
* Conversation Assistant E2E Tests for Platform Dev
|
|
*
|
|
* Tests the conversation-assistant integration:
|
|
* - Scammer Database page (/scammers)
|
|
* - Training Samples page (/training)
|
|
*
|
|
* These pages proxy requests to the conversation-assistant backend (port 3100)
|
|
* through platform-dev's vite proxy configuration.
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { applyConversationMocks } from './fixtures/api-mocks';
|
|
|
|
test.describe('Conversation Assistant - Scammers Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyConversationMocks(page);
|
|
});
|
|
|
|
test('renders scammers page header', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should display page title
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent.locator('h1').first()).toContainText(/scammer/i);
|
|
});
|
|
|
|
test('displays scammer stats cards', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check for stat labels based on SCAMMERS_MOCKS stats
|
|
await expect(page.locator('text=Total').first()).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('text=Suspected').first()).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('text=Confirmed').first()).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('text=Cleared').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('displays scammers table with phone numbers', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Should show phone number from mock (+1234567890)
|
|
await expect(page.locator('text=/\\+\\d+/').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('shows status badges', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for status indicators (suspected, confirmed, cleared)
|
|
await expect(page.locator('text=/suspected|confirmed|cleared/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('shows risk score visualization', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Should show risk score from mock (75)
|
|
await expect(page.locator('text=/75|score/i').first()).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
test('shows detection count badges', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for AI detection, manipulation, phishing counts
|
|
await expect(page.locator('text=/ai|detection|manipulation|phishing/i').first()).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
});
|
|
|
|
test('shows action buttons for suspected entries', async ({ page }) => {
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Should have Confirm button
|
|
const confirmBtn = page.locator('button:has-text("Confirm")').first();
|
|
await expect(confirmBtn).toBeVisible({ timeout: 10000 });
|
|
|
|
// Should have Clear button
|
|
const clearBtn = page.locator('button:has-text("Clear")').first();
|
|
await expect(clearBtn).toBeVisible({ timeout: 10000 });
|
|
});
|
|
});
|
|
|
|
test.describe('Conversation Assistant - Training Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await applyConversationMocks(page);
|
|
});
|
|
|
|
test('renders training page or maintenance mode', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
const mainContent = page.locator('main');
|
|
|
|
// Should display either:
|
|
// 1. Training page header, OR
|
|
// 2. Maintenance mode message (since route-health marks it as isMaintenancePage: true)
|
|
const hasTrainingHeader = await mainContent
|
|
.locator('h1')
|
|
.filter({ hasText: /training/i })
|
|
.count();
|
|
const hasMaintenanceMessage = await page.locator('text=/maintenance/i').count();
|
|
|
|
expect(hasTrainingHeader + hasMaintenanceMessage).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('displays training samples table if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check if we can see training samples section
|
|
const hasSamplesSection =
|
|
(await page.locator('text=/sample/i').first().isVisible({ timeout: 5000 }).catch(() => false)) ||
|
|
(await page.locator('text=/maintenance/i').isVisible({ timeout: 5000 }));
|
|
|
|
expect(hasSamplesSection).toBeTruthy();
|
|
});
|
|
|
|
test('displays training sample context from mocks if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Try to find sample context text ("How are you?") or maintenance message
|
|
const hasSampleText =
|
|
(await page.locator('text=/How are you/i').count()) > 0 ||
|
|
(await page.locator('text=/maintenance/i').count()) > 0;
|
|
|
|
expect(hasSampleText).toBeTruthy();
|
|
});
|
|
|
|
test('shows approve/reject buttons for samples if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for action buttons or maintenance message
|
|
const hasApproveBtn =
|
|
(await page.locator('button:has-text("Approve")').count()) > 0 ||
|
|
(await page.locator('text=/maintenance/i').count()) > 0;
|
|
|
|
expect(hasApproveBtn).toBeTruthy();
|
|
});
|
|
|
|
test('displays training jobs table if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check if we can see training jobs or maintenance
|
|
const hasJobsSection =
|
|
(await page.locator('text=/job|epoch/i').first().isVisible({ timeout: 5000 }).catch(() => false)) ||
|
|
(await page.locator('text=/maintenance/i').isVisible({ timeout: 5000 }));
|
|
|
|
expect(hasJobsSection).toBeTruthy();
|
|
});
|
|
|
|
test('shows job status from mocks if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for job statuses (completed, training, queued) or maintenance
|
|
const hasJobStatus =
|
|
(await page.locator('text=/completed|training|queued/i').count()) > 0 ||
|
|
(await page.locator('text=/maintenance/i').count()) > 0;
|
|
|
|
expect(hasJobStatus).toBeTruthy();
|
|
});
|
|
|
|
test('displays ML models section if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Check if we can see ML models or maintenance
|
|
const hasModelsSection =
|
|
(await page.locator('text=/model|ministral|qwen/i').first().isVisible({ timeout: 5000 }).catch(() => false)) ||
|
|
(await page.locator('text=/maintenance/i').isVisible({ timeout: 5000 }));
|
|
|
|
expect(hasModelsSection).toBeTruthy();
|
|
});
|
|
|
|
test('shows model loaded status from mocks if not in maintenance', async ({ page }) => {
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.waitForTimeout(500);
|
|
|
|
// Look for model names (ministral-3b-instruct, qwen2.5:3b-instruct) or maintenance
|
|
const hasModelInfo =
|
|
(await page.locator('text=/ministral|qwen/i').count()) > 0 ||
|
|
(await page.locator('text=/maintenance/i').count()) > 0;
|
|
|
|
expect(hasModelInfo).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
test.describe('Conversation Assistant - Route Health', () => {
|
|
test('scammers route is accessible', async ({ page }) => {
|
|
await applyConversationMocks(page);
|
|
await page.goto('/scammers');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should not show 404 page
|
|
await expect(page.getByRole('heading', { name: /404/ })).not.toBeVisible();
|
|
|
|
// Should show scammers content
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent).toBeVisible();
|
|
});
|
|
|
|
test('training route is accessible', async ({ page }) => {
|
|
await applyConversationMocks(page);
|
|
await page.goto('/training');
|
|
await page.waitForLoadState('networkidle');
|
|
|
|
// Should not show 404 page
|
|
await expect(page.getByRole('heading', { name: /404/ })).not.toBeVisible();
|
|
|
|
// Should show training content or maintenance
|
|
const mainContent = page.locator('main');
|
|
await expect(mainContent).toBeVisible();
|
|
});
|
|
});
|