Files
multiAgentTry/frontend/tests/e2e-workflow.spec.ts
Claude Code dc398d7c7b 完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py)
- 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py)
- 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py)
- 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py)
- 新增进程管理器 (backend/app/services/process_manager.py)
- 新增 Agent 控制 API (backend/app/routers/agents_control.py)
- 新增 WebSocket 实时通信 (backend/app/routers/websocket.py)
- 更新前端 AgentsPage,支持启动/停止 Agent
- 测试通过:Agent 启动、批量操作、栅栏同步

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:32:11 +08:00

268 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect, Page } from '@playwright/test';
/**
* Swarm Command Center - 端到端工作流测试
* 测试完整用户场景和交互流程
*/
// 辅助函数:等待页面加载完成
async function waitForPageLoad(page: Page) {
await page.waitForLoadState('networkidle');
await page.waitForSelector('text=Swarm Center', { state: 'visible' });
}
// 辅助函数:导航到指定页面
async function navigateTo(page: Page, menuText: string) {
await page.click(`text=${menuText}`);
await page.waitForLoadState('networkidle');
}
test.describe('端到端工作流测试', () => {
const baseUrl = 'http://localhost:3000';
test.beforeEach(async ({ page }) => {
await page.goto(baseUrl);
await waitForPageLoad(page);
});
test('完整用户旅程 - 从仪表盘到各功能模块', async ({ page }) => {
// 1. 验证仪表盘首页
await expect(page.locator('h1:has-text("仪表盘")')).toBeVisible();
await expect(page.locator('text=系统概览与实时监控')).toBeVisible();
// 2. 点击统计卡片导航到 Agent 管理
await page.click('text=Agent 总数');
await expect(page).toHaveURL(/\/agents/);
await expect(page.locator('h1:has-text("Agent 管理")')).toBeVisible();
// 3. 返回仪表盘
await navigateTo(page, '仪表盘');
await expect(page).toHaveURL(/\/$/);
// 4. 点击会议统计卡片
await page.click('text=今日会议');
await expect(page).toHaveURL(/\/meetings/);
await expect(page.locator('h1:has-text("会议管理")')).toBeVisible();
});
test('Agent 管理 - 完整 CRUD 流程', async ({ page }) => {
await navigateTo(page, 'Agent 管理');
// 1. 检查页面元素
await expect(page.locator('button:has-text("注册 Agent")')).toBeVisible();
await expect(page.locator('input[placeholder="搜索 Agent..."]')).toBeVisible();
// 2. 打开注册对话框
await page.click('button:has-text("注册 Agent")');
await expect(page.locator('text=注册新 Agent')).toBeVisible();
// 3. 填写表单
const testAgentName = `TestAgent-${Date.now()}`;
await page.fill('input[name="name"]', testAgentName);
await page.fill('input[name="model"]', 'gpt-4');
await page.selectOption('select[name="role"]', 'developer');
await page.fill('textarea[name="description"]', 'Playwright 端到端测试用 Agent');
// 4. 验证表单填写成功
await expect(page.locator('input[name="name"]')).toHaveValue(testAgentName);
await expect(page.locator('select[name="role"]')).toHaveValue('developer');
// 5. 关闭对话框(取消提交,避免影响实际数据)
await page.click('button:has-text("取消")');
await expect(page.locator('text=注册新 Agent')).not.toBeVisible();
// 6. 测试搜索功能
await page.fill('input[placeholder="搜索 Agent..."]', 'nonexistent');
await expect(page.locator('text=没有找到匹配的 Agent')).toBeVisible();
});
test('会议管理 - 创建会议流程', async ({ page }) => {
await navigateTo(page, '会议管理');
// 1. 检查页面元素
await expect(page.locator('button:has-text("创建会议")')).toBeVisible();
// 2. 打开创建会议对话框
await page.click('button:has-text("创建会议")');
await expect(page.locator('text=创建新会议')).toBeVisible();
// 3. 填写会议表单
const testMeetingTitle = `TestMeeting-${Date.now()}`;
await page.fill('input[name="title"]', testMeetingTitle);
await page.fill('textarea[name="agenda"]', '测试议程内容\n1. 测试项1\n2. 测试项2');
await page.selectOption('select[name="type"]', 'design_review');
// 4. 添加参与者
await page.click('button:has-text("添加参与者")');
// 注:实际实现可能需要选择具体的 Agent
// 5. 验证表单填写
await expect(page.locator('input[name="title"]')).toHaveValue(testMeetingTitle);
// 6. 关闭对话框
await page.click('button:has-text("取消")');
await expect(page.locator('text=创建新会议')).not.toBeVisible();
});
test('资源监控 - 切换标签页和数据展示', async ({ page }) => {
await navigateTo(page, '资源监控');
// 1. 验证标签页存在
const tabs = ['文件锁', '心跳状态', 'Agent 状态'];
for (const tab of tabs) {
await expect(page.locator(`button:has-text("${tab}")`)).toBeVisible();
}
// 2. 切换标签页
await page.click('button:has-text("心跳状态")');
await page.waitForTimeout(300); // 等待切换动画
await page.click('button:has-text("Agent 状态")');
await page.waitForTimeout(300);
await page.click('button:has-text("文件锁")');
await page.waitForTimeout(300);
// 3. 验证刷新按钮
await expect(page.locator('button:has-text("刷新")')).toBeVisible();
await page.click('button:has-text("刷新")');
await expect(page.locator('text=加载中...')).not.toBeVisible();
});
test('工作流页面 - 工作流操作', async ({ page }) => {
await navigateTo(page, '工作流');
// 1. 验证页面元素
await expect(page.locator('h1:has-text("工作流")')).toBeVisible();
await expect(page.locator('button:has-text("新建工作流")')).toBeVisible();
// 2. 打开新建工作流对话框
await page.click('button:has-text("新建工作流")');
await expect(page.locator('text=新建工作流')).toBeVisible();
// 3. 填写工作流名称
const workflowName = `TestWorkflow-${Date.now()}`;
await page.fill('input[name="name"]', workflowName);
// 4. 关闭对话框
await page.click('button:has-text("取消")');
});
test('系统配置 - 保存设置', async ({ page }) => {
await navigateTo(page, '配置');
// 1. 验证页面元素
await expect(page.locator('h1:has-text("系统配置")')).toBeVisible();
await expect(page.locator('text=API 基础地址')).toBeVisible();
// 2. 修改配置
await page.fill('input[name="apiBaseUrl"]', 'http://localhost:9000');
await page.fill('input[name="heartbeatInterval"]', '15');
// 3. 保存配置
await page.click('button:has-text("保存配置")');
// 4. 验证成功提示
await expect(page.locator('text=配置已保存')).toBeVisible();
// 5. 恢复原始配置
await page.fill('input[name="apiBaseUrl"]', 'http://localhost:8000');
await page.fill('input[name="heartbeatInterval"]', '30');
await page.click('button:has-text("保存配置")');
});
test('响应式布局 - 侧边栏折叠展开', async ({ page }) => {
// 1. 验证侧边栏默认展开
await expect(page.locator('text=仪表盘')).toBeVisible();
await expect(page.locator('text=Agent 管理')).toBeVisible();
// 2. 测试移动端视图(如果支持)
await page.setViewportSize({ width: 375, height: 667 });
await page.waitForTimeout(500);
// 3. 恢复桌面视图
await page.setViewportSize({ width: 1280, height: 720 });
await page.waitForTimeout(500);
// 4. 验证侧边栏仍然可见
await expect(page.locator('text=仪表盘')).toBeVisible();
});
});
test.describe('错误处理和边界情况', () => {
const baseUrl = 'http://localhost:3000';
test('后端连接失败时的错误显示', async ({ page }) => {
// 模拟后端不可用的情况
await page.route('http://localhost:8000/api/**', (route) => {
route.abort('failed');
});
await page.goto(baseUrl);
await waitForPageLoad(page);
// 验证错误提示显示
await expect(page.locator('text=连接后端失败')).toBeVisible();
});
test('404 页面处理', async ({ page }) => {
await page.goto(`${baseUrl}/nonexistent-page`);
await waitForPageLoad(page);
// 应该重定向到首页
await expect(page).toHaveURL(/\/$/);
await expect(page.locator('h1:has-text("仪表盘")')).toBeVisible();
});
test('快速导航切换', async ({ page }) => {
await page.goto(baseUrl);
await waitForPageLoad(page);
// 快速切换多个页面
const pages = ['Agent 管理', '会议管理', '资源监控', '工作流', '配置'];
for (const pageName of pages) {
await navigateTo(page, pageName);
}
// 最后回到仪表盘
await navigateTo(page, '仪表盘');
await expect(page.locator('h1:has-text("仪表盘")')).toBeVisible();
});
});
test.describe('性能和加载测试', () => {
const baseUrl = 'http://localhost:3000';
test('页面加载性能', async ({ page }) => {
const startTime = Date.now();
await page.goto(baseUrl);
await waitForPageLoad(page);
const loadTime = Date.now() - startTime;
// 验证页面加载时间在合理范围内5秒内
expect(loadTime).toBeLessThan(5000);
// 验证关键元素渲染
await expect(page.locator('h1:has-text("仪表盘")')).toBeVisible();
});
test('数据刷新功能', async ({ page }) => {
await page.goto(baseUrl);
await waitForPageLoad(page);
// 等待自动刷新间隔10秒
// 这里我们只是验证初始加载成功
await expect(page.locator('text=自动刷新')).toBeVisible();
await expect(page.locator('text=10 秒')).toBeVisible();
});
test('大量数据渲染性能', async ({ page }) => {
// 这个测试需要后端返回大量数据
// 目前只是验证页面结构可以支持列表渲染
await navigateTo(page, 'Agent 管理');
// 验证列表容器存在
await expect(page.locator('[data-testid="agent-list"]').or(page.locator('text=暂无 Agent 数据'))).toBeVisible();
});
});