Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Full E2E test suite for agent chat functionality including: - Message send/receive flow - Streaming response verification - Model switching behavior - Error handling scenarios - Multi-turn conversation context Includes test report documenting coverage and known gaps.
1016 lines
31 KiB
TypeScript
1016 lines
31 KiB
TypeScript
/**
|
||
* Agent 对话功能全面验证测试
|
||
*
|
||
* 测试范围:
|
||
* 1. Agent 对话初始化
|
||
* 2. 消息发送与接收
|
||
* 3. 对话历史记录
|
||
* 4. 上下文保持
|
||
* 5. 功能按钮交互
|
||
* 6. 异常状态处理
|
||
* 7. 边界条件测试
|
||
* 8. 异常输入测试
|
||
* 9. 错误流程测试
|
||
*
|
||
* 使用 Chrome DevTools Protocol 进行深度验证
|
||
*/
|
||
|
||
import { test, expect, Page, CDPSession } from '@playwright/test';
|
||
import { execSync } from 'child_process';
|
||
|
||
// 测试配置
|
||
const TEST_CONFIG = {
|
||
baseURL: 'http://localhost:3000',
|
||
timeouts: {
|
||
navigation: 30000,
|
||
element: 10000,
|
||
message: 60000,
|
||
stream: 120000,
|
||
},
|
||
};
|
||
|
||
// 测试数据
|
||
const TEST_MESSAGES = {
|
||
normal: [
|
||
'你好,请介绍一下自己',
|
||
'你能帮我做什么?',
|
||
'请用 Python 写一个快速排序算法',
|
||
],
|
||
boundary: {
|
||
empty: '',
|
||
whitespace: ' ',
|
||
long: 'a'.repeat(5000),
|
||
special: '!@#$%^&*()_+-=[]{}|;\':",./<>?',
|
||
multiline: '第一行\n第二行\n第三行',
|
||
unicode: '你好世界 🌍 Привет мир こんにちは世界',
|
||
code: '```python\ndef hello():\n print("Hello")\n```',
|
||
},
|
||
edge: {
|
||
sqlInjection: "'; DROP TABLE users; --",
|
||
xss: '<script>alert("xss")</script>',
|
||
json: '{"key": "value", "nested": {"array": [1,2,3]}}',
|
||
xml: '<?xml version="1.0"?><root><item>test</item></root>',
|
||
},
|
||
};
|
||
|
||
// 辅助函数:等待指定时间
|
||
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||
|
||
// 辅助函数:获取 CDP Session
|
||
async function getCDPSession(page: Page): Promise<CDPSession> {
|
||
return await page.context().newCDPSession(page);
|
||
}
|
||
|
||
// 辅助函数:清除浏览器存储
|
||
async function clearBrowserStorage(page: Page) {
|
||
await page.evaluate(() => {
|
||
localStorage.clear();
|
||
sessionStorage.clear();
|
||
indexedDB.databases().then(dbs => {
|
||
dbs.forEach(db => {
|
||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
// 辅助函数:获取控制台日志
|
||
async function getConsoleLogs(page: Page): Promise<string[]> {
|
||
const logs: string[] = [];
|
||
page.on('console', msg => {
|
||
logs.push(`[${msg.type()}] ${msg.text()}`);
|
||
});
|
||
return logs;
|
||
}
|
||
|
||
// 辅助函数:获取网络请求
|
||
async function getNetworkRequests(page: Page): Promise<any[]> {
|
||
const requests: any[] = [];
|
||
page.on('request', request => {
|
||
requests.push({
|
||
url: request.url(),
|
||
method: request.method(),
|
||
headers: request.headers(),
|
||
});
|
||
});
|
||
return requests;
|
||
}
|
||
|
||
test.describe('Agent 对话功能全面验证', () => {
|
||
let page: Page;
|
||
let cdp: CDPSession;
|
||
let consoleLogs: string[] = [];
|
||
let networkRequests: any[] = [];
|
||
|
||
test.beforeEach(async ({ browser }) => {
|
||
// 创建新页面
|
||
const context = await browser.newContext({
|
||
viewport: { width: 1280, height: 720 },
|
||
deviceScaleFactor: 1,
|
||
});
|
||
page = await context.newPage();
|
||
|
||
// 获取 CDP Session
|
||
cdp = await getCDPSession(page);
|
||
|
||
// 启用 CDP 域
|
||
await cdp.send('Console.enable');
|
||
await cdp.send('Network.enable');
|
||
await cdp.send('Runtime.enable');
|
||
await cdp.send('Page.enable');
|
||
|
||
// 收集控制台日志
|
||
consoleLogs = [];
|
||
page.on('console', msg => {
|
||
consoleLogs.push(`[${msg.type()}] ${msg.text()}`);
|
||
});
|
||
|
||
// 收集网络请求
|
||
networkRequests = [];
|
||
page.on('request', request => {
|
||
networkRequests.push({
|
||
url: request.url(),
|
||
method: request.method(),
|
||
timestamp: Date.now(),
|
||
});
|
||
});
|
||
|
||
// 收集页面错误
|
||
page.on('pageerror', error => {
|
||
consoleLogs.push(`[PAGE_ERROR] ${error.message}`);
|
||
});
|
||
|
||
// 清除存储并导航到应用
|
||
await clearBrowserStorage(page);
|
||
await page.goto(TEST_CONFIG.baseURL, {
|
||
timeout: TEST_CONFIG.timeouts.navigation,
|
||
waitUntil: 'networkidle',
|
||
});
|
||
|
||
// 等待应用初始化
|
||
await wait(2000);
|
||
});
|
||
|
||
test.afterEach(async () => {
|
||
// 输出收集的日志
|
||
if (consoleLogs.length > 0) {
|
||
console.log('\n=== 控制台日志 ===');
|
||
consoleLogs.forEach(log => console.log(log));
|
||
}
|
||
|
||
if (networkRequests.length > 0) {
|
||
console.log('\n=== 网络请求 ===');
|
||
console.log(`总请求数: ${networkRequests.length}`);
|
||
}
|
||
|
||
await page.close();
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 1: Agent 对话初始化
|
||
// ============================================
|
||
test.describe('1. Agent 对话初始化', () => {
|
||
test('1.1 页面加载后应显示初始界面', async () => {
|
||
// 验证页面标题
|
||
const title = await page.title();
|
||
expect(title).toBeTruthy();
|
||
|
||
// 验证主要元素存在
|
||
const chatArea = await page.locator('[data-testid="chat-area"], .chat-area, [class*="ChatArea"]').first();
|
||
const inputArea = await page.locator('textarea, input[type="text"], [data-testid="chat-input"]').first();
|
||
|
||
// 截图记录初始状态
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-init.png',
|
||
fullPage: true,
|
||
});
|
||
|
||
// 验证至少有一个输入区域
|
||
const hasInput = await inputArea.isVisible().catch(() => false);
|
||
expect(hasInput || await chatArea.isVisible().catch(() => false)).toBeTruthy();
|
||
});
|
||
|
||
test('1.2 应正确加载默认 Agent 配置', async () => {
|
||
// 使用 CDP 检查 localStorage
|
||
const localStorage = await cdp.send('Runtime.evaluate', {
|
||
expression: 'JSON.stringify(localStorage)',
|
||
});
|
||
|
||
console.log('LocalStorage 内容:', localStorage.result.value);
|
||
|
||
// 检查 IndexedDB
|
||
const indexedDBData = await cdp.send('Runtime.evaluate', {
|
||
expression: `
|
||
(async () => {
|
||
const dbs = await indexedDB.databases();
|
||
return JSON.stringify(dbs.map(db => db.name));
|
||
})()
|
||
`,
|
||
awaitPromise: true,
|
||
});
|
||
|
||
console.log('IndexedDB 数据库:', indexedDBData.result.value);
|
||
});
|
||
|
||
test('1.3 网络连接状态应正确显示', async () => {
|
||
// 检查连接状态指示器
|
||
const connectionIndicators = await page.locator(
|
||
'[data-testid="connection-status"], .connection-status, [class*="Connection"], [class*="connection"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${connectionIndicators.length} 个连接状态指示器`);
|
||
|
||
// 验证没有连接错误
|
||
const errorLogs = consoleLogs.filter(log =>
|
||
log.includes('error') || log.includes('Error') || log.includes('failed')
|
||
);
|
||
|
||
// 允许初始连接尝试的错误
|
||
const criticalErrors = errorLogs.filter(log =>
|
||
!log.includes('WebSocket') && !log.includes('connection')
|
||
);
|
||
|
||
expect(criticalErrors.length).toBeLessThan(5);
|
||
});
|
||
|
||
test('1.4 首次加载应显示欢迎界面或引导', async () => {
|
||
// 检查是否有欢迎消息或引导
|
||
const welcomeElements = await page.locator(
|
||
'text=/欢迎|Welcome|开始|Start|你好|Hello/i'
|
||
).all();
|
||
|
||
console.log(`找到 ${welcomeElements.length} 个欢迎元素`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-welcome.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 2: 消息发送与接收
|
||
// ============================================
|
||
test.describe('2. 消息发送与接收', () => {
|
||
test('2.1 正常消息发送应成功', async () => {
|
||
const testMessage = TEST_MESSAGES.normal[0];
|
||
|
||
// 找到输入框
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(testMessage);
|
||
|
||
// 找到发送按钮
|
||
const sendButton = await page.locator(
|
||
'button[type="submit"], button:has-text("发送"), button:has-text("Send"), [data-testid="send-button"]'
|
||
).first();
|
||
|
||
// 点击发送或按回车
|
||
if (await sendButton.isVisible().catch(() => false)) {
|
||
await sendButton.click();
|
||
} else {
|
||
await input.press('Enter');
|
||
}
|
||
|
||
// 等待消息发送
|
||
await wait(1000);
|
||
|
||
// 验证消息出现在对话中
|
||
const messages = await page.locator(
|
||
'[data-testid="message"], .message, [class*="Message"], [class*="message"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${messages.length} 条消息`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-message-sent.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('2.2 流式响应应正确显示', async () => {
|
||
const testMessage = '请详细介绍一下人工智能的发展历程';
|
||
|
||
// 发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(testMessage);
|
||
await input.press('Enter');
|
||
|
||
// 等待流式响应开始
|
||
await wait(2000);
|
||
|
||
// 使用 CDP 监控 DOM 变化
|
||
const initialDOM = await cdp.send('Runtime.evaluate', {
|
||
expression: 'document.body.innerHTML.length',
|
||
});
|
||
|
||
console.log('初始 DOM 长度:', initialDOM.result.value);
|
||
|
||
// 等待一段时间观察流式更新
|
||
await wait(5000);
|
||
|
||
const updatedDOM = await cdp.send('Runtime.evaluate', {
|
||
expression: 'document.body.innerHTML.length',
|
||
});
|
||
|
||
console.log('更新后 DOM 长度:', updatedDOM.result.value);
|
||
|
||
// 截图记录流式响应状态
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-streaming.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('2.3 多轮对话应保持上下文', async () => {
|
||
const messages = [
|
||
'我的名字叫张三',
|
||
'我叫什么名字?',
|
||
'请用我的名字写一首短诗',
|
||
];
|
||
|
||
for (const message of messages) {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(message);
|
||
await input.press('Enter');
|
||
|
||
// 等待响应
|
||
await wait(3000);
|
||
|
||
// 截图记录每轮对话
|
||
await page.screenshot({
|
||
path: `test-results/agent-chat-context-${messages.indexOf(message)}.png`,
|
||
fullPage: true,
|
||
});
|
||
}
|
||
|
||
// 验证所有消息都存在于对话中
|
||
const allMessages = await page.locator(
|
||
'[data-testid="message"], .message, [class*="Message"]'
|
||
).all();
|
||
|
||
console.log(`对话中共有 ${allMessages.length} 条消息`);
|
||
expect(allMessages.length).toBeGreaterThanOrEqual(messages.length * 2);
|
||
});
|
||
|
||
test('2.4 代码块应正确渲染', async () => {
|
||
const codeMessage = '请写一个 Python 函数来计算斐波那契数列';
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(codeMessage);
|
||
await input.press('Enter');
|
||
|
||
// 等待响应
|
||
await wait(8000);
|
||
|
||
// 检查代码块
|
||
const codeBlocks = await page.locator(
|
||
'pre, code, [class*="code"], [class*="Code"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${codeBlocks.length} 个代码块元素`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-code-block.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 3: 对话历史记录
|
||
// ============================================
|
||
test.describe('3. 对话历史记录', () => {
|
||
test('3.1 对话列表应正确显示', async () => {
|
||
// 查找对话列表
|
||
const conversationList = await page.locator(
|
||
'[data-testid="conversation-list"], .conversation-list, [class*="Conversation"], [class*="conversation"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${conversationList.length} 个对话列表元素`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-conversation-list.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('3.2 新建对话应创建新会话', async () => {
|
||
// 查找新建对话按钮
|
||
const newChatButton = await page.locator(
|
||
'button:has-text("新对话"), button:has-text("New Chat"), [data-testid="new-conversation"], button:has([svg])'
|
||
).first();
|
||
|
||
if (await newChatButton.isVisible().catch(() => false)) {
|
||
await newChatButton.click();
|
||
await wait(1000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-new-conversation.png',
|
||
fullPage: true,
|
||
});
|
||
}
|
||
});
|
||
|
||
test('3.3 切换对话应加载正确内容', async () => {
|
||
// 先发送一条消息创建对话
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('这是测试对话 1');
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-switch-conversation.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('3.4 对话标题应正确生成', async () => {
|
||
// 发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('讨论机器学习算法');
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 检查对话标题
|
||
const titles = await page.locator(
|
||
'[class*="title"], [class*="Title"], h1, h2, h3'
|
||
).allInnerTexts();
|
||
|
||
console.log('找到的对话标题:', titles);
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 4: 上下文保持
|
||
// ============================================
|
||
test.describe('4. 上下文保持', () => {
|
||
test('4.1 页面刷新后应恢复对话状态', async () => {
|
||
// 发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('记住这个数字:42');
|
||
await input.press('Enter');
|
||
await wait(3000);
|
||
|
||
// 刷新页面
|
||
await page.reload({ waitUntil: 'networkidle' });
|
||
await wait(2000);
|
||
|
||
// 验证对话恢复
|
||
const messages = await page.locator(
|
||
'[data-testid="message"], .message, [class*="Message"]'
|
||
).all();
|
||
|
||
console.log(`刷新后找到 ${messages.length} 条消息`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-refresh-recovery.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('4.2 长对话上下文应正确处理', async () => {
|
||
// 发送多条消息建立长对话
|
||
for (let i = 0; i < 5; i++) {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(`消息 ${i + 1}: 这是测试消息`);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
}
|
||
|
||
// 询问关于之前的内容
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('我发送的第一条消息是什么?');
|
||
await input.press('Enter');
|
||
await wait(5000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-long-context.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 5: 功能按钮交互
|
||
// ============================================
|
||
test.describe('5. 功能按钮交互', () => {
|
||
test('5.1 聊天模式切换应正常工作', async () => {
|
||
// 查找模式切换按钮
|
||
const modeButtons = await page.locator(
|
||
'button:has-text("闪速"), button:has-text("思考"), button:has-text("Pro"), button:has-text("Ultra"), [data-testid="chat-mode"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${modeButtons.length} 个模式按钮`);
|
||
|
||
if (modeButtons.length > 0) {
|
||
await modeButtons[0].click();
|
||
await wait(500);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-mode-switch.png',
|
||
fullPage: true,
|
||
});
|
||
}
|
||
});
|
||
|
||
test('5.2 文件上传按钮应可点击', async () => {
|
||
// 查找文件上传按钮
|
||
const fileButton = await page.locator(
|
||
'button:has([name="paperclip"]), button:has-text("附件"), [data-testid="file-upload"], input[type="file"]'
|
||
).first();
|
||
|
||
const isVisible = await fileButton.isVisible().catch(() => false);
|
||
console.log('文件上传按钮可见:', isVisible);
|
||
|
||
if (isVisible) {
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-file-button.png',
|
||
fullPage: true,
|
||
});
|
||
}
|
||
});
|
||
|
||
test('5.3 停止生成按钮应在流式响应时显示', async () => {
|
||
// 发送长消息触发流式响应
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('请详细解释量子计算的原理和应用,包括量子比特、量子纠缠等概念');
|
||
await input.press('Enter');
|
||
|
||
// 等待流式响应开始
|
||
await wait(1000);
|
||
|
||
// 查找停止按钮
|
||
const stopButton = await page.locator(
|
||
'button:has-text("停止"), button:has-text("Stop"), [data-testid="stop-generation"], button:has([name="square"])'
|
||
).first();
|
||
|
||
const isVisible = await stopButton.isVisible().catch(() => false);
|
||
console.log('停止按钮可见:', isVisible);
|
||
|
||
if (isVisible) {
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-stop-button.png',
|
||
fullPage: true,
|
||
});
|
||
}
|
||
});
|
||
|
||
test('5.4 重新生成按钮应在响应完成后显示', async () => {
|
||
// 发送消息并等待完成
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('你好');
|
||
await input.press('Enter');
|
||
await wait(5000);
|
||
|
||
// 查找重新生成按钮
|
||
const regenerateButton = await page.locator(
|
||
'button:has-text("重新生成"), button:has-text("Regenerate"), [data-testid="regenerate"], button:has([name="refresh"])'
|
||
).first();
|
||
|
||
const isVisible = await regenerateButton.isVisible().catch(() => false);
|
||
console.log('重新生成按钮可见:', isVisible);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-regenerate-button.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 6: 异常状态处理
|
||
// ============================================
|
||
test.describe('6. 异常状态处理', () => {
|
||
test('6.1 网络断开时应显示离线提示', async () => {
|
||
// 模拟网络断开
|
||
await cdp.send('Network.emulateNetworkConditions', {
|
||
offline: true,
|
||
latency: 0,
|
||
downloadThroughput: 0,
|
||
uploadThroughput: 0,
|
||
});
|
||
|
||
await wait(1000);
|
||
|
||
// 检查离线提示
|
||
const offlineIndicator = await page.locator(
|
||
'text=/离线|Offline|网络|Network/i, [class*="offline"], [class*="Offline"]'
|
||
).first();
|
||
|
||
const isVisible = await offlineIndicator.isVisible().catch(() => false);
|
||
console.log('离线提示可见:', isVisible);
|
||
|
||
// 恢复网络
|
||
await cdp.send('Network.emulateNetworkConditions', {
|
||
offline: false,
|
||
latency: 0,
|
||
downloadThroughput: -1,
|
||
uploadThroughput: -1,
|
||
});
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-offline.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('6.2 发送空消息应被阻止或提示', async () => {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
|
||
// 尝试发送空消息
|
||
await input.fill('');
|
||
await input.press('Enter');
|
||
|
||
await wait(500);
|
||
|
||
// 检查是否有错误提示
|
||
const errorMessages = await page.locator(
|
||
'text=/不能为空|Cannot be empty|请输入|Please enter/i, [class*="error"], [class*="Error"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${errorMessages.length} 个错误提示`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-empty-message.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('6.3 快速连续发送应被正确处理', async () => {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
|
||
// 快速发送多条消息
|
||
for (let i = 0; i < 5; i++) {
|
||
await input.fill(`快速消息 ${i + 1}`);
|
||
await input.press('Enter');
|
||
}
|
||
|
||
await wait(2000);
|
||
|
||
// 检查消息数量
|
||
const messages = await page.locator(
|
||
'[data-testid="message"], .message, [class*="Message"]'
|
||
).all();
|
||
|
||
console.log(`快速发送后找到 ${messages.length} 条消息`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-rapid-send.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 7: 边界条件测试
|
||
// ============================================
|
||
test.describe('7. 边界条件测试', () => {
|
||
test('7.1 超长消息应被正确处理', async () => {
|
||
const longMessage = TEST_MESSAGES.boundary.long;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(longMessage);
|
||
|
||
// 检查输入框内容
|
||
const inputValue = await input.inputValue();
|
||
console.log(`输入框内容长度: ${inputValue.length}`);
|
||
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-long-message.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('7.2 特殊字符应正确显示', async () => {
|
||
const specialMessage = TEST_MESSAGES.boundary.special;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(specialMessage);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-special-chars.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('7.3 Unicode 字符应正确显示', async () => {
|
||
const unicodeMessage = TEST_MESSAGES.boundary.unicode;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(unicodeMessage);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-unicode.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('7.4 多行消息应正确渲染', async () => {
|
||
const multilineMessage = TEST_MESSAGES.boundary.multiline;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(multilineMessage);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-multiline.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 8: 异常输入测试
|
||
// ============================================
|
||
test.describe('8. 异常输入测试', () => {
|
||
test('8.1 SQL 注入尝试应被安全处理', async () => {
|
||
const sqlInjection = TEST_MESSAGES.edge.sqlInjection;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(sqlInjection);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 检查控制台是否有错误
|
||
const securityErrors = consoleLogs.filter(log =>
|
||
log.includes('SQL') || log.includes('injection') || log.includes('security')
|
||
);
|
||
|
||
console.log('安全相关日志:', securityErrors);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-sql-injection.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('8.2 XSS 尝试应被安全处理', async () => {
|
||
const xssAttempt = TEST_MESSAGES.edge.xss;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(xssAttempt);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 检查页面是否执行了脚本
|
||
const alertTriggered = consoleLogs.some(log =>
|
||
log.includes('alert') || log.includes('xss')
|
||
);
|
||
|
||
expect(alertTriggered).toBe(false);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-xss.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('8.3 JSON 数据应被正确格式化', async () => {
|
||
const jsonMessage = TEST_MESSAGES.edge.json;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(jsonMessage);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-json.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('8.4 XML 数据应被正确处理', async () => {
|
||
const xmlMessage = TEST_MESSAGES.edge.xml;
|
||
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(xmlMessage);
|
||
await input.press('Enter');
|
||
await wait(2000);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-xml.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 9: 错误流程测试
|
||
// ============================================
|
||
test.describe('9. 错误流程测试', () => {
|
||
test('9.1 后端服务不可用时应有降级处理', async () => {
|
||
// 模拟后端服务不可用
|
||
await cdp.send('Network.setBlockedURLs', {
|
||
urls: ['*localhost*', '*127.0.0.1*'],
|
||
});
|
||
|
||
await wait(1000);
|
||
|
||
// 尝试发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('测试消息');
|
||
await input.press('Enter');
|
||
|
||
await wait(2000);
|
||
|
||
// 检查错误提示
|
||
const errorMessages = await page.locator(
|
||
'text=/错误|Error|失败|Failed|连接|Connection/i, [class*="error"], [class*="Error"]'
|
||
).all();
|
||
|
||
console.log(`找到 ${errorMessages.length} 个错误提示`);
|
||
|
||
// 恢复网络
|
||
await cdp.send('Network.setBlockedURLs', {
|
||
urls: [],
|
||
});
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-backend-down.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('9.2 超时情况应有正确处理', async () => {
|
||
// 发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('这是一个测试');
|
||
await input.press('Enter');
|
||
|
||
// 等待较长时间
|
||
await wait(10000);
|
||
|
||
// 检查是否有超时提示
|
||
const timeoutMessages = await page.locator(
|
||
'text=/超时|Timeout|等待|Waiting/i'
|
||
).all();
|
||
|
||
console.log(`找到 ${timeoutMessages.length} 个超时相关提示`);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-timeout.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
|
||
test('9.3 内存使用情况监控', async () => {
|
||
// 使用 CDP 获取内存信息
|
||
const memoryInfo = await cdp.send('Runtime.getHeapUsage');
|
||
console.log('堆内存使用:', memoryInfo);
|
||
|
||
// 发送多条消息
|
||
for (let i = 0; i < 10; i++) {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(`内存测试消息 ${i + 1}`);
|
||
await input.press('Enter');
|
||
await wait(1000);
|
||
}
|
||
|
||
// 再次获取内存信息
|
||
const memoryInfoAfter = await cdp.send('Runtime.getHeapUsage');
|
||
console.log('发送消息后堆内存使用:', memoryInfoAfter);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-memory.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
|
||
// ============================================
|
||
// 测试套件 10: 性能测试
|
||
// ============================================
|
||
test.describe('10. 性能测试', () => {
|
||
test('10.1 首屏加载时间', async () => {
|
||
// 使用 CDP 测量性能
|
||
const metrics = await cdp.send('Performance.getMetrics');
|
||
console.log('性能指标:', metrics);
|
||
|
||
// 使用 Navigation Timing API
|
||
const timing = await cdp.send('Runtime.evaluate', {
|
||
expression: `
|
||
JSON.stringify({
|
||
navigationStart: performance.timing.navigationStart,
|
||
loadEventEnd: performance.timing.loadEventEnd,
|
||
domContentLoaded: performance.timing.domContentLoadedEventEnd,
|
||
firstPaint: performance.getEntriesByType('paint')[0]?.startTime,
|
||
firstContentfulPaint: performance.getEntriesByType('paint')[1]?.startTime,
|
||
})
|
||
`,
|
||
});
|
||
|
||
console.log('页面加载时间:', timing.result.value);
|
||
});
|
||
|
||
test('10.2 消息渲染性能', async () => {
|
||
const startTime = Date.now();
|
||
|
||
// 发送消息
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill('性能测试消息');
|
||
await input.press('Enter');
|
||
|
||
// 等待响应
|
||
await wait(5000);
|
||
|
||
const endTime = Date.now();
|
||
console.log(`消息响应时间: ${endTime - startTime}ms`);
|
||
|
||
// 使用 CDP 获取渲染性能
|
||
const renderMetrics = await cdp.send('Runtime.evaluate', {
|
||
expression: `
|
||
JSON.stringify(performance.getEntriesByType('measure'))
|
||
`,
|
||
});
|
||
|
||
console.log('渲染性能指标:', renderMetrics.result.value);
|
||
});
|
||
|
||
test('10.3 大量消息滚动性能', async () => {
|
||
// 发送大量消息
|
||
for (let i = 0; i < 20; i++) {
|
||
const input = await page.locator('textarea, input[type="text"]').first();
|
||
await input.fill(`滚动性能测试消息 ${i + 1}`);
|
||
await input.press('Enter');
|
||
await wait(500);
|
||
}
|
||
|
||
// 滚动到顶部
|
||
await page.evaluate(() => {
|
||
window.scrollTo(0, 0);
|
||
});
|
||
|
||
await wait(500);
|
||
|
||
// 滚动到底部
|
||
await page.evaluate(() => {
|
||
window.scrollTo(0, document.body.scrollHeight);
|
||
});
|
||
|
||
await wait(500);
|
||
|
||
// 截图记录
|
||
await page.screenshot({
|
||
path: 'test-results/agent-chat-scroll-performance.png',
|
||
fullPage: true,
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
// 测试总结报告
|
||
test.describe('测试总结', () => {
|
||
test('生成测试报告', async () => {
|
||
console.log('\n');
|
||
console.log('==============================================');
|
||
console.log('Agent 对话功能全面验证测试完成');
|
||
console.log('==============================================');
|
||
console.log('测试范围:');
|
||
console.log(' 1. Agent 对话初始化');
|
||
console.log(' 2. 消息发送与接收');
|
||
console.log(' 3. 对话历史记录');
|
||
console.log(' 4. 上下文保持');
|
||
console.log(' 5. 功能按钮交互');
|
||
console.log(' 6. 异常状态处理');
|
||
console.log(' 7. 边界条件测试');
|
||
console.log(' 8. 异常输入测试');
|
||
console.log(' 9. 错误流程测试');
|
||
console.log(' 10. 性能测试');
|
||
console.log('==============================================');
|
||
});
|
||
});
|