import { test, expect } from '@playwright/test'
import path from 'path'
// Chrome扩展E2E测试
test.describe('SSP Chrome扩展 E2E测试', () => {
let extensionId: string
test.beforeAll(async ({ context }) => {
// 加载Chrome扩展
const pathToExtension = path.join(__dirname, '../../dist')
const [extension] = await Promise.all([
context.waitForEvent('page'), // 等待扩展页面打开
context.addInitScript(() => {
// 模拟Chrome扩展API
window.chrome = {
storage: {
local: {
get: (keys: any, callback: any) => {
const result = {
user_settings: {
defaultEngine: 'baidu',
language: 'zh-CN',
enableHistory: true,
theme: 'auto',
historyLimit: 1000,
autoOpenInNewTab: true
}
}
callback?.(result)
return Promise.resolve(result)
},
set: (items: any, callback?: any) => {
console.log('Chrome storage set:', items)
callback?.()
return Promise.resolve()
},
remove: (keys: any, callback?: any) => {
console.log('Chrome storage remove:', keys)
callback?.()
return Promise.resolve()
}
}
},
tabs: {
create: (options: any) => {
console.log('Chrome tabs create:', options)
return Promise.resolve({ id: 1 })
}
}
}
})
])
// 创建扩展页面
const page = await context.newPage()
await page.goto(`chrome-extension://${extensionId}/popup.html`)
// 如果没有实��扩展ID,使用本地HTML文件
try {
await page.goto('http://localhost:3000/popup.html')
} catch {
// 如果无法访问,直接测试组件
await page.setContent(`
SSP智能搜索
`)
}
})
test('应该正确加载扩展界面', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 检查页面标题
await expect(page).toHaveTitle(/SSP智能搜索/)
// 检查主要元素存在
await expect(page.locator('h1')).toContainText('智能搜索')
await expect(page.locator('input[placeholder*="关键词"]')).toBeVisible()
await expect(page.locator('select[name="engine"]')).toBeVisible()
})
test('应该执行基础搜索流程', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 输入搜索关键词
await page.fill('input[placeholder*="关键词"]', 'React框架学习')
// 2. 等待查询预览生成
await expect(page.locator('.query-preview')).toBeVisible()
await expect(page.locator('.query-preview')).toContainText('React框架学习')
// 3. 点击搜索按钮
await page.click('button:has-text("搜索")')
// 4. 验证搜索执行(模拟)
// 在实际环境中会验证新标签页打开
await expect(page.locator('.search-status')).toBeVisible()
})
test('应该展开和使用高级搜索选项', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 点击高级选项展开
await page.click('button:has-text("高级搜索选项")')
// 2. 验证高级选项显示
await expect(page.locator('input[placeholder*="网站"]')).toBeVisible()
await expect(page.locator('select[name="fileType"]')).toBeVisible()
await expect(page.locator('input[placeholder*="精确"]')).toBeVisible()
// 3. 填写网站限制
await page.fill('input[placeholder*="网站"]', 'react.dev')
// 4. 选择文件类型
await page.selectOption('select[name="fileType"]', 'pdf')
// 5. 输入精确匹配
await page.fill('input[placeholder*="精确"]', 'React文档')
// 6. 验证查询预览更新
await expect(page.locator('.query-preview')).toContainText('site:react.dev')
await expect(page.locator('.query-preview')).toContainText('filetype:pdf')
await expect(page.locator('.query-preview')).toContainText('"React文档"')
})
test('应该保存和显示搜索历史', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 执行一次搜索
await page.fill('input[placeholder*="关键词"]', 'Vue.js教程')
await page.click('button:has-text("搜索")')
// 2. 模拟页面刷新(重新加载)
await page.reload()
// 3. 验证历史记录显示
await expect(page.locator('.search-history')).toBeVisible()
await expect(page.locator('.search-history')).toContainText('Vue.js教程')
// 4. 点击历史记录项
await page.click('.search-history-item:has-text("Vue.js教程")')
// 5. 验证搜索参数恢复
await expect(page.locator('input[placeholder*="关键词"]')).toHaveValue('Vue.js教程')
})
test('应该处理搜索错误和验证', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 展开高级选项
await page.click('button:has-text("高级搜索选项")')
// 2. 输入无效的网站域名
await page.fill('input[placeholder*="网站"]', 'invalid-domain')
// 3. 验证错误信息显示
await expect(page.locator('.error-message')).toBeVisible()
await expect(page.locator('.error-message')).toContainText('网站域名格式不正确')
// 4. 验证搜索按钮被禁用
await expect(page.locator('button:has-text("搜索")')).toBeDisabled()
// 5. 修正域名
await page.fill('input[placeholder*="网站"]', 'example.com')
// 6. 验证错误信息消失,搜索按钮可用
await expect(page.locator('.error-message')).not.toBeVisible()
await expect(page.locator('button:has-text("搜索")')).toBeEnabled()
})
test('应该切换搜索引擎', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 切换到Google
await page.selectOption('select[name="engine"]', 'google')
// 2. 验证底部状态更新
await expect(page.locator('.current-engine')).toContainText('GOOGLE')
// 3. 切换到Bing
await page.selectOption('select[name="engine"]', 'bing')
// 4. 验证底部状态更新
await expect(page.locator('.current-engine')).toContainText('BING')
})
test('应该响应键盘快捷键', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 聚焦到关键词输入框
await page.focus('input[placeholder*="关键词"]')
// 2. 输入搜索内容
await page.type('input[placeholder*="关键词"]', 'TypeScript指南')
// 3. 按Enter键执行搜索
await page.press('input[placeholder*="关键词"]', 'Enter')
// 4. 验证搜索执行
await expect(page.locator('.search-status')).toBeVisible()
// 5. 测试Ctrl+K快捷键(如果实现)
await page.keyboard.press('Control+k')
await expect(page.locator('input[placeholder*="关键词"]')).toBeFocused()
})
test('应该处理无障碍功能', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 验证ARIA标签
await expect(page.locator('[aria-label]')).toHaveCount({ min: 1 })
// 2. 测试Tab键导航
await page.keyboard.press('Tab')
await expect(page.locator('input:focus, button:focus, select:focus')).toBeVisible()
// 3. 测试键盘导航高级选项
await page.keyboard.press('Tab')
await page.keyboard.press('Enter') // 打开高级选项
// 4. 验证高级选项可通过键盘访问
await expect(page.locator('input[placeholder*="网站"]')).toBeVisible()
await page.keyboard.press('Tab')
await expect(page.locator('select[name="fileType"]:focus')).toBeVisible()
})
test('应该在移动端正常工作', async ({ page }) => {
// 模拟移动设备视口
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('http://localhost:3000/popup.html')
// 1. 验证响应式布局
await expect(page.locator('body')).toHaveCSS('width', '380px')
// 2. 验证触摸交互
await page.tap('input[placeholder*="关键词"]')
await page.fill('input[placeholder*="关键词"]', '移动端搜索测试')
// 3. 验证按钮大小适合触摸
const searchButton = page.locator('button:has-text("搜索")')
const buttonBox = await searchButton.boundingBox()
expect(buttonBox?.height).toBeGreaterThanOrEqual(44) // 最小触摸目标
// 4. 执行搜索
await page.tap('button:has-text("搜索")')
await expect(page.locator('.search-status')).toBeVisible()
})
test('应该处理极端输入情况', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
// 1. 测试超长关键词
const longKeyword = 'a'.repeat(1000)
await page.fill('input[placeholder*="关键词"]', longKeyword)
// 2. 验证输入处理
await expect(page.locator('.query-preview')).toContainText('aaaaaaaa...')
// 3. 测试特殊字符
await page.fill('input[placeholder*="关键词"]', '测试特殊字符: !@#$%^&*()[]{}|\\:";\'<>?,./')
// 4. 验证特殊字符正确编码
await expect(page.locator('.query-preview')).toBeVisible()
// 5. 测试Unicode字符
await page.fill('input[placeholder*="关键词"]', 'Тест на русском языке')
await expect(page.locator('.query-preview')).toBeVisible()
// 6. 测试Emoji
await page.fill('input[placeholder*="关键词"]', 'React框架学习 🚀 ⚛️')
await expect(page.locator('.query-preview')).toBeVisible()
})
})
test.describe('性能测试', () => {
test('应该在合理时间内响应', async ({ page }) => {
const startTime = Date.now()
await page.goto('http://localhost:3000/popup.html')
// 等待页面完全加载
await page.waitForLoadState('networkidle')
const loadTime = Date.now() - startTime
// 验证页面加载时间 < 1秒
expect(loadTime).toBeLessThan(1000)
})
test('应该快速响应搜索输入', async ({ page }) => {
await page.goto('http://localhost:3000/popup.html')
const startTime = Date.now()
// 输入搜索内容
await page.fill('input[placeholder*="关键词"]', '性能测试')
// 等待查询预览更新
await page.waitForSelector('.query-preview')
const responseTime = Date.now() - startTime
// 验证响应时间 < 100ms
expect(responseTime).toBeLessThan(100)
})
})