import { test, expect } from '@playwright/test'; // 辅助:在对话框内定位输入框 function formInputs(page: import('@playwright/test').Page) { const dialog = page.locator('.t-dialog:visible'); return { id: dialog.locator('input[placeholder="例如: openai"]'), name: dialog.locator('input[placeholder="例如: OpenAI"]'), apiKey: dialog.locator('input[type="password"]'), baseUrl: dialog.locator('input[placeholder="例如: https://api.openai.com/v1"]'), saveBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '保存' }), cancelBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '取消' }), }; } // 辅助:在模型对话框内定位输入框 function modelFormInputs(page: import('@playwright/test').Page) { const dialog = page.locator('.t-dialog:visible'); return { id: dialog.locator('input[placeholder="例如: gpt-4o"]').first(), modelName: dialog.locator('input[placeholder="例如: gpt-4o"]').nth(1), saveBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '保存' }), cancelBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '取消' }), }; } test.describe('供应商和模型完整CRUD流程', () => { test.beforeEach(async ({ page }) => { await page.goto('/providers'); await expect(page.getByRole('heading', { name: '供应商管理' })).toBeVisible(); }); test('完整的供应商创建流程', async ({ page }) => { await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); const inputs = formInputs(page); const testId = `e2e-${Date.now()}`; await inputs.id.fill(testId); await inputs.name.fill('E2E Test Provider'); await inputs.apiKey.fill('sk-e2e-test-key'); await inputs.baseUrl.fill('https://api.e2e-test.com/v1'); // 验证所有字段填写正确 await expect(inputs.id).toHaveValue(testId); await expect(inputs.name).toHaveValue('E2E Test Provider'); await expect(inputs.baseUrl).toHaveValue('https://api.e2e-test.com/v1'); await inputs.saveBtn.click(); // 注意:对话框关闭依赖后端 API 响应成功,此处仅验证提交按钮可点击 }); test('供应商创建后编辑流程', async ({ page }) => { const editBtns = page.locator('.t-table__body button:has-text("编辑")'); const count = await editBtns.count(); if (count > 0) { await editBtns.first().click(); await expect(page.locator('.t-dialog:visible')).toBeVisible(); const inputs = formInputs(page); await inputs.name.clear(); await inputs.name.fill('Updated Provider Name'); // 验证名称字段已更新 await expect(inputs.name).toHaveValue('Updated Provider Name'); await inputs.saveBtn.click(); // 注意:对话框关闭依赖后端 API 响应成功,此处仅验证提交按钮可点击 } else { test.skip(); } }); test('供应商删除流程', async ({ page }) => { const deleteBtns = page.locator('.t-table__body button:has-text("删除")'); const count = await deleteBtns.count(); if (count > 0) { await deleteBtns.first().click(); await expect(page.getByText('确定要删除这个供应商吗?')).toBeVisible(); // 点击确认(TDesign Popconfirm 确定按钮) await page.locator('.t-popconfirm').getByRole('button', { name: '确定' }).click(); await expect(page.getByText('确定要删除这个供应商吗?')).not.toBeVisible({ timeout: 3000 }); } else { test.skip(); } }); test('展开供应商并添加模型', async ({ page }) => { const expandBtns = page.locator('.t-table__expandable-icon'); const expandCount = await expandBtns.count(); if (expandCount > 0) { await expandBtns.first().click(); await expect(page.locator('.t-table__expanded-row').first()).toBeVisible(); const addModelBtn = page.locator('.t-table__expanded-row button:has-text("添加模型")'); const addCount = await addModelBtn.count(); if (addCount > 0) { await addModelBtn.first().click(); const dialog = page.locator('.t-dialog'); await expect(dialog).toBeVisible(); await expect(dialog.getByText('添加模型')).toBeVisible(); const modelInputs = modelFormInputs(page); await modelInputs.id.fill(`model-${Date.now()}`); await modelInputs.modelName.fill('gpt-4-turbo'); await modelInputs.saveBtn.click(); await expect(page.locator('.t-dialog')).not.toBeVisible({ timeout: 5000 }); } else { test.skip(); } } else { test.skip(); } }); test('编辑已有模型', async ({ page }) => { const expandBtns = page.locator('.t-table__expandable-icon'); const expandCount = await expandBtns.count(); if (expandCount > 0) { await expandBtns.first().click(); await expect(page.locator('.t-table__expanded-row').first()).toBeVisible(); const modelEditBtns = page.locator('.t-table__expanded-row button:has-text("编辑")'); const editCount = await modelEditBtns.count(); if (editCount > 0) { await modelEditBtns.first().click(); const dialog = page.locator('.t-dialog'); await expect(dialog).toBeVisible(); await expect(dialog.getByText('编辑模型')).toBeVisible(); // ID 字段应被禁用 await expect(modelFormInputs(page).id).toBeDisabled(); await modelFormInputs(page).cancelBtn.click(); } else { test.skip(); } } else { test.skip(); } }); }); test.describe('错误处理和边界情况', () => { test.beforeEach(async ({ page }) => { await page.goto('/providers'); await expect(page.getByRole('heading', { name: '供应商管理' })).toBeVisible(); }); test('应显示必填字段验证', async ({ page }) => { await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); await formInputs(page).saveBtn.click(); await expect(page.getByText('请输入供应商 ID')).toBeVisible(); await expect(page.getByText('请输入名称')).toBeVisible(); await expect(page.getByText('请输入 API Key')).toBeVisible(); await expect(page.getByText('请输入 Base URL')).toBeVisible(); }); test('应验证URL格式', async ({ page }) => { await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); const inputs = formInputs(page); await inputs.id.fill('test-url'); await inputs.name.fill('Test'); await inputs.apiKey.fill('sk-test'); await inputs.baseUrl.fill('not-a-url'); await inputs.saveBtn.click(); await expect(page.getByText('请输入有效的 URL')).toBeVisible(); }); test('超长输入处理', async ({ page }) => { await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); const inputs = formInputs(page); await inputs.id.fill('test-long'); await inputs.name.fill('a'.repeat(500)); await inputs.apiKey.fill('sk-test'); await inputs.baseUrl.fill('https://api.test.com/v1'); await inputs.saveBtn.click(); // 等待 API 响应或验证错误 await page.waitForTimeout(2000); }); test('快速连续点击只打开一个对话框', async ({ page }) => { await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); expect(await page.locator('.t-dialog').count()).toBe(1); await formInputs(page).cancelBtn.click(); await expect(page.locator('.t-dialog')).not.toBeVisible(); }); test('取消后表单应重置', async ({ page }) => { // 打开并填写表单 await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); let inputs = formInputs(page); await inputs.id.fill('should-be-reset'); await inputs.name.fill('Should Be Reset'); await inputs.cancelBtn.click(); await expect(page.locator('.t-dialog')).not.toBeVisible(); // 重新打开 await page.getByRole('button', { name: '添加供应商' }).click(); await expect(page.locator('.t-dialog')).toBeVisible(); // 验证表单已重置 inputs = formInputs(page); await expect(inputs.id).toHaveValue(''); await expect(inputs.name).toHaveValue(''); }); }); test.describe('用量统计筛选功能', () => { test.beforeEach(async ({ page }) => { await page.goto('/stats'); await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible(); }); test('组合筛选条件', async ({ page }) => { await page.locator('.t-select').first().click(); await page.waitForSelector('.t-select__dropdown', { timeout: 3000 }); await page.keyboard.press('Escape'); await page.getByPlaceholder('模型名称').fill('gpt-4'); await expect(page.getByPlaceholder('模型名称')).toHaveValue('gpt-4'); }); 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.getByPlaceholder('模型名称').fill('gpt-4'); await page.reload(); await expect(page.getByRole('heading', { name: '用量统计' })).toBeVisible(); await expect(page.getByPlaceholder('模型名称')).toHaveValue(''); }); });