feat(web): refine AI sessions and task filtering
This commit is contained in:
@@ -37,11 +37,14 @@ const visualCheckWorkspace = {
|
||||
{ id: 'overview', projectId, type: 'overview', title: '概况', icon: 'home', count: 0, url: '', sortOrder: 0 },
|
||||
{ id: 'inbox', projectId, type: 'inbox', title: 'Inbox 消息流', icon: 'email', count: 2, url: '', sortOrder: 1 },
|
||||
{ id: 'tasks', projectId, type: 'tasks', title: '工作计划', icon: 'list', count: 0, url: '', sortOrder: 1 },
|
||||
{ id: 'ai', projectId, type: 'ai_sessions', title: 'AI 对话', icon: 'robot', count: 0, url: '', sortOrder: 2 },
|
||||
{ id: 'ai', projectId, type: 'ai_sessions', title: 'AI 会话', icon: 'robot', count: 0, url: '', sortOrder: 2 },
|
||||
{ id: 'notes', projectId, type: 'notes_sources', title: '笔记资料', icon: 'file', count: 0, url: '', sortOrder: 3 },
|
||||
{ id: 'cron', projectId, type: 'cron', title: '计划任务', icon: 'clock', count: 0, url: '', sortOrder: 4 },
|
||||
],
|
||||
tags: [{ id: '019b0000-0000-7000-8000-000000000002', name: '产品' }],
|
||||
tags: [
|
||||
{ id: '019b0000-0000-7000-8000-000000000002', name: '产品' },
|
||||
{ id: '019b0000-0000-7000-8000-000000000005', name: '设计' },
|
||||
],
|
||||
recentSessions: [],
|
||||
inbox: [
|
||||
{
|
||||
@@ -65,7 +68,50 @@ const visualCheckWorkspace = {
|
||||
time: '2026-07-22T07:00:00Z',
|
||||
},
|
||||
],
|
||||
tasks: [],
|
||||
tasks: [
|
||||
{
|
||||
id: '019b0000-0000-7000-8000-000000000006',
|
||||
projectId,
|
||||
title: '梳理产品需求',
|
||||
summary: '确认首版工作台的功能边界。',
|
||||
completed: false,
|
||||
owner: '森林AI',
|
||||
createdAt: '2026-07-22',
|
||||
completedAt: '',
|
||||
duration: '',
|
||||
progress: '40%',
|
||||
tagId: '019b0000-0000-7000-8000-000000000002',
|
||||
tag: '产品',
|
||||
},
|
||||
{
|
||||
id: '019b0000-0000-7000-8000-000000000007',
|
||||
projectId,
|
||||
title: '优化会话界面',
|
||||
summary: '统一 AI 会话的阅读与输入体验。',
|
||||
completed: false,
|
||||
owner: '森林AI',
|
||||
createdAt: '2026-07-22',
|
||||
completedAt: '',
|
||||
duration: '',
|
||||
progress: '60%',
|
||||
tagId: '019b0000-0000-7000-8000-000000000005',
|
||||
tag: '设计',
|
||||
},
|
||||
{
|
||||
id: '019b0000-0000-7000-8000-000000000008',
|
||||
projectId,
|
||||
title: '确认项目定位',
|
||||
summary: '完成项目功能与亮点说明。',
|
||||
completed: true,
|
||||
owner: '森林AI',
|
||||
createdAt: '2026-07-21',
|
||||
completedAt: '2026-07-22',
|
||||
duration: '1 天',
|
||||
progress: '100%',
|
||||
tagId: '019b0000-0000-7000-8000-000000000002',
|
||||
tag: '产品',
|
||||
},
|
||||
],
|
||||
aiSessions: [],
|
||||
notesSources: [],
|
||||
cronPlans: [],
|
||||
@@ -90,7 +136,32 @@ await page.route('http://localhost:9150/api/v1/**', async (route) => {
|
||||
return
|
||||
}
|
||||
if (url.pathname === `/api/v1/projects/${projectId}/ai-sessions`) {
|
||||
await route.fulfill({ json: [] })
|
||||
if (route.request().method() === 'POST') {
|
||||
const input = route.request().postDataJSON()
|
||||
await route.fulfill({
|
||||
json: {
|
||||
id: '019b0000-0000-7000-8000-000000000010',
|
||||
projectId,
|
||||
title: input.title,
|
||||
context: input.context,
|
||||
status: 'ready',
|
||||
createdAt: '2026-07-22T09:00:00Z',
|
||||
updatedAt: '2026-07-22T09:00:00Z',
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
await route.fulfill({
|
||||
json: [{
|
||||
id: '019b0000-0000-7000-8000-000000000009',
|
||||
projectId,
|
||||
title: '报价分析',
|
||||
context: '分析当前方案的报价结构。',
|
||||
status: 'ready',
|
||||
createdAt: '2026-07-22T08:30:00Z',
|
||||
updatedAt: '2026-07-22T08:30:00Z',
|
||||
}],
|
||||
})
|
||||
return
|
||||
}
|
||||
await route.fulfill({ status: 404, json: { error: { code: 'not_found', message: '视觉检查未配置该接口' } } })
|
||||
@@ -238,16 +309,18 @@ await page.locator('.channel-list').hover()
|
||||
const channelListHoverMetrics = await collectMetrics()
|
||||
|
||||
const channelPageChecks = []
|
||||
let taskTagFilterCheck = null
|
||||
let aiSessionCheck = null
|
||||
for (const channel of [
|
||||
{ label: '工作计划', pageClass: 'project-tasks-page', expectedHeading: '工作计划' },
|
||||
{ label: 'AI 对话', pageClass: 'project-ai-page', expectedHeading: 'AI 对话' },
|
||||
{ label: 'AI 会话', pageClass: 'project-ai-page', expectedHeading: '' },
|
||||
{ label: '笔记资料', pageClass: 'project-notes-page', expectedHeading: '笔记资料' },
|
||||
{ label: '计划任务', pageClass: 'project-cron-page', expectedHeading: '计划任务' },
|
||||
{ label: '新建频道', pageClass: 'project-new-channel-page', expectedHeading: '新建频道' },
|
||||
]) {
|
||||
await page.locator('.channel-button', { hasText: channel.label }).click()
|
||||
await page.waitForTimeout(250)
|
||||
channelPageChecks.push(await page.evaluate((expected) => {
|
||||
const pageCheck = await page.evaluate((expected) => {
|
||||
const pageNode = document.querySelector(`.${expected.pageClass}`)
|
||||
const heading = pageNode?.querySelector('h4, h5')?.textContent ?? ''
|
||||
const activeChannel = document.querySelector('.channel-button.active')?.textContent ?? ''
|
||||
@@ -258,8 +331,37 @@ for (const channel of [
|
||||
activeChannel,
|
||||
inputCount: pageNode?.querySelectorAll('input, textarea').length ?? 0,
|
||||
hasSaveChannel: [...(pageNode?.querySelectorAll('button') ?? [])].some((button) => button.textContent?.includes('保存频道')),
|
||||
hasOverviewHead: Boolean(pageNode?.querySelector('.overview-head')),
|
||||
hasSessionList: Boolean(pageNode?.querySelector('.agent-session-list')),
|
||||
hasComposer: Boolean(pageNode?.querySelector('.agent-composer')),
|
||||
}
|
||||
}, channel))
|
||||
}, channel)
|
||||
|
||||
if (channel.label === '工作计划') {
|
||||
await page.getByRole('button', { name: '设计', exact: true }).click()
|
||||
await page.waitForTimeout(150)
|
||||
taskTagFilterCheck = await page.evaluate(() => ({
|
||||
activeTag: document.querySelector('.task-tag-filter.active')?.textContent?.trim() ?? '',
|
||||
pendingTitles: [...document.querySelectorAll('.task-plan-title')].map((node) => node.textContent?.trim()),
|
||||
completedTitles: [...document.querySelectorAll('.completed-plan-title')].map((node) => node.textContent?.trim()),
|
||||
pressed: document.querySelector('.task-tag-filter[aria-pressed="true"]')?.textContent?.trim() ?? '',
|
||||
}))
|
||||
await page.screenshot({ path: 'test-results/project-task-tag-filter-light.png', fullPage: true })
|
||||
}
|
||||
|
||||
if (channel.label === 'AI 会话') {
|
||||
await page.locator('.agent-composer textarea').fill('请分析这个项目的下一步计划')
|
||||
await page.getByRole('button', { name: '发送消息' }).click()
|
||||
await page.waitForSelector('.agent-user-message')
|
||||
aiSessionCheck = await page.evaluate(() => ({
|
||||
userMessage: document.querySelector('.agent-user-message')?.textContent?.trim() ?? '',
|
||||
activeSession: document.querySelector('.agent-session.active')?.textContent?.trim() ?? '',
|
||||
composerValue: document.querySelector('.agent-composer textarea')?.value ?? '',
|
||||
}))
|
||||
await page.screenshot({ path: 'test-results/project-ai-session-light.png', fullPage: true })
|
||||
}
|
||||
|
||||
channelPageChecks.push(pageCheck)
|
||||
}
|
||||
|
||||
await page.locator('.topbar-actions .arco-btn').first().click()
|
||||
@@ -280,6 +382,8 @@ console.log(JSON.stringify({
|
||||
stageHoverMetrics,
|
||||
channelListHoverMetrics,
|
||||
channelPageChecks,
|
||||
taskTagFilterCheck,
|
||||
aiSessionCheck,
|
||||
errors,
|
||||
}, null, 2))
|
||||
|
||||
@@ -439,7 +543,7 @@ for (const check of channelPageChecks) {
|
||||
if (!check.foundPage) {
|
||||
failures.push(`expected ${check.label} to render .${check.pageClass}`)
|
||||
}
|
||||
if (!check.heading.includes(check.expectedHeading)) {
|
||||
if (check.expectedHeading && !check.heading.includes(check.expectedHeading)) {
|
||||
failures.push(`expected ${check.label} heading to include ${check.expectedHeading}, got ${JSON.stringify(check.heading)}`)
|
||||
}
|
||||
if (!check.activeChannel.includes(check.label)) {
|
||||
@@ -448,6 +552,27 @@ for (const check of channelPageChecks) {
|
||||
if (check.label === '新建频道' && (check.inputCount < 4 || !check.hasSaveChannel)) {
|
||||
failures.push(`expected legacy new channel editor, got inputs=${check.inputCount}, save=${check.hasSaveChannel}`)
|
||||
}
|
||||
if (check.label === 'AI 会话' && (check.hasOverviewHead || !check.hasSessionList || !check.hasComposer)) {
|
||||
failures.push(`expected headerless AI session chat layout, got ${JSON.stringify(check)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!taskTagFilterCheck ||
|
||||
taskTagFilterCheck.activeTag !== '设计' ||
|
||||
taskTagFilterCheck.pressed !== '设计' ||
|
||||
taskTagFilterCheck.pendingTitles.join('|') !== '优化会话界面' ||
|
||||
taskTagFilterCheck.completedTitles.length !== 0
|
||||
) {
|
||||
failures.push(`expected clickable task tags to filter pending and completed plans, got ${JSON.stringify(taskTagFilterCheck)}`)
|
||||
}
|
||||
if (
|
||||
!aiSessionCheck ||
|
||||
aiSessionCheck.userMessage !== '请分析这个项目的下一步计划' ||
|
||||
!aiSessionCheck.activeSession.includes('请分析这个项目的下一步计划') ||
|
||||
aiSessionCheck.composerValue !== ''
|
||||
) {
|
||||
failures.push(`expected AI composer to create and select a session, got ${JSON.stringify(aiSessionCheck)}`)
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
|
||||
Reference in New Issue
Block a user