feat: align controlled AI sessions and MVP controls
This commit is contained in:
@@ -31,6 +31,8 @@ const secondInboxNoteSuggestionId = '019b0000-0000-7000-8000-000000000010'
|
||||
const secondInboxSourceSuggestionId = '019b0000-0000-7000-8000-000000000011'
|
||||
const thirdInboxItemId = '019b0000-0000-7000-8000-000000000013'
|
||||
const thirdInboxTaskSuggestionId = '019b0000-0000-7000-8000-000000000014'
|
||||
const aiSessionId = '019b0000-0000-7000-8000-000000000015'
|
||||
const createdAISessionId = '019b0000-0000-7000-8000-000000000016'
|
||||
const unknownProjectId = '019b0000-0000-7000-8000-000000000099'
|
||||
const externalProjectId = '019b0000-0000-7000-8000-000000000088'
|
||||
const externalTaskId = '019b0000-0000-7000-8000-000000000089'
|
||||
@@ -40,6 +42,7 @@ const workspaceRequests = []
|
||||
const projectPatchRequests = []
|
||||
const inboxAnalyzeRequests = []
|
||||
const inboxConfirmRequests = []
|
||||
const aiSessionRequests = []
|
||||
let expectingProjectPatchError = false
|
||||
let expectedProjectPatchConsoleErrorCount = 0
|
||||
let expectingInboxConfirmError = false
|
||||
@@ -55,6 +58,17 @@ let failNextInboxWorkspaceRefresh = false
|
||||
let conflictNextInboxConfirm = false
|
||||
let validationNextInboxConfirm = false
|
||||
let invalidResponseNextInboxConfirm = false
|
||||
const controlledAISessions = [
|
||||
{
|
||||
id: aiSessionId,
|
||||
projectId,
|
||||
title: '项目风险梳理',
|
||||
context: '只维护会话上下文,不创建正式对象。',
|
||||
status: 'ready',
|
||||
createdAt: '2026-07-21T03:00:00Z',
|
||||
updatedAt: '2026-07-21T03:00:00Z',
|
||||
},
|
||||
]
|
||||
page.on('console', (message) => {
|
||||
if (message.type() !== 'error') return
|
||||
if (
|
||||
@@ -217,6 +231,27 @@ await page.route('http://localhost:9150/api/v1/**', async (route) => {
|
||||
await route.fulfill({ json: secondVisualCheckWorkspace })
|
||||
return
|
||||
}
|
||||
if (url.pathname === `/api/v1/projects/${projectId}/ai-sessions` && method === 'GET') {
|
||||
await new Promise((resolve) => setTimeout(resolve, 120))
|
||||
await route.fulfill({ json: controlledAISessions })
|
||||
return
|
||||
}
|
||||
if (url.pathname === `/api/v1/projects/${projectId}/ai-sessions` && method === 'POST') {
|
||||
const input = route.request().postDataJSON()
|
||||
aiSessionRequests.push(input)
|
||||
const created = {
|
||||
id: createdAISessionId,
|
||||
projectId,
|
||||
title: input.title,
|
||||
context: input.context,
|
||||
status: 'ready',
|
||||
createdAt: '2026-07-21T03:10:00Z',
|
||||
updatedAt: '2026-07-21T03:10:00Z',
|
||||
}
|
||||
controlledAISessions.unshift(created)
|
||||
await route.fulfill({ status: 201, json: created })
|
||||
return
|
||||
}
|
||||
if (url.pathname === `/api/v1/inbox/${inboxItemId}/analyze` && method === 'POST') {
|
||||
inboxAnalyzeRequests.push(inboxItemId)
|
||||
if (inboxAnalyzeRequests.length === 1) await new Promise((resolve) => setTimeout(resolve, 250))
|
||||
@@ -487,6 +522,10 @@ await page.locator('.dashboard-button[title="探索"]').click()
|
||||
await page.waitForTimeout(500)
|
||||
await page.screenshot({ path: 'test-results/workspace-explore-react-acro-light.png', fullPage: true })
|
||||
const workspaceExploreMetrics = await collectMetrics()
|
||||
const workspaceExploreControls = await page.evaluate(() => ({
|
||||
emptyState: document.querySelector('.workspace-explore-page')?.textContent ?? '',
|
||||
buttonCount: document.querySelectorAll('.workspace-explore-page button').length,
|
||||
}))
|
||||
|
||||
await page.locator('.project-button').first().click()
|
||||
await page.waitForTimeout(500)
|
||||
@@ -924,13 +963,22 @@ if (await inboxChannelButton.count() === 0) {
|
||||
const channelPageChecks = []
|
||||
for (const channel of [
|
||||
{ label: '工作计划', pageClass: 'project-tasks-page', expectedHeading: '工作计划' },
|
||||
{ label: 'AI 助手', pageClass: 'project-ai-page', expectedHeading: '选择专家,开始对话' },
|
||||
{ label: 'AI 助手', pageClass: 'project-ai-page', expectedHeading: 'AI 助手' },
|
||||
{ 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)
|
||||
if (channel.label === 'AI 助手') {
|
||||
const aiPage = page.locator('.project-ai-page')
|
||||
await aiPage.getByText('项目风险梳理', { exact: true }).waitFor({ state: 'visible', timeout: 2000 })
|
||||
await aiPage.locator('input').fill('新会话视觉检查')
|
||||
await aiPage.locator('textarea').fill('只创建受控会话入口')
|
||||
await aiPage.getByRole('button', { name: '创建会话', exact: true }).click()
|
||||
await aiPage.getByText('新会话视觉检查', { exact: true }).waitFor({ state: 'visible', timeout: 2000 })
|
||||
await page.screenshot({ path: 'test-results/project-ai-controlled.png', fullPage: true })
|
||||
}
|
||||
channelPageChecks.push(await page.evaluate((expected) => {
|
||||
const pageNode = document.querySelector(`.${expected.pageClass}`)
|
||||
const heading = pageNode?.querySelector('h2, h3, h4, h5')?.textContent ?? ''
|
||||
@@ -944,6 +992,13 @@ for (const channel of [
|
||||
}, channel))
|
||||
}
|
||||
|
||||
const unsupportedControlMetrics = await page.evaluate(() => ({
|
||||
topbarLabels: [...document.querySelectorAll('.topbar-actions button')].map((button) => button.getAttribute('aria-label') ?? button.textContent?.trim()),
|
||||
statusbarText: document.querySelector('.statusbar')?.textContent ?? '',
|
||||
newChannelText: document.querySelector('.project-new-channel-page')?.textContent ?? '',
|
||||
newChannelButtonCount: document.querySelectorAll('.project-new-channel-page button').length,
|
||||
}))
|
||||
|
||||
await page.getByRole('button', { name: '切换深色模式', exact: true }).click()
|
||||
await page.screenshot({ path: 'test-results/project-react-acro-dark.png', fullPage: true })
|
||||
|
||||
@@ -953,12 +1008,15 @@ await server.close()
|
||||
console.log(JSON.stringify({
|
||||
workspaceMetrics,
|
||||
workspaceExploreMetrics,
|
||||
workspaceExploreControls,
|
||||
projectMetrics,
|
||||
mobileProjectMetrics,
|
||||
channelSidebarHoverMetrics,
|
||||
stageHoverMetrics,
|
||||
channelListHoverMetrics,
|
||||
channelPageChecks,
|
||||
unsupportedControlMetrics,
|
||||
aiSessionRequests,
|
||||
errors,
|
||||
}, null, 2))
|
||||
|
||||
@@ -1038,6 +1096,8 @@ if (
|
||||
}
|
||||
|
||||
if (!workspaceExploreMetrics.workspaceExploreButtonActive) failures.push('expected fixed workspace explore button to become active when selected')
|
||||
if (!workspaceExploreControls.emptyState.includes('暂未开放')) failures.push('workspace explore must present the unsupported feature as 暂未开放')
|
||||
if (workspaceExploreControls.buttonCount !== 0) failures.push(`workspace explore must not render active controls, got ${workspaceExploreControls.buttonCount}`)
|
||||
if (workspaceExploreMetrics.dashboardButtonActive) failures.push('expected dashboard button not to be active on workspace explore page')
|
||||
if (workspaceExploreMetrics.channelSidebar) failures.push(`expected workspace explore to hide channel sidebar, got ${JSON.stringify(workspaceExploreMetrics.channelSidebar)}`)
|
||||
if (workspaceExploreMetrics.inspector) failures.push(`expected workspace explore to hide inspector, got ${JSON.stringify(workspaceExploreMetrics.inspector)}`)
|
||||
@@ -1110,6 +1170,23 @@ for (const check of channelPageChecks) {
|
||||
}
|
||||
}
|
||||
|
||||
if (aiSessionRequests.length !== 1) failures.push(`expected one controlled AI session request, got ${aiSessionRequests.length}`)
|
||||
if (aiSessionRequests.length === 1) {
|
||||
const requestKeys = Object.keys(aiSessionRequests[0]).sort()
|
||||
if (JSON.stringify(requestKeys) !== JSON.stringify(['context', 'title'])) {
|
||||
failures.push(`AI session create must send only title/context, got ${JSON.stringify(requestKeys)}`)
|
||||
}
|
||||
}
|
||||
if (unsupportedControlMetrics.topbarLabels.some((label) => label?.includes('停靠'))) {
|
||||
failures.push(`topbar must not expose dock controls, got ${JSON.stringify(unsupportedControlMetrics.topbarLabels)}`)
|
||||
}
|
||||
if (/升级|支付|AI 空闲|GB 可用/.test(unsupportedControlMetrics.statusbarText)) {
|
||||
failures.push(`statusbar contains unsupported product state: ${unsupportedControlMetrics.statusbarText}`)
|
||||
}
|
||||
if (!unsupportedControlMetrics.newChannelText.includes('暂未开放') || unsupportedControlMetrics.newChannelButtonCount !== 0) {
|
||||
failures.push(`new channel must be a non-interactive 暂未开放 state, got ${JSON.stringify(unsupportedControlMetrics)}`)
|
||||
}
|
||||
|
||||
if (failures.length) {
|
||||
throw new Error(failures.join('\n'))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user