init: 2026Technology-Competition initial commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { ADMIN } from '../utils/auth'
|
||||
|
||||
test.describe('登录', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login')
|
||||
await page.evaluate(() => localStorage.clear())
|
||||
await page.reload()
|
||||
})
|
||||
|
||||
test('成功登录:跳转工作台并存储 token', async ({ page }) => {
|
||||
await page.getByPlaceholder('账号').fill(ADMIN.username)
|
||||
await page.getByPlaceholder('密码').fill(ADMIN.password)
|
||||
await page.getByRole('button', { name: /登\s*录/i }).click()
|
||||
await expect(page).toHaveURL(/\/dashboard/)
|
||||
const token = await page.evaluate(() => localStorage.getItem('accessToken'))
|
||||
expect(token).toBeTruthy()
|
||||
})
|
||||
|
||||
test('失败登录:错误密码提示', async ({ page }) => {
|
||||
await page.getByPlaceholder('账号').fill(ADMIN.username)
|
||||
await page.getByPlaceholder('密码').fill('wrong-password')
|
||||
await page.getByRole('button', { name: /登\s*录/i }).click()
|
||||
await expect(page.getByText('账号或密码错误')).toBeVisible()
|
||||
})
|
||||
|
||||
test('未登录访问受保护路由:重定向到登录页', async ({ page }) => {
|
||||
await page.goto('/issues')
|
||||
await expect(page).toHaveURL(/\/login/)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
import { createIssueViaApi, deleteIssueViaApi, changeStatusViaApi } from '../utils/api-helpers'
|
||||
|
||||
test.describe('指摘 CRUD', () => {
|
||||
let apiToken = ''
|
||||
let issueId = 0
|
||||
let issueNo = ''
|
||||
|
||||
test.beforeEach(async ({ page, request }) => {
|
||||
const created = await createIssueViaApi(request)
|
||||
issueId = created.issue.id
|
||||
issueNo = created.issue.issueNo
|
||||
apiToken = created.token
|
||||
await uiLogin(page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (issueId) {
|
||||
await deleteIssueViaApi(request, issueId, apiToken).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
test('列表页显示新建的指摘', async ({ page }) => {
|
||||
await page.goto('/issues')
|
||||
await expect(page.getByText('指摘列表')).toBeVisible()
|
||||
await expect(page.getByText(issueNo)).toBeVisible()
|
||||
})
|
||||
|
||||
test('详情页显示完整信息与状态流转历史', async ({ page }) => {
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
await expect(page.getByText(issueNo)).toBeVisible()
|
||||
await expect(page.getByText('状态流转历史')).toBeVisible()
|
||||
await expect(page.getByText('创建指摘').first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('合法状态流转 draft -> open', async ({ page }) => {
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
const dialog = page.getByRole('dialog')
|
||||
await page.getByRole('button', { name: /更改状态/i }).click()
|
||||
await expect(dialog).toBeVisible()
|
||||
await dialog.locator('.ant-select-input').click()
|
||||
const opt = page.locator('.ant-select-item-option[title="待处理"]')
|
||||
await expect(opt).toBeVisible()
|
||||
await opt.click()
|
||||
await expect(dialog.locator('.ant-select-content')).toContainText('待处理')
|
||||
await dialog.getByRole('button', { name: /确认变更/i }).click()
|
||||
await expect(page.getByText('状态已更新')).toBeVisible()
|
||||
})
|
||||
|
||||
test('非法状态流转返回 400 错误提示', async ({ request }) => {
|
||||
const ok = await changeStatusViaApi(request, issueId, apiToken, 'open')
|
||||
expect(ok.status()).toBe(200)
|
||||
const illegal = await changeStatusViaApi(request, issueId, apiToken, 'closed')
|
||||
expect(illegal.status()).toBe(400)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,35 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
import { createIssueViaApi, deleteIssueViaApi } from '../utils/api-helpers'
|
||||
|
||||
test.describe('附件功能', () => {
|
||||
let apiToken = ''
|
||||
let issueId = 0
|
||||
|
||||
test.beforeEach(async ({ page, request }) => {
|
||||
const created = await createIssueViaApi(request)
|
||||
issueId = created.issue.id
|
||||
apiToken = created.token
|
||||
await uiLogin(page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (issueId) {
|
||||
await deleteIssueViaApi(request, issueId, apiToken).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
test('上传附件后列表显示文件信息', async ({ page }) => {
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
await expect(page.getByText('附件 (0)')).toBeVisible()
|
||||
|
||||
await page.setInputFiles('input[type=file]', {
|
||||
name: 'e2e-test.txt',
|
||||
mimeType: 'text/plain',
|
||||
buffer: Buffer.from('IMS E2E attachment content'),
|
||||
})
|
||||
await expect(page.getByText('上传成功')).toBeVisible()
|
||||
await expect(page.getByText(/e2e-test\.txt/)).toBeVisible()
|
||||
await expect(page.getByText(/附件 \(1\)/)).toBeVisible()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
import { createIssueViaApi, deleteIssueViaApi } from '../utils/api-helpers'
|
||||
|
||||
test.describe('Agent 驾驶舱', () => {
|
||||
let apiToken = ''
|
||||
let issueId = 0
|
||||
|
||||
test.beforeEach(async ({ page, request }) => {
|
||||
const created = await createIssueViaApi(request)
|
||||
issueId = created.issue.id
|
||||
apiToken = created.token
|
||||
await uiLogin(page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (issueId) {
|
||||
await deleteIssueViaApi(request, issueId, apiToken).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
test('执行指令后显示工具卡片', async ({ page }) => {
|
||||
test.setTimeout(560000)
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
await expect(page.getByText('IMS Agent 驾驶舱')).toBeVisible()
|
||||
|
||||
await page.getByPlaceholder(/给 Agent 下达指令/).fill('查找知识库相似案例并生成对应方案')
|
||||
await page.getByRole('button', { name: /执行指令/i }).click()
|
||||
|
||||
// 模型输出工具调用后 SSE 渲染工具卡片(真实模型可能只输出 search_knowledge,不强依赖具体工具)
|
||||
await expect(page.getByText(/call:/).first()).toBeVisible({ timeout: 480000 })
|
||||
})
|
||||
|
||||
test('写工具触发审批后可批准', async ({ page }) => {
|
||||
test.setTimeout(480000)
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
await page.getByPlaceholder(/给 Agent 下达指令/).fill('请调用 update_issue 工具,把本指摘的优先级改为 high')
|
||||
await page.getByRole('button', { name: /执行指令/i }).click()
|
||||
|
||||
await expect(page.getByText('需要您的审批')).toBeVisible({ timeout: 420000 })
|
||||
await page.getByRole('button', { name: /批准执行/i }).click()
|
||||
await expect(page.getByText('已批准执行')).toBeVisible({ timeout: 30000 })
|
||||
await expect(page.getByText(/Agent 指令已批准/)).toBeVisible({ timeout: 30000 })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin, apiLogin } from '../utils/auth'
|
||||
import { getDashboardViaApi } from '../utils/api-helpers'
|
||||
|
||||
test.describe('工作台 Dashboard', () => {
|
||||
let apiToken = ''
|
||||
|
||||
test.beforeEach(async ({ request }) => {
|
||||
apiToken = (await apiLogin(request)).accessToken
|
||||
})
|
||||
|
||||
test('登录后工作台显示统计与图表', async ({ page }) => {
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByText('工作台概览')).toBeVisible()
|
||||
|
||||
await expect(page.getByText('Agent 快捷指令')).toBeVisible()
|
||||
await expect(page.getByText('指摘处理趋势')).toBeVisible()
|
||||
await expect(page.getByText('Agent 洞察')).toBeVisible()
|
||||
await expect(page.getByText('最新动态')).toBeVisible()
|
||||
await expect(page.getByText('指摘状态分布')).toBeVisible()
|
||||
})
|
||||
|
||||
test('统计卡显示正确的数量(API 对比)', async ({ page, request }) => {
|
||||
const stats = await getDashboardViaApi(request, apiToken)
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
|
||||
const pending = page.locator('.ant-statistic', { hasText: '待处理指摘' }).first()
|
||||
await expect(pending.getByText('待处理指摘', { exact: true })).toBeVisible()
|
||||
await expect(pending.getByText(String(stats.pendingCount))).toBeVisible()
|
||||
|
||||
await expect(page.locator('.ant-statistic', { hasText: '进行中指摘' }).first()).toBeVisible()
|
||||
await expect(page.locator('.ant-statistic', { hasText: '本月已完成' }).first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('Agent 快捷指令输入框存在且可输入', async ({ page }) => {
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
const input = page.getByPlaceholder(/例如:查找知识库/)
|
||||
await input.fill('测试指令')
|
||||
await expect(input).toHaveValue('测试指令')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
|
||||
test.describe('通知铃铛', () => {
|
||||
test('铃铛显示未读数且下拉展开', async ({ page }) => {
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
|
||||
const bell = page.locator('.ant-badge')
|
||||
await expect(bell.first()).toBeVisible()
|
||||
await bell.first().click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const menu = page.locator('.ant-dropdown')
|
||||
if (await menu.count() > 0) {
|
||||
await expect(menu.first()).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test('通知下拉包含通知标题或空提示', async ({ page }) => {
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
await page.locator('.ant-badge').first().click()
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const hasTitle = await page.getByText('通知').count()
|
||||
const hasEmpty = await page.getByText('暂无通知').count()
|
||||
expect(hasTitle + hasEmpty).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
|
||||
test.describe('新建页 Agent 智能填充', () => {
|
||||
test('点击「让 Agent 智能填充」真实调用 AI 并回填字段', async ({ page }) => {
|
||||
test.setTimeout(420000)
|
||||
await uiLogin(page)
|
||||
await page.goto('/issues/new')
|
||||
|
||||
await page.getByPlaceholder('请输入指摘标题').fill('登录页面在低分辨率下按钮错位、样式异常,影响操作')
|
||||
|
||||
const fillBtn = page.getByRole('button', { name: /让 Agent 智能填充/i })
|
||||
await fillBtn.click()
|
||||
await expect(fillBtn).toHaveClass(/ant-btn-loading/, { timeout: 15000 })
|
||||
// 模型调用耗时波动大(数十秒到数分钟),axios 侧 180s 超时后走降级回填,故等待上限放宽到 240s
|
||||
await expect(fillBtn).not.toHaveClass(/ant-btn-loading/, { timeout: 240000 })
|
||||
|
||||
// 工程阶段/区分/影响度三个 Select 至少被回填 3 个非"请选择"值(AI 成功或降级都会回填)
|
||||
await expect.poll(async () => {
|
||||
return page.locator('.ant-select').evaluateAll(els =>
|
||||
els.filter(e => {
|
||||
const t = e.textContent?.trim() ?? ''
|
||||
return t !== '' && t !== '请选择'
|
||||
}).length)
|
||||
}, { timeout: 30000, intervals: [2000] }).toBeGreaterThanOrEqual(3)
|
||||
|
||||
// 优先级 Radio 也被回填(高/中/低 任一)
|
||||
const checked = (await page.locator('.ant-radio-button-wrapper-checked').allTextContents()).map(s => s.trim()).filter(Boolean)
|
||||
expect(checked.length).toBeGreaterThan(0)
|
||||
console.log('filled selects:', JSON.stringify(checked))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
import { createIssueViaApi, deleteIssueViaApi } from '../utils/api-helpers'
|
||||
|
||||
test.describe('Agent SSE 流式渲染', () => {
|
||||
let apiToken = ''
|
||||
let issueId = 0
|
||||
|
||||
test.beforeEach(async ({ page, request }) => {
|
||||
const created = await createIssueViaApi(request)
|
||||
issueId = created.issue.id
|
||||
apiToken = created.token
|
||||
await uiLogin(page)
|
||||
})
|
||||
|
||||
test.afterEach(async ({ request }) => {
|
||||
if (issueId) {
|
||||
await deleteIssueViaApi(request, issueId, apiToken).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
test('执行指令后 SSE 事件实时渲染 thought / prompt_info / model_info', async ({ page }) => {
|
||||
test.setTimeout(120000)
|
||||
await page.goto(`/issues/${issueId}`)
|
||||
await expect(page.getByText('IMS Agent 驾驶舱')).toBeVisible()
|
||||
|
||||
await page.getByPlaceholder(/给 Agent 下达指令/).fill('查找知识库相似案例并生成对应方案')
|
||||
await page.getByRole('button', { name: /执行指令/i }).click()
|
||||
|
||||
// thought:初始连接消息经 SSE 渲染为绿色状态框
|
||||
await expect(page.getByText(/已连接执行流/)).toBeVisible({ timeout: 20000 })
|
||||
|
||||
// model_info:引擎来源 Tag
|
||||
await expect(page.getByText(/引擎:/)).toBeVisible({ timeout: 20000 })
|
||||
|
||||
// prompt_info:展开模板信息,模板标识非"未提供"
|
||||
await page.getByText('Prompt 模板信息').click()
|
||||
await expect(page.getByText(/系统角色:\s*\S+/)).toBeVisible()
|
||||
await expect(page.getByText(/规划模板:\s*\S+/)).toBeVisible()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { uiLogin } from '../utils/auth'
|
||||
|
||||
test.describe('工作台 Agent 快捷指令执行面板', () => {
|
||||
test('执行「生成本周报告」打开执行流面板并渲染 SSE 事件', async ({ page }) => {
|
||||
await uiLogin(page)
|
||||
await page.goto('/dashboard')
|
||||
await expect(page.getByText('Agent 快捷指令')).toBeVisible()
|
||||
|
||||
const input = page.getByPlaceholder(/例如:查找知识库/)
|
||||
await input.fill('生成本周指摘处理统计报告')
|
||||
await page.getByRole('button', { name: /执\s*行/ }).click()
|
||||
|
||||
await expect(page.getByText(/Agent 执行流/)).toBeVisible({ timeout: 30000 })
|
||||
await expect(page.getByText(/指令已提交,正在连接执行流/)).toBeVisible({ timeout: 30000 })
|
||||
|
||||
await expect(page.getByText(/已连接执行流/)).toBeVisible({ timeout: 60000 })
|
||||
|
||||
const execBox = page.getByText(/Agent 执行流/)
|
||||
await expect(execBox).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user