初始化
Some checks failed
Some checks failed
This commit is contained in:
72
frontend/tests/e2e/global-setup.js
Normal file
72
frontend/tests/e2e/global-setup.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const { chromium } = require('@playwright/test')
|
||||
|
||||
async function globalSetup(config) {
|
||||
console.log('🚀 开始全局设置...')
|
||||
|
||||
// 获取浏览器实例
|
||||
const browser = await chromium.launch()
|
||||
const context = await browser.newContext()
|
||||
const page = await context.newPage()
|
||||
|
||||
try {
|
||||
// 设置测试数据
|
||||
await setupTestData(page)
|
||||
|
||||
// 设置用户认证
|
||||
await setupAuthentication(page)
|
||||
|
||||
// 设置测试环境变量
|
||||
await setupEnvironmentVariables()
|
||||
|
||||
console.log('✅ 全局设置完成')
|
||||
} catch (error) {
|
||||
console.error('❌ 全局设置失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
await context.close()
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
|
||||
async function setupTestData(page) {
|
||||
console.log('📊 设置测试数据...')
|
||||
|
||||
// 这里可以设置测试数据,例如:
|
||||
// 1. 创建测试用户
|
||||
// 2. 准备测试产品数据
|
||||
// 3. 设置测试类别等
|
||||
|
||||
// 示例:通过API设置测试数据
|
||||
// await page.goto('/api/test/setup')
|
||||
// await page.waitForResponse(response => response.status() === 200)
|
||||
}
|
||||
|
||||
async function setupAuthentication(page) {
|
||||
console.log('🔐 设置用户认证...')
|
||||
|
||||
// 这里可以设置测试用户认证,例如:
|
||||
// 1. 创建测试用户
|
||||
// 2. 登录测试用户
|
||||
// 3. 保存认证令牌
|
||||
|
||||
// 示例:通过API登录
|
||||
// await page.goto('/api/auth/login')
|
||||
// await page.fill('[data-testid="username"]', 'testuser')
|
||||
// await page.fill('[data-testid="password"]', 'testpassword')
|
||||
// await page.click('[data-testid="login-button"]')
|
||||
// await page.waitForResponse(response => response.status() === 200)
|
||||
|
||||
// 保存认证状态
|
||||
// await context.storageState({ path: 'test-auth-state.json' })
|
||||
}
|
||||
|
||||
async function setupEnvironmentVariables() {
|
||||
console.log('🌍 设置环境变量...')
|
||||
|
||||
// 设置测试环境变量
|
||||
process.env.TEST_MODE = 'true'
|
||||
process.env.API_BASE_URL = 'http://localhost:7001/api'
|
||||
process.env.TEST_TIMEOUT = '30000'
|
||||
}
|
||||
|
||||
module.exports = globalSetup
|
||||
120
frontend/tests/e2e/global-teardown.js
Normal file
120
frontend/tests/e2e/global-teardown.js
Normal file
@@ -0,0 +1,120 @@
|
||||
const { chromium } = require('@playwright/test')
|
||||
|
||||
async function globalTeardown(config) {
|
||||
console.log('🧹 开始全局清理...')
|
||||
|
||||
// 获取浏览器实例
|
||||
const browser = await chromium.launch()
|
||||
const context = await browser.newContext()
|
||||
const page = await context.newPage()
|
||||
|
||||
try {
|
||||
// 清理测试数据
|
||||
await cleanupTestData(page)
|
||||
|
||||
// 清理用户认证
|
||||
await cleanupAuthentication()
|
||||
|
||||
// 清理环境变量
|
||||
await cleanupEnvironmentVariables()
|
||||
|
||||
// 清理测试文件
|
||||
await cleanupTestFiles()
|
||||
|
||||
console.log('✅ 全局清理完成')
|
||||
} catch (error) {
|
||||
console.error('❌ 全局清理失败:', error)
|
||||
throw error
|
||||
} finally {
|
||||
await context.close()
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanupTestData(page) {
|
||||
console.log('📊 清理测试数据...')
|
||||
|
||||
// 这里可以清理测试数据,例如:
|
||||
// 1. 删除测试用户
|
||||
// 2. 清理测试产品数据
|
||||
// 3. 重置测试类别等
|
||||
|
||||
// 示例:通过API清理测试数据
|
||||
// await page.goto('/api/test/cleanup')
|
||||
// await page.waitForResponse(response => response.status() === 200)
|
||||
}
|
||||
|
||||
async function cleanupAuthentication() {
|
||||
console.log('🔐 清理用户认证...')
|
||||
|
||||
// 这里可以清理用户认证,例如:
|
||||
// 1. 删除测试用户
|
||||
// 2. 清理认证令牌
|
||||
|
||||
// 示例:删除认证状态文件
|
||||
// const fs = require('fs')
|
||||
// if (fs.existsSync('test-auth-state.json')) {
|
||||
// fs.unlinkSync('test-auth-state.json')
|
||||
// }
|
||||
}
|
||||
|
||||
async function cleanupEnvironmentVariables() {
|
||||
console.log('🌍 清理环境变量...')
|
||||
|
||||
// 清理测试环境变量
|
||||
delete process.env.TEST_MODE
|
||||
delete process.env.API_BASE_URL
|
||||
delete process.env.TEST_TIMEOUT
|
||||
}
|
||||
|
||||
async function cleanupTestFiles() {
|
||||
console.log('📁 清理测试文件...')
|
||||
|
||||
// 清理测试生成的文件
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
// 清理测试报告目录
|
||||
const reportDirs = ['test-results', 'playwright-report']
|
||||
reportDirs.forEach(dir => {
|
||||
const dirPath = path.resolve(process.cwd(), dir)
|
||||
if (fs.existsSync(dirPath)) {
|
||||
// 保留目录,但清理其中的文件
|
||||
const files = fs.readdirSync(dirPath)
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(dirPath, file)
|
||||
const stat = fs.statSync(filePath)
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// 递归删除子目录
|
||||
deleteFolderRecursive(filePath)
|
||||
} else {
|
||||
// 删除文件
|
||||
fs.unlinkSync(filePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 递归删除文件夹
|
||||
function deleteFolderRecursive(folderPath) {
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
if (fs.existsSync(folderPath)) {
|
||||
fs.readdirSync(folderPath).forEach(file => {
|
||||
const curPath = path.join(folderPath, file)
|
||||
if (fs.lstatSync(curPath).isDirectory()) {
|
||||
// 递归删除子目录
|
||||
deleteFolderRecursive(curPath)
|
||||
} else {
|
||||
// 删除文件
|
||||
fs.unlinkSync(curPath)
|
||||
}
|
||||
})
|
||||
fs.rmdirSync(folderPath)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = globalTeardown
|
||||
315
frontend/tests/e2e/user-journey.spec.js
Normal file
315
frontend/tests/e2e/user-journey.spec.js
Normal file
@@ -0,0 +1,315 @@
|
||||
import { test, expect, chromium } from '@playwright/test'
|
||||
|
||||
test.describe('用户旅程测试', () => {
|
||||
let browser
|
||||
let context
|
||||
let page
|
||||
|
||||
test.beforeAll(async () => {
|
||||
// 启动浏览器
|
||||
browser = await chromium.launch()
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
// 关闭浏览器
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
test.beforeEach(async () => {
|
||||
// 创建新的浏览器上下文和页面
|
||||
context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
|
||||
// 设置视口大小
|
||||
await page.setViewportSize({ width: 1280, height: 720 })
|
||||
})
|
||||
|
||||
test.afterEach(async () => {
|
||||
// 关闭浏览器上下文
|
||||
await context.close()
|
||||
})
|
||||
|
||||
test('用户应该能够浏览首页并选择产品类别', async () => {
|
||||
// 访问首页
|
||||
await page.goto('/')
|
||||
|
||||
// 检查页面标题
|
||||
await expect(page).toHaveTitle(/硬件性能排行榜/)
|
||||
|
||||
// 检查应用标题
|
||||
await expect(page.locator('.header__title')).toContainText('硬件性能排行榜')
|
||||
|
||||
// 等待类别卡片加载
|
||||
await page.waitForSelector('.category-card')
|
||||
|
||||
// 检查类别卡片数量
|
||||
const categoryCards = await page.locator('.category-card').count()
|
||||
expect(categoryCards).toBeGreaterThan(0)
|
||||
|
||||
// 点击第一个类别卡片
|
||||
await page.locator('.category-card').first().click()
|
||||
|
||||
// 检查是否跳转到类别排名页面
|
||||
await expect(page).toHaveURL(/\/category\/\d+/)
|
||||
|
||||
// 检查页面标题是否更新为类别名称
|
||||
const categoryName = await page.locator('.page-title').textContent()
|
||||
expect(categoryName).toBeTruthy()
|
||||
})
|
||||
|
||||
test('用户应该能够浏览产品排名列表', async () => {
|
||||
// 直接访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待产品列表加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 检查产品卡片数量
|
||||
const productCards = await page.locator('.product-card').count()
|
||||
expect(productCards).toBeGreaterThan(0)
|
||||
|
||||
// 检查排名显示
|
||||
const firstProductRank = await page.locator('.product-card').first().locator('.product-rank').textContent()
|
||||
expect(firstProductRank).toContain('#1')
|
||||
|
||||
// 点击第一个产品卡片
|
||||
await page.locator('.product-card').first().click()
|
||||
|
||||
// 检查是否跳转到产品详情页面
|
||||
await expect(page).toHaveURL(/\/product\/\d+/)
|
||||
|
||||
// 检查产品详情页面内容
|
||||
await expect(page.locator('.product-detail')).toBeVisible()
|
||||
await expect(page.locator('.product-name')).toBeVisible()
|
||||
await expect(page.locator('.product-specs')).toBeVisible()
|
||||
})
|
||||
|
||||
test('用户应该能够使用搜索功能查找产品', async () => {
|
||||
// 访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待搜索框加载
|
||||
await page.waitForSelector('.search-input')
|
||||
|
||||
// 输入搜索关键词
|
||||
await page.fill('.search-input', 'Intel')
|
||||
|
||||
// 点击搜索按钮
|
||||
await page.click('.search-button')
|
||||
|
||||
// 等待搜索结果加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 检查搜索结果
|
||||
const productCards = await page.locator('.product-card').count()
|
||||
expect(productCards).toBeGreaterThan(0)
|
||||
|
||||
// 检查搜索结果是否包含搜索关键词
|
||||
const firstProductName = await page.locator('.product-card').first().locator('.product-name').textContent()
|
||||
expect(firstProductName.toLowerCase()).toContain('intel'.toLowerCase())
|
||||
})
|
||||
|
||||
test('用户应该能够使用筛选功能过滤产品', async () => {
|
||||
// 访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待筛选器加载
|
||||
await page.waitForSelector('.product-filter')
|
||||
|
||||
// 选择品牌筛选
|
||||
await page.selectOption('.brand-filter', 'Intel')
|
||||
|
||||
// 设置性能分数范围
|
||||
await page.fill('.min-score-input', '5000')
|
||||
await page.fill('.max-score-input', '10000')
|
||||
|
||||
// 点击应用筛选按钮
|
||||
await page.click('.apply-filter-button')
|
||||
|
||||
// 等待筛选结果加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 检查筛选结果
|
||||
const productCards = await page.locator('.product-card').count()
|
||||
expect(productCards).toBeGreaterThan(0)
|
||||
|
||||
// 检查第一个产品的品牌
|
||||
const firstProductBrand = await page.locator('.product-card').first().locator('.product-brand').textContent()
|
||||
expect(firstProductBrand).toContain('Intel')
|
||||
})
|
||||
|
||||
test('用户应该能够使用排序功能对产品进行排序', async () => {
|
||||
// 访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待排序选择器加载
|
||||
await page.waitForSelector('.sort-select')
|
||||
|
||||
// 选择按性能分数降序排序
|
||||
await page.selectOption('.sort-select', 'score-desc')
|
||||
|
||||
// 等待排序结果加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 检查第一个产品的性能分数是否最高
|
||||
const firstProductScore = await page.locator('.product-card').first().locator('.product-score').textContent()
|
||||
const secondProductScore = await page.locator('.product-card').nth(1).locator('.product-score').textContent()
|
||||
|
||||
// 提取数字部分进行比较
|
||||
const firstScore = parseInt(firstProductScore.replace(/[^0-9]/g, ''))
|
||||
const secondScore = parseInt(secondProductScore.replace(/[^0-9]/g, ''))
|
||||
|
||||
expect(firstScore).toBeGreaterThanOrEqual(secondScore)
|
||||
})
|
||||
|
||||
test('用户应该能够选择产品进行对比', async () => {
|
||||
// 访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待产品列表加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 选择第一个产品的复选框
|
||||
await page.check('.product-card').first().locator('.compare-checkbox')
|
||||
|
||||
// 选择第二个产品的复选框
|
||||
await page.check('.product-card').nth(1).locator('.compare-checkbox')
|
||||
|
||||
// 点击对比按钮
|
||||
await page.click('.compare-button')
|
||||
|
||||
// 检查是否跳转到产品对比页面
|
||||
await expect(page).toHaveURL(/\/compare/)
|
||||
|
||||
// 检查对比页面内容
|
||||
await expect(page.locator('.comparison-table')).toBeVisible()
|
||||
await expect(page.locator('.comparison-chart')).toBeVisible()
|
||||
})
|
||||
|
||||
test('用户应该能够查看性能监控页面', async () => {
|
||||
// 访问性能监控页面
|
||||
await page.goto('/monitor')
|
||||
|
||||
// 等待页面加载
|
||||
await page.waitForSelector('.monitor-dashboard')
|
||||
|
||||
// 检查性能指标卡片
|
||||
await expect(page.locator('.metric-card')).toBeVisible()
|
||||
|
||||
// 检查性能图表
|
||||
await expect(page.locator('.performance-chart')).toBeVisible()
|
||||
|
||||
// 检查系统状态
|
||||
await expect(page.locator('.system-status')).toBeVisible()
|
||||
})
|
||||
|
||||
test('用户应该能够在移动设备上正常使用应用', async () => {
|
||||
// 设置移动设备视口
|
||||
await page.setViewportSize({ width: 375, height: 667 })
|
||||
|
||||
// 访问首页
|
||||
await page.goto('/')
|
||||
|
||||
// 检查移动端菜单按钮
|
||||
await expect(page.locator('.header__mobile-menu')).toBeVisible()
|
||||
|
||||
// 点击移动端菜单按钮
|
||||
await page.click('.header__mobile-menu')
|
||||
|
||||
// 检查移动端菜单是否展开
|
||||
await expect(page.locator('.mobile-menu')).toBeVisible()
|
||||
|
||||
// 点击类别卡片
|
||||
await page.click('.category-card').first()
|
||||
|
||||
// 检查是否跳转到类别排名页面
|
||||
await expect(page).toHaveURL(/\/category\/\d+/)
|
||||
|
||||
// 检查移动端产品列表布局
|
||||
await expect(page.locator('.product-card')).toBeVisible()
|
||||
|
||||
// 检查移动端筛选器
|
||||
await expect(page.locator('.mobile-filter')).toBeVisible()
|
||||
})
|
||||
|
||||
test('用户应该能够处理网络错误', async () => {
|
||||
// 模拟网络错误
|
||||
await page.route('**/api/**', route => route.abort())
|
||||
|
||||
// 访问首页
|
||||
await page.goto('/')
|
||||
|
||||
// 等待错误提示
|
||||
await page.waitForSelector('.error-message')
|
||||
|
||||
// 检查错误提示内容
|
||||
await expect(page.locator('.error-message')).toContainText('网络错误')
|
||||
|
||||
// 点击重试按钮
|
||||
await page.click('.retry-button')
|
||||
|
||||
// 恢复网络连接
|
||||
await page.unroute('**/api/**')
|
||||
|
||||
// 检查页面是否恢复正常
|
||||
await page.waitForSelector('.category-card')
|
||||
await expect(page.locator('.category-card')).toBeVisible()
|
||||
})
|
||||
|
||||
test('用户应该能够使用页面导航', async () => {
|
||||
// 访问首页
|
||||
await page.goto('/')
|
||||
|
||||
// 点击导航栏中的性能监控链接
|
||||
await page.click('[data-testid="monitor-button"]')
|
||||
|
||||
// 检查是否跳转到性能监控页面
|
||||
await expect(page).toHaveURL('/monitor')
|
||||
|
||||
// 点击浏览器后退按钮
|
||||
await page.goBack()
|
||||
|
||||
// 检查是否返回到首页
|
||||
await expect(page).toHaveURL('/')
|
||||
|
||||
// 点击浏览器前进按钮
|
||||
await page.goForward()
|
||||
|
||||
// 检查是否前进到性能监控页面
|
||||
await expect(page).toHaveURL('/monitor')
|
||||
})
|
||||
|
||||
test('用户应该能够使用分页功能浏览更多产品', async () => {
|
||||
// 访问类别排名页面
|
||||
await page.goto('/category/1')
|
||||
|
||||
// 等待产品列表和分页组件加载
|
||||
await page.waitForSelector('.product-card')
|
||||
await page.waitForSelector('.pagination')
|
||||
|
||||
// 记录第一页的产品
|
||||
const firstPageProducts = await page.locator('.product-card').allTextContents()
|
||||
|
||||
// 点击下一页按钮
|
||||
await page.click('.pagination-next')
|
||||
|
||||
// 等待新页面加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 记录第二页的产品
|
||||
const secondPageProducts = await page.locator('.product-card').allTextContents()
|
||||
|
||||
// 检查两页的产品是否不同
|
||||
expect(firstPageProducts).not.toEqual(secondPageProducts)
|
||||
|
||||
// 点击上一页按钮
|
||||
await page.click('.pagination-prev')
|
||||
|
||||
// 等待页面加载
|
||||
await page.waitForSelector('.product-card')
|
||||
|
||||
// 检查是否返回到第一页
|
||||
const backToFirstPageProducts = await page.locator('.product-card').allTextContents()
|
||||
expect(backToFirstPageProducts).toEqual(firstPageProducts)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user