1
0

feat: E2E 测试集成真实后端

- Playwright 双 webServer 模式自动启动 Go 后端 + Vite 前端
- 后端使用临时 SQLite 数据库隔离,固定端口 19026
- vite.config.ts proxy target 动态读取环境变量
- 新增 sql.js 依赖用于 SQLite 统计数据 seed
- 新增 e2e/fixtures.ts 共享工具模块(API seed + SQLite seed)
- 拆分测试文件 5→7(providers/models/stats/navigation/validation)
- 删除旧文件 crud.spec.ts/sidebar.spec.ts/stats-cards.spec.ts
- E2E 测试尚有部分用例需调试修复
This commit is contained in:
2026-04-22 00:31:35 +08:00
parent 4fc5fb4764
commit 59179094ed
20 changed files with 973 additions and 540 deletions

View File

@@ -1,96 +1,127 @@
import { test, expect } from '@playwright/test';
import { test, expect } from '@playwright/test'
import { API_BASE, seedUsageStats } from './fixtures'
test.describe('用量统计', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/stats');
// 等待页面加载完成
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible();
});
test.describe('统计概览', () => {
test.beforeAll(async ({ request }) => {
const p1 = `sp1_${Date.now()}`
const p2 = `sp2_${Date.now()}`
process.env._STATS_P1 = p1
process.env._STATS_P2 = p2
test('应显示用量统计页面', async ({ page }) => {
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible();
});
test('应显示筛选控件', async ({ page }) => {
// 验证供应商筛选下拉框
await expect(page.locator('.t-select').first()).toBeVisible();
// 验证模型名称输入框
await expect(page.getByPlaceholder('模型名称')).toBeVisible();
// 验证日期范围选择器 - TDesign DateRangePicker 使用 t-range-input 类
await expect(page.locator('.t-range-input')).toBeVisible();
});
test('应通过导航返回供应商页面', async ({ page }) => {
await page.getByText('供应商管理').click();
await expect(page.getByRole('heading', { name: '供应商管理' })).toBeVisible();
});
test('应显示统计表格列标题', async ({ page }) => {
// 验证表格存在
await expect(page.locator('.t-table')).toBeVisible();
// 通过 thead th 验证列标题存在
const headers = page.locator('.t-table__header th');
await expect(headers).toHaveCount(4);
// 逐个验证列标题文本
await expect(headers.nth(0)).toContainText('供应商');
await expect(headers.nth(1)).toContainText('模型');
await expect(headers.nth(2)).toContainText('日期');
await expect(headers.nth(3)).toContainText('请求数');
});
test('应能输入模型名称筛选', async ({ page }) => {
const modelInput = page.getByPlaceholder('模型名称');
// 输入模型名称
await modelInput.fill('gpt-4');
// 验证输入值
await expect(modelInput).toHaveValue('gpt-4');
// 清空输入
await modelInput.clear();
await expect(modelInput).toHaveValue('');
});
test('应能打开供应商筛选下拉框', async ({ page }) => {
// 点击供应商下拉框
await page.locator('.t-select').first().click();
// 验证下拉选项出现
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 });
// 点击外部关闭下拉框
await page.keyboard.press('Escape');
});
test('应能打开日期范围选择器', async ({ page }) => {
// 点击日期选择器 - TDesign DateRangePicker
const dateRangePicker = page.locator('.t-range-input');
await expect(dateRangePicker).toBeVisible();
// 点击日期选择器
await dateRangePicker.click();
// 等待一下让面板打开
await page.waitForTimeout(500);
// 点击外部关闭
await page.keyboard.press('Escape');
});
test('应显示空数据提示', async ({ page }) => {
// 等待表格加载
await page.waitForSelector('.t-table', { timeout: 5000 });
// 检查是否有数据
const emptyText = page.locator('.t-table__empty');
if (await emptyText.count() > 0) {
await expect(emptyText).toBeVisible();
for (const id of [p1, p2]) {
await request.post(`${API_BASE}/api/providers`, {
data: {
id,
name: `Stats Provider ${id}`,
api_key: 'sk_test',
base_url: 'https://api.example.com/v1',
protocol: 'openai',
enabled: true,
},
})
}
});
});
const today = new Date()
const statsData = []
for (let i = 0; i < 7; i++) {
const d = new Date(today)
d.setDate(d.getDate() - i)
const dateStr = d.toISOString().slice(0, 10)
statsData.push({ providerId: p1, modelName: 'gpt_4', requestCount: 10 + i, date: dateStr })
if (i < 3) {
statsData.push({ providerId: p1, modelName: 'claude_3', requestCount: 5 + i, date: dateStr })
}
if (i < 5) {
statsData.push({ providerId: p2, modelName: 'gpt_4', requestCount: 8 + i, date: dateStr })
}
}
await seedUsageStats(statsData)
})
test.beforeEach(async ({ page }) => {
await page.goto('/stats')
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible()
})
test('应显示正确的总请求量', async ({ page }) => {
await page.waitForTimeout(1000)
await expect(page.getByText('总请求量')).toBeVisible()
})
test('应显示正确的活跃模型数和活跃供应商数', async ({ page }) => {
await page.waitForTimeout(1000)
await expect(page.getByText('活跃模型数')).toBeVisible()
await expect(page.getByText('活跃供应商数')).toBeVisible()
})
test('应显示统计数据行', async ({ page }) => {
await expect(page.locator('.t-table__body tr').first()).toBeVisible({ timeout: 5000 })
})
test('应渲染趋势图表区域', async ({ page }) => {
await expect(page.getByText('请求趋势')).toBeVisible()
})
})
test.describe('统计筛选', () => {
test.beforeAll(async ({ request }) => {
const p1 = `fp1_${Date.now()}`
const p2 = `fp2_${Date.now()}`
process.env._FILTER_P1 = p1
process.env._FILTER_P2 = p2
for (const id of [p1, p2]) {
await request.post(`${API_BASE}/api/providers`, {
data: {
id,
name: `Filter Provider ${id}`,
api_key: 'sk_test',
base_url: 'https://api.example.com/v1',
protocol: 'openai',
enabled: true,
},
})
}
const today = new Date()
const statsData = []
for (let i = 0; i < 3; i++) {
const d = new Date(today)
d.setDate(d.getDate() - i)
const dateStr = d.toISOString().slice(0, 10)
statsData.push({ providerId: p1, modelName: 'gpt_4', requestCount: 10 + i, date: dateStr })
statsData.push({ providerId: p2, modelName: 'claude_3', requestCount: 5 + i, date: dateStr })
}
await seedUsageStats(statsData)
})
test.beforeEach(async ({ page }) => {
await page.goto('/stats')
await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible()
})
test('按供应商筛选', async ({ page }) => {
await expect(page.locator('.t-table__body tr').first()).toBeVisible({ timeout: 5000 })
const rowCountBefore = await page.locator('.t-table__body tr:not(.t-table__empty-row)').count()
await page.locator('.t-select').first().click()
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 })
await page.locator('.t-select__dropdown .t-select-option').first().click()
await page.waitForTimeout(1000)
const rowCountAfter = await page.locator('.t-table__body tr:not(.t-table__empty-row)').count()
expect(rowCountAfter).toBeLessThanOrEqual(rowCountBefore)
})
test('按模型名称筛选', async ({ page }) => {
await expect(page.locator('.t-table__body tr').first()).toBeVisible({ timeout: 5000 })
await page.getByPlaceholder('模型名称').fill('gpt_4')
await page.waitForTimeout(1000)
await expect(page.locator('.t-table__body')).toBeVisible()
})
test('应显示筛选栏', async ({ page }) => {
await expect(page.locator('.t-select').first()).toBeVisible()
await expect(page.getByPlaceholder('模型名称')).toBeVisible()
})
})