- Dialog 设置 lazy={false} 修复首次打开编辑弹窗表单为空
- API Key 改为普通字段(前端去掉 password 类型,后端去掉掩码逻辑)
- 删除模型编辑弹窗中的统一模型 ID 字段
- 简化 ProviderService.Get 签名(去掉 maskKey 参数)
- 删除 domain 和 config 层的 MaskAPIKey() 方法
- 更新前后端测试(107 单元测试 + 16 E2E 全部通过)
- 同步 delta spec 到主 spec
123 lines
5.6 KiB
TypeScript
123 lines
5.6 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { clearDatabase } from './fixtures'
|
|
|
|
let uid = Date.now()
|
|
|
|
function nextId() {
|
|
return `pw_${++uid}`
|
|
}
|
|
|
|
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[placeholder="sk-..."]'),
|
|
baseUrl: dialog.locator('input[placeholder="例如: https://api.openai.com/v1"]'),
|
|
protocol: dialog.locator('.t-select'),
|
|
saveBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '保存' }),
|
|
cancelBtn: dialog.locator('.t-dialog__footer').getByRole('button', { name: '取消' }),
|
|
}
|
|
}
|
|
|
|
test.describe('供应商管理', () => {
|
|
test.beforeEach(async ({ page, request }) => {
|
|
await clearDatabase(request)
|
|
await page.goto('/providers')
|
|
await expect(page.getByRole('heading', { name: '供应商管理' })).toBeVisible()
|
|
})
|
|
|
|
test('应能创建供应商并验证出现在表格中', async ({ page }) => {
|
|
const testId = nextId()
|
|
await page.getByRole('button', { name: '添加供应商' }).click()
|
|
await expect(page.locator('.t-dialog:visible')).toBeVisible()
|
|
|
|
const inputs = formInputs(page)
|
|
await inputs.id.fill(testId)
|
|
await inputs.name.fill('Test Provider')
|
|
await inputs.apiKey.fill('sk_test_key_12345')
|
|
await inputs.baseUrl.fill('https://api.openai.com/v1')
|
|
await inputs.protocol.click()
|
|
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 })
|
|
await page.locator('.t-select__dropdown .t-select-option').first().click()
|
|
await page.waitForSelector('.t-select__dropdown', { state: 'hidden', timeout: 3000 })
|
|
|
|
await inputs.saveBtn.click()
|
|
|
|
await expect(page.locator('.t-table__body').getByText('Test Provider')).toBeVisible({ timeout: 10000 })
|
|
})
|
|
|
|
test('应能编辑供应商并验证更新生效', async ({ page }) => {
|
|
const testId = nextId()
|
|
await page.getByRole('button', { name: '添加供应商' }).click()
|
|
await expect(page.locator('.t-dialog:visible')).toBeVisible()
|
|
const inputs = formInputs(page)
|
|
await inputs.id.fill(testId)
|
|
await inputs.name.fill('Before Edit')
|
|
await inputs.apiKey.fill('sk_key')
|
|
await inputs.baseUrl.fill('https://api.example.com/v1')
|
|
await inputs.protocol.click()
|
|
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 })
|
|
await page.locator('.t-select__dropdown .t-select-option').first().click()
|
|
await page.waitForSelector('.t-select__dropdown', { state: 'hidden', timeout: 3000 })
|
|
|
|
const responsePromise = page.waitForResponse(resp => resp.url().includes('/api/providers') && resp.request().method() === 'POST')
|
|
await inputs.saveBtn.click()
|
|
await responsePromise
|
|
await expect(page.locator('.t-table__body').getByText('Before Edit')).toBeVisible({ timeout: 5000 })
|
|
|
|
await page.locator('.t-table__body button:has-text("编辑")').first().click()
|
|
await expect(page.locator('.t-dialog:visible')).toBeVisible()
|
|
|
|
const editInputs = formInputs(page)
|
|
await editInputs.name.clear()
|
|
await editInputs.name.fill('After Edit')
|
|
|
|
const updateResponsePromise = page.waitForResponse(resp => resp.url().includes('/api/providers') && resp.request().method() === 'PUT')
|
|
await editInputs.saveBtn.click()
|
|
await updateResponsePromise
|
|
await expect(page.locator('.t-table__body').getByText('After Edit')).toBeVisible({ timeout: 5000 })
|
|
})
|
|
|
|
test('应能删除供应商并验证消失', async ({ page }) => {
|
|
const testId = nextId()
|
|
await page.getByRole('button', { name: '添加供应商' }).click()
|
|
await expect(page.locator('.t-dialog:visible')).toBeVisible()
|
|
const inputs = formInputs(page)
|
|
await inputs.id.fill(testId)
|
|
await inputs.name.fill('To Delete')
|
|
await inputs.apiKey.fill('sk_key')
|
|
await inputs.baseUrl.fill('https://api.example.com/v1')
|
|
await inputs.protocol.click()
|
|
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 })
|
|
await page.locator('.t-select__dropdown .t-select-option').first().click()
|
|
await page.waitForSelector('.t-select__dropdown', { state: 'hidden', timeout: 3000 })
|
|
await inputs.saveBtn.click()
|
|
await expect(page.locator('.t-table__body').getByText('To Delete')).toBeVisible({ timeout: 10000 })
|
|
|
|
await page.locator('.t-table__body button:has-text("删除")').first().click()
|
|
await expect(page.getByText('确定要删除这个供应商吗?')).toBeVisible()
|
|
await page.locator('.t-popconfirm').getByRole('button', { name: '确定' }).click()
|
|
await expect(page.locator('.t-table__body').getByText('To Delete')).not.toBeVisible({ timeout: 5000 })
|
|
})
|
|
|
|
test('应正确显示完整 API Key', async ({ page }) => {
|
|
const testId = nextId()
|
|
await page.getByRole('button', { name: '添加供应商' }).click()
|
|
await expect(page.locator('.t-dialog:visible')).toBeVisible()
|
|
const inputs = formInputs(page)
|
|
await inputs.id.fill(testId)
|
|
await inputs.name.fill('Mask Test')
|
|
await inputs.apiKey.fill('sk_abcdefghijklmnopqrstuvwxyz')
|
|
await inputs.baseUrl.fill('https://api.example.com/v1')
|
|
await inputs.protocol.click()
|
|
await page.waitForSelector('.t-select__dropdown', { timeout: 3000 })
|
|
await page.locator('.t-select__dropdown .t-select-option').first().click()
|
|
await page.waitForSelector('.t-select__dropdown', { state: 'hidden', timeout: 3000 })
|
|
await inputs.saveBtn.click()
|
|
await expect(page.locator('.t-table__body').getByText('Mask Test')).toBeVisible({ timeout: 10000 })
|
|
|
|
await expect(page.locator('.t-table__body')).toContainText('sk_abcdefghijklmnopqrstuvwxyz')
|
|
})
|
|
})
|