/** * ZCLAW 功能验证脚本 - Playwright CLI (改进版) */ import { chromium } from 'playwright'; const BASE_URL = 'http://localhost:1420'; const SCREENSHOT_DIR = 'test-results/screenshots'; const results = { passed: [], failed: [], warnings: [], screenshots: [] }; function log(msg) { console.log(`[${new Date().toISOString().slice(11, 19)}] ${msg}`); } async function screenshot(page, name) { try { await page.screenshot({ path: `${SCREENSHOT_DIR}/${name}.png`, fullPage: true }); results.screenshots.push(name); log(`📸 ${name}.png`); } catch (e) { log(`⚠️ 截图失败: ${name}`); } } async function test(name, fn) { try { await fn(); results.passed.push(name); log(`✅ ${name}`); } catch (e) { results.failed.push({ name, error: e.message }); log(`❌ ${name} - ${e.message}`); } } async function main() { log('🚀 ZCLAW 功能验证测试 (改进版)'); log(`📍 ${BASE_URL}`); const browser = await chromium.launch({ headless: true }); const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } }); const logs = []; page.on('console', msg => logs.push(`[${msg.type()}] ${msg.text()}`)); page.on('pageerror', e => logs.push(`[error] ${e.message}`)); try { // === 套件 1: 应用启动 === log('\n📋 应用启动与初始化'); await test('1.1 应用加载', async () => { await page.goto(BASE_URL, { timeout: 10000 }); await page.waitForLoadState('domcontentloaded'); await page.waitForTimeout(2000); await screenshot(page, '01-app'); }); await test('1.2 左侧边栏', async () => { const sidebar = page.locator('aside').first(); if (!await sidebar.isVisible()) throw new Error('左侧边栏不可见'); await screenshot(page, '02-sidebar'); }); await test('1.3 主区域', async () => { if (!await page.locator('main').isVisible()) throw new Error('主区域不可见'); await screenshot(page, '03-main'); }); await test('1.4 右侧边栏', async () => { const rightPanel = page.locator('aside').last(); if (!await rightPanel.isVisible()) throw new Error('右侧边栏不可见'); await screenshot(page, '04-right-panel'); }); // === 套件 2: 标签导航 === log('\n📋 标签导航'); await test('2.1 分身标签', async () => { const tab = page.locator('button[role="tab"]').filter({ hasText: '分身' }); if (await tab.count() > 0) { await tab.first().click(); await page.waitForTimeout(500); await screenshot(page, 'tab-clones'); } else { throw new Error('分身标签不可见'); } }); await test('2.2 Hands 标签', async () => { const tab = page.locator('button[role="tab"]').filter({ hasText: 'Hands' }); if (await tab.count() > 0) { await tab.first().click(); await page.waitForTimeout(500); await screenshot(page, 'tab-hands'); } else { throw new Error('Hands 标签不可见'); } }); await test('2.3 工作流标签', async () => { const tab = page.locator('button[role="tab"]').filter({ hasText: '工作流' }); if (await tab.count() > 0) { await tab.first().click(); await page.waitForTimeout(500); await screenshot(page, 'tab-workflow'); } else { throw new Error('工作流标签不可见'); } }); await test('2.4 团队标签', async () => { const tab = page.locator('button[role="tab"]').filter({ hasText: '团队' }); if (await tab.count() > 0) { await tab.first().click(); await page.waitForTimeout(500); await screenshot(page, 'tab-team'); } else { throw new Error('团队标签不可见'); } }); await test('2.5 协作标签', async () => { const tab = page.locator('button[role="tab"]').filter({ hasText: '协作' }); if (await tab.count() > 0) { await tab.first().click(); await page.waitForTimeout(500); await screenshot(page, 'tab-swarm'); } else { throw new Error('协作标签不可见'); } }); // === 套件 3: Gateway 状态 === log('\n📋 Gateway 连接状态'); await test('3.1 连接状态显示', async () => { const content = await page.content(); if (content.includes('Gateway 未连接') || content.includes('Connecting')) { results.warnings.push('Gateway 未连接 - 需启动后端'); } await screenshot(page, '05-gateway'); }); // === 套件 4: 设置 === log('\n📋 设置页面'); await test('4.1 打开设置', async () => { const btn = page.getByRole('button', { name: /设置|Settings/i }); if (await btn.count() > 0) { await btn.first().click(); await page.waitForTimeout(500); await screenshot(page, '06-settings'); } else { throw new Error('设置按钮不可见'); } }); // === 套件 5: 聊天 === log('\n📋 聊天功能'); await test('5.1 聊天输入框', async () => { await page.goto(BASE_URL, { timeout: 10000 }); await page.waitForTimeout(1000); const input = page.locator('textarea'); if (await input.count() > 0) { if (await input.first().isDisabled()) { results.warnings.push('输入框已禁用 - 需 Gateway 连接'); } } else { throw new Error('聊天输入框不可见'); } await screenshot(page, '07-chat'); }); await test('5.2 模型选择器', async () => { const selector = page.locator('button').filter({ hasText: /模型|model/i }); if (await selector.count() > 0) { await selector.first().click(); await page.waitForTimeout(300); await screenshot(page, '08-model-selector'); } }); // === 套件 6: 控制台错误 === log('\n📋 控制台错误'); await test('6.1 JS 错误检查', async () => { const errors = logs.filter(l => l.includes('[error]')); const criticalErrors = errors.filter(e => !e.includes('DevTools') && !e.includes('extension') ); if (criticalErrors.length > 0) { results.warnings.push(`发现 ${criticalErrors.length} 个 JS 错误`); criticalErrors.slice(0, 5).forEach(e => log(` ${e.slice(0, 100)}...`)); } }); // === 套件 7: 响应式 === log('\n📋 响应式布局'); await test('7.1 移动端', async () => { await page.setViewportSize({ width: 375, height: 667 }); await page.waitForTimeout(500); await screenshot(page, '09-mobile'); }); await test('7.2 平板', async () => { await page.setViewportSize({ width: 768, height: 1024 }); await page.waitForTimeout(500); await screenshot(page, '10-tablet'); }); await test('7.3 桌面', async () => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.waitForTimeout(500); await screenshot(page, '11-desktop'); }); // === 套件 8: 性能 === log('\n📋 性能'); await test('8.1 加载时间', async () => { const start = Date.now(); await page.goto(BASE_URL, { timeout: 10000 }); const time = Date.now() - start; log(`加载时间: ${time}ms`); if (time > 5000) results.warnings.push(`加载时间较长: ${time}ms`); }); await test('8.2 DOM 数量', async () => { const count = await page.evaluate(() => document.querySelectorAll('*').length); log(`DOM 节点: ${count}`); if (count > 3000) results.warnings.push(`DOM 过多: ${count}`); }); } catch (e) { log(`❌ 执行出错: ${e.message}`); } finally { await browser.close(); } // 报告 console.log('\n' + '='.repeat(60)); console.log('📊 ZCLAW 功能验证报告'); console.log('='.repeat(60)); console.log(`\n✅ 通过: ${results.passed.length}`); results.passed.forEach(n => console.log(` - ${n}`)); console.log(`\n❌ 失败: ${results.failed.length}`); results.failed.forEach(f => console.log(` - ${f.name}: ${f.error}`)); console.log(`\n⚠️ 警告: ${results.warnings.length}`); results.warnings.forEach(w => console.log(` - ${w}`)); console.log(`\n📸 截图: ${results.screenshots.length} 张`); const total = results.passed.length + results.failed.length; const rate = total > 0 ? ((results.passed.length / total) * 100).toFixed(1) : 0; console.log(`\n总计: ${total} | 通过: ${results.passed.length} | 失败: ${results.failed.length} | 通过率: ${rate}%`); process.exit(results.failed.length > 0 ? 1 : 0); } main().catch(console.error);