mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 05:31:59 +00:00
前端:审批卡按审批当场拼出待办(不再为一张卡取一次待办,收到中断就出卡;有 task_id 走待办接口,没有走老的审批接口);矮窗口导航两档高度不再叠加,480px 放得下九项;待办数随目录启动时就取、外壳订阅时当场记底,派给自己的新待办通知不再漏;头一回取目录的卡住从启动算起;外壳横向裁掉悬停提示的占位;证据抽屉同一指标在取时不另发请求;编辑器去掉第二个 main、检查一次只跑一遍;记录页对不上待办的审批照旧画审批卡,待办行写 runName 和节点名;检查脚本按新页面更新断言(沙箱没有待办时用合成的两条、未保存与已删除工作流分开认、生效记录页签、能力子页签)
This commit is contained in:
@@ -285,11 +285,13 @@ await section('检查:和发布同一套规则,点一条定位到组件', as
|
||||
check('列出错误和提醒', await waitFor(page, async () => await issues.count() === 3))
|
||||
await page.locator('[data-app-issue="app.binding_target_missing"] [data-app-issue-locate]').click()
|
||||
await page.waitForURL('**/edit/pages')
|
||||
check('点「定位」:跳到页面一节、选中那个组件(点号加下标的 path)', await page.locator('[data-app-inspector="gmv"]').count() === 1)
|
||||
// 页面一节要等组件目录到了才画页面树和属性面板:等它出来再判,别读到骨架屏
|
||||
const shows = (sel) => page.locator(sel).waitFor({ timeout: 4000 }).then(async () => await page.locator(sel).count() === 1, () => false)
|
||||
check('点「定位」:跳到页面一节、选中那个组件(点号加下标的 path)', await shows('[data-app-inspector="gmv"]'))
|
||||
await page.goto(`${WEB}/apps/app-ae/edit/check`, { waitUntil: 'networkidle' })
|
||||
await page.locator('[data-app-issue="app.expr_arith"] [data-app-issue-locate]').click()
|
||||
await page.waitForURL('**/edit/pages')
|
||||
check('JSON 指针的 path 也认得', await page.locator('[data-app-inspector="warn"]').count() === 1)
|
||||
check('JSON 指针的 path 也认得', await shows('[data-app-inspector="warn"]'))
|
||||
})
|
||||
|
||||
await section('发布:固定版本升了版,必须为每一项选处置', async () => {
|
||||
|
||||
@@ -945,9 +945,11 @@ await section('正式运行:入口看 published_version;运行期间画布
|
||||
await page.waitForTimeout(400)
|
||||
const pop = (await page.locator('.sf-pop').innerText()).replace(/\s+/g, ' ')
|
||||
check('提示不含画布改动', pop.includes('不含画布改动'), pop.slice(0, 80))
|
||||
// 发起表单统一用 SchemaForm 画(X2):字段按名字挂 data-field,id 是 run-formal-<字段名>
|
||||
check('表单字段取已发布版本的(topic),不取画布的(q)',
|
||||
await page.locator('.sf-pop textarea#run-field-topic').count() === 1
|
||||
&& await page.locator('.sf-pop textarea#run-field-q').count() === 0)
|
||||
await page.locator('.sf-pop [data-launch-form] [data-field="topic"] #run-formal-topic').count() === 1
|
||||
&& await page.locator('.sf-pop [data-field="q"]').count() === 0,
|
||||
(await page.locator('.sf-pop [data-field]').evaluateAll((els) => els.map((e) => e.getAttribute('data-field')))).join(','))
|
||||
const formalPop = await marks('.sf-pop')
|
||||
check('正式运行浮层的标题和确认按钮都是实心圆点', formalPop.formal === 2 && formalPop.exploratory === 0 && formalPop.shield === 0,
|
||||
JSON.stringify(formalPop))
|
||||
|
||||
@@ -191,13 +191,17 @@ const browser = await chromium.launch({ executablePath: CHROME })
|
||||
// ================================================================
|
||||
await section('能力列表:效果、筛选、同步', async () => {
|
||||
const { page, state, errors } = await open('/tools/capabilities')
|
||||
check('工具页有「能力」页签', await page.locator('[role=tab]', { hasText: '能力' }).count() === 1)
|
||||
// 「能力」页签里还有「全部能力 / 待审阅」两个子页签(D-i),按名字整字匹配
|
||||
check('工具页有「能力」页签', await page.getByRole('tab', { name: '能力', exact: true }).count() === 1,
|
||||
(await page.locator('[role=tab]').allInnerTexts()).join(' | '))
|
||||
check('列出全部能力', await page.locator('[data-capability]').count() === CAPS.length)
|
||||
const pay = page.locator('[data-capability="erp_pay"] [data-capability-effect]')
|
||||
check('治理后的效果和同步得出的不一致时标出来', (await pay.innerText()).includes('写入') && (await pay.innerText()).includes('*'))
|
||||
check('没治理过的按同步得出的效果显示', (await page.locator('[data-capability="erp_query"] [data-capability-effect]').innerText()).includes('只读'))
|
||||
await page.locator('[data-capability="mcp:crm/find"]').click()
|
||||
check('能力名带斜杠也能打开(地址转义)', await page.locator('[data-capability-editor="mcp:crm/find"]').count() === 1)
|
||||
// 右栏的治理设置是点开之后另取的:等它出来再判
|
||||
check('能力名带斜杠也能打开(地址转义)', await waitFor(page, async () =>
|
||||
await page.locator('[data-capability-editor="mcp:crm/find"]').count() === 1), page.url())
|
||||
await page.locator('[data-capability-sync]').click()
|
||||
check('立即同步', await waitFor(page, async () => state.syncs === 1))
|
||||
check('没有运行时报错', errors.length === 0, errors.slice(0, 2).join(' | '))
|
||||
@@ -273,7 +277,8 @@ await section('能力:待审阅复用数据目录的审阅队列,成批确
|
||||
check('冲突的那项照实标出(说的是能力,不是表)', await waitFor(page, async () =>
|
||||
(await page.locator('[data-review-summary]').innerText()).includes('所在能力刚被修改过')))
|
||||
await rows.first().locator('[data-review-open]').click()
|
||||
check('点开一项:右栏是这个能力的治理设置,标出那个位置', await page.locator('[data-capability-editor="erp_pay"] [data-capability-focus="/amount"]').count() === 1)
|
||||
check('点开一项:右栏是这个能力的治理设置,标出那个位置', await waitFor(page, async () =>
|
||||
await page.locator('[data-capability-editor="erp_pay"] [data-capability-focus="/amount"]').count() === 1), page.url())
|
||||
check('没有运行时报错', errors.length === 0, errors.slice(0, 2).join(' | '))
|
||||
})
|
||||
|
||||
|
||||
@@ -1994,14 +1994,17 @@ await section('应用平台:发布成功做成应用;已发布版本的使
|
||||
})
|
||||
await page.route(/\/api\/ledger\/workflow-versions\/.+$/, (route) =>
|
||||
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(usage) }))
|
||||
await page.keyboard.press('Alt+h')
|
||||
// 编排页一进来焦点在助手输入框里,那时的 ⌥H 是打字:先把焦点放回页面(同 check-studio)
|
||||
await page.evaluate(() => document.activeElement?.blur?.())
|
||||
await page.keyboard.press('Alt+KeyH')
|
||||
const sheet = page.locator('[role="dialog"][aria-label="版本历史"]')
|
||||
await sheet.waitFor()
|
||||
check('版本列表标出已退役的版本', await sheet.locator('[data-version-retired-chip]').count() === 1)
|
||||
await sheet.locator('ol button', { hasText: 'v4' }).first().click()
|
||||
const cur = sheet.locator('[data-version-lifecycle="4"]')
|
||||
check('展开当前发布版:写使用情况', await waitFor(page, async () => await cur.count() === 1)
|
||||
&& (await cur.innerText()).includes('被 1 个应用固定'))
|
||||
// 使用情况是展开之后另取的(账本接口):等它回来再读,别读到占位的「…」
|
||||
check('展开当前发布版:写使用情况', await waitFor(page, async () => await cur.count() === 1
|
||||
&& (await cur.innerText().catch(() => '')).includes('被 1 个应用固定')), await cur.innerText().catch(() => ''))
|
||||
check('……当前发布版的退役按钮不可点,写明原因', await cur.locator('[data-version-retire]').isDisabled()
|
||||
&& (await cur.innerText()).includes('当前发布版不能退役'))
|
||||
await sheet.locator('ol button', { hasText: 'v3' }).first().click()
|
||||
|
||||
+71
-12
@@ -131,6 +131,28 @@ const listTop = await getJson('/runs?limit=50')
|
||||
const wfHost = (await getJson('/workflows'))[0]
|
||||
const base = allRuns.find((r) => r.status === 'succeeded' && !r.workflow_id)
|
||||
|
||||
/**
|
||||
* 合成的两条待处理待办(沙箱里一条都没有时用):挂在沙箱里真实的运行上——最近的一条,和排在第 100 名之后的一条
|
||||
* (原来只取 100 条时找不到的那种)。一条等了 8 天,一条刚来
|
||||
*/
|
||||
function syntheticTasks() {
|
||||
const picks = [allRuns[0], allRuns[Math.min(allRuns.length - 1, 120)]].filter((r, i, a) => r && a.indexOf(r) === i)
|
||||
return picks.map((r, i) => {
|
||||
const at = new Date(Date.now() - (i ? 8 * 86400_000 : 3 * 60_000)).toISOString()
|
||||
return { id: `fake0task0list${i}`, approval_id: `fake0appr0list${i}`, run_id: r.id, node_id: 'gate', node_label: '人工审批',
|
||||
run_class: r.run_class ?? 'exploratory', workflow_id: r.workflow_id ?? null, workflow_name: r.workflow_name || '订单日报',
|
||||
app_id: null, app_version: null, kind: i ? 'input' : 'approve', title: i ? '补一下预算上限' : '这份报表可以发出吗?', form: null,
|
||||
policy: { kind: 'any' }, status: 'pending', due_at: null, escalation_level: 0, payload: {}, can_respond: true,
|
||||
assignments: [{ id: `fake0as${i}`, principal_kind: 'anyone', principal: '', source: 'anyone', status: 'pending',
|
||||
responded_by: null, responded_at: null, verified: false }],
|
||||
outcome: null, resolved_by: null, resolved_at: null, created_at: at }
|
||||
})
|
||||
}
|
||||
const syntheticApprovals = (tasks) => tasks.map((t) => ({ id: t.approval_id, run_id: t.run_id, node_id: t.node_id,
|
||||
mode: t.kind === 'input' ? 'input' : 'approve', title: t.title, payload: { kind: 'human_node' }, status: 'pending', response: {},
|
||||
created_at: t.created_at, workflow_id: t.workflow_id, workflow_name: t.workflow_name, node_label: t.node_label,
|
||||
run_status: 'interrupted', run_class: t.run_class, task_id: t.id }))
|
||||
|
||||
// ------------------------------------------------------------------ 页签
|
||||
|
||||
await section('页签与地址', async () => {
|
||||
@@ -182,30 +204,50 @@ await section('待办页签', async () => {
|
||||
ws.send(JSON.stringify({ seq: lastSeq + 2, type: 'node.started', node_id: target.node_id, ts: t + 0.05, data: { resumed: true } }))
|
||||
})
|
||||
}
|
||||
// 沙箱是开发库的拷贝:手上的待办刚好都处理完时一条都没有,列表这几项就只剩空态可看。那时拿两条合成的待办来验列表
|
||||
// (挂在沙箱里真实的运行上,一条排在最近 100 条之外);点进详情、别处批掉这几段要真待审批,照旧只在有的时候跑
|
||||
const synthetic = !pendingTasks.length && allRuns.length > 0
|
||||
const listTasks = synthetic ? syntheticTasks() : pendingTasks
|
||||
if (synthetic) {
|
||||
console.log(' · 沙箱里没有待处理的待办:列表这几项用合成的 2 条')
|
||||
fakes.set('GET /api/tasks', (url) => ({ status: 200, json: { items: url.searchParams.get('run_id') ? [] : listTasks } }))
|
||||
fakes.set('GET /api/approvals', (url) => ({ status: 200, json: url.searchParams.get('run_id') ? [] : syntheticApprovals(listTasks) }))
|
||||
}
|
||||
await page.goto(`${WEB}/runs?tab=approvals`, { waitUntil: 'networkidle' })
|
||||
await page.waitForTimeout(400)
|
||||
check('页签叫「待办」', (await page.locator('[role=tab][data-tab=approvals]').innerText()).includes('待办'))
|
||||
const taskRows = page.locator('[data-tasks-list] [data-task-id]')
|
||||
const shownTasks = await taskRows.evaluateAll((els) => els.map((e) => e.getAttribute('data-task-id')))
|
||||
check('沙箱里不属于应用的待办都在页签里', pendingTasks.length > 0 && pendingTasks.every((t) => shownTasks.includes(t.id)),
|
||||
`API ${pendingTasks.length} 条 / 页面 ${shownTasks.length} 条`)
|
||||
check(synthetic ? '不属于应用的待办都在页签里(合成的 2 条)' : '沙箱里不属于应用的待办都在页签里',
|
||||
listTasks.length > 0 && listTasks.every((t) => shownTasks.includes(t.id)),
|
||||
`API ${listTasks.length} 条 / 页面 ${shownTasks.length} 条`)
|
||||
check('存量的待审批都补建了待办', pending.every((a) => pendingTasks.some((t) => t.approval_id === a.id)),
|
||||
`${pending.filter((a) => !pendingTasks.some((t) => t.approval_id === a.id)).length} 条没有对应的待办`)
|
||||
const deep = pendingTasks.filter((t) => allRuns.findIndex((r) => r.id === t.run_id) >= 100)
|
||||
const deep = listTasks.filter((t) => allRuns.findIndex((r) => r.id === t.run_id) >= 100)
|
||||
check('排在最近 100 条之外的待办也看得到(原来的死路)', deep.every((t) => shownTasks.includes(t.id)),
|
||||
`${deep.length} 条在第 100 名之后`)
|
||||
check('每条都写着等了多久', await page.locator('[data-tasks-list] [data-task-age]').count() === shownTasks.length)
|
||||
check('每条标着待办的种类', await page.locator('[data-tasks-list] [data-task-kind-chip]').count() === shownTasks.length)
|
||||
if (pendingTasks.length) {
|
||||
const rowText = await page.locator(`[data-task-id="${pendingTasks[0].id}"]`).innerText()
|
||||
const hasWf = rowText.includes(pendingTasks[0].workflow_name ?? '')
|
||||
if (listTasks.length) {
|
||||
const first = listTasks[0]
|
||||
const rowText = await page.locator(`[data-task-id="${first.id}"]`).innerText()
|
||||
// 应用里的写应用名;没保存就跑的写「未保存的工作流」、不露「临时图」(同审批行的 runName)
|
||||
const unsaved = !first.workflow_id && (!first.workflow_name || ['临时图', '未保存的工作流'].includes(first.workflow_name))
|
||||
const wfName = first.app_name || (unsaved ? '未保存的工作流' : first.workflow_name ?? '')
|
||||
const hasWf = rowText.includes(wfName) && !rowText.includes('临时图')
|
||||
const hasNode = rowText.includes(`「${first.node_label ?? first.node_id}」`)
|
||||
// 名字来自沙箱库,只报在不在
|
||||
check('每条写着工作流名', hasWf, `工作流名 ${hasWf ? '在' : '缺'}`)
|
||||
check('每条写着工作流名和节点(节点写名字,不写 id)', hasWf && hasNode,
|
||||
`工作流名 ${hasWf ? '在' : '缺'} · 节点 ${hasNode ? '在' : '缺'}`)
|
||||
check('每条有处理入口', rowText.includes('去处理'))
|
||||
}
|
||||
check('列表下面写明应用里的待办在哪里处理', (await page.locator('[data-tasks-list]').innerText().catch(() => '')).includes('应用 → 待办'))
|
||||
const badge = await page.locator('[role=tab][data-tab=approvals]').innerText()
|
||||
check('页签上的数和待办条数一致', badge.includes(String(pendingTasks.length)), badge.replace(/\s+/g, ' '))
|
||||
check('页签上的数和待办条数一致', badge.includes(String(listTasks.length)), badge.replace(/\s+/g, ' '))
|
||||
if (synthetic) {
|
||||
fakes.delete('GET /api/tasks')
|
||||
fakes.delete('GET /api/approvals')
|
||||
}
|
||||
if (pending.length) {
|
||||
const target = pending[0]
|
||||
const task = pendingTasks.find((t) => t.approval_id === target.id)
|
||||
@@ -1563,8 +1605,21 @@ await section('工作流筛选:未保存的工作流', async () => {
|
||||
if (wfOptions.includes('__none__')) {
|
||||
await listAfter(() => page.locator('[data-runs-workflow]').selectOption('__none__'), (p) => p.get('workflow_id') === '__none__')
|
||||
check('地址记下 ?wf=__none__', new URL(page.url()).searchParams.get('wf') === '__none__')
|
||||
const names = await page.locator('[data-runs-list] [data-run-id] > div:first-of-type').evaluateAll((els) => els.map((e) => e.textContent ?? ''))
|
||||
check('筛出来的都是未保存的工作流', names.length > 0 && names.every((n) => n.includes('未保存的工作流')), `${names.length} 行`)
|
||||
const rows = await page.locator('[data-runs-list] [data-run-id]').evaluateAll((els) => els.map((e) => ({
|
||||
id: e.getAttribute('data-run-id'), name: e.querySelector(':scope > div:first-of-type')?.textContent ?? '' })))
|
||||
// 没有工作流的运行有两种:没保存就跑的(记的名字是「临时图」、空,或已经是「未保存的工作流」),和工作流后来删掉了的
|
||||
// (记着当时的名字)。前一种一律写「未保存的工作流」、不露「临时图」;后一种照记下的名字写。名字来自沙箱库,只报条数
|
||||
const byId = new Map((await getJson('/runs?workflow_id=__none__&limit=200')).map((r) => [r.id, r]))
|
||||
const unsavedName = (r) => !r.workflow_name || ['临时图', '未保存的工作流'].includes(r.workflow_name)
|
||||
const wrong = rows.filter(({ id, name }) => {
|
||||
const r = byId.get(id)
|
||||
if (!r || r.workflow_id) return true
|
||||
return unsavedName(r) ? !name.includes('未保存的工作流') : !name.includes(r.workflow_name)
|
||||
})
|
||||
check('筛出来的都是没有工作流的运行:没保存就跑的写「未保存的工作流」,不露「临时图」',
|
||||
rows.length > 0 && !wrong.length && rows.every((r) => !r.name.includes('临时图'))
|
||||
&& rows.some((r) => r.name.includes('未保存的工作流')),
|
||||
`${rows.length} 行 · ${rows.filter((r) => unsavedName(byId.get(r.id) ?? {})).length} 行是没保存就跑的 · ${wrong.length} 行不对`)
|
||||
await page.locator('[data-runs-workflow]').selectOption('')
|
||||
await settle()
|
||||
}
|
||||
@@ -1886,8 +1941,10 @@ await section('证据页签:左边报告、右边常驻面板、下方审计
|
||||
})
|
||||
await page.goto(`${WEB}/runs/${EV}`, { waitUntil: 'networkidle' })
|
||||
await page.locator('[data-run-detail]').waitFor()
|
||||
// 应用平台在工件后面加了「生效记录」(§3.8)
|
||||
const tabOrder = (await page.locator('[data-run-detail] [role=tab]').evaluateAll((els) => els.map((e) => e.getAttribute('data-tab')))).join(',')
|
||||
check('详情多了「证据」页签(在航迹和工件之间)', await viewTab('evidence').count() === 1
|
||||
&& (await page.locator('[data-run-detail] [role=tab]').evaluateAll((els) => els.map((e) => e.getAttribute('data-tab')))).join(',') === 'stream,trace,evidence,artifacts')
|
||||
&& tabOrder === 'stream,trace,evidence,artifacts,effects', tabOrder)
|
||||
check('没打开证据页签之前不取证据图和审计表', !evRequests.some((u) => u.includes(EV)), evRequests.join(' '))
|
||||
await viewTab('evidence').click()
|
||||
await page.locator('[data-view-pane=evidence] [data-evidence-pane=cited]').waitFor({ timeout: 8000 }).catch(() => {})
|
||||
@@ -2294,7 +2351,9 @@ await section('待办页签:审批带着 task_id,新来的审批按 id 补
|
||||
const byId = []
|
||||
const onRequest = (req) => {
|
||||
const u = new URL(req.url())
|
||||
if (u.pathname === '/api/tasks') lists.push(u.search)
|
||||
// 只数这个页签的整表重取(assigned=all)。外壳的新待办通知按「派给我的」(assigned=me)取,那是另一回事:
|
||||
// 它在派给我的待办数变了、或刚打开页面记底时取一次
|
||||
if (u.pathname === '/api/tasks' && u.searchParams.get('assigned') !== 'me') lists.push(u.search)
|
||||
const m = u.pathname.match(/^\/api\/tasks\/([^/]+)$/)
|
||||
if (m && m[1] !== 'count') byId.push(m[1])
|
||||
}
|
||||
|
||||
+10
-2
@@ -92,11 +92,13 @@ async function open({ theme = 'dark', state }) {
|
||||
// 主题以本地为准,别让沙箱库里存的偏好把这次检查的主题改掉
|
||||
if (url.pathname === '/api/settings') return route.fulfill({ json: { ui: { theme } } })
|
||||
if (url.pathname === '/api/approvals') return route.fulfill({ json: state.approvals })
|
||||
// 导航上的两个徽标按来源分开数:「记录」数不属于应用的(other),「应用」数应用里的(apps)。这里的待审批都不属于应用
|
||||
// 导航上的两个徽标按来源分开数:「记录」数不属于应用的(other),「应用」数应用里的(apps)。这里的待审批都不属于应用。
|
||||
// latest 同服务端:派给我的待处理待办里最新一条的开出时间(数不变、却换了一条时,新待办通知看它)
|
||||
if (url.pathname === '/api/tasks/count') {
|
||||
const other = state.approvals.filter((a) => a.status === 'pending').length
|
||||
const apps = state.appTasks ?? 0
|
||||
return route.fulfill({ json: { mine: state.tasks ? state.tasks.length : other + apps, apps, other } })
|
||||
const latest = state.tasks?.length ? state.tasks.map((t) => t.created_at).sort().at(-1) : null
|
||||
return route.fulfill({ json: { mine: state.tasks ? state.tasks.length : other + apps, apps, other, latest } })
|
||||
}
|
||||
// 派给我的待办(外壳只在待办数变了时取一次,拿来判断哪些是新来的)
|
||||
if (url.pathname === '/api/tasks' && state.tasks) return route.fulfill({ json: { items: state.tasks } })
|
||||
@@ -566,6 +568,12 @@ await section('新的待办', '\n=== 派给自己的新待办:后台时发系
|
||||
const notes = await page.evaluate(() => window.__notes)
|
||||
check('已按待审批通知过的那一条不再发第二条', notes.some((n) => n.tag === 'approval:shell-ap-task')
|
||||
&& !notes.some((n) => n.tag === 'task:shell-task-ap'), notes.map((n) => n.tag).join(','))
|
||||
// 数不变、却换了一条(核实的那条处理掉了,又来一条新的):派给我的还是 2 条,靠最新一条的时间(latest)认出来
|
||||
state.tasks = [state.tasks[1], taskOf('shell-task-swap', { title: '退款结果未知,请核实', created_at: new Date().toISOString() })]
|
||||
const swapped = await page.waitForFunction(() => window.__notes.some((n) => n.tag === 'task:shell-task-swap'), null, { timeout: 12000 })
|
||||
.then(() => true, () => false)
|
||||
check('数不变、换了一条:按最新一条的时间认出新来的,照样发通知', swapped,
|
||||
(await page.evaluate(() => window.__notes.map((n) => n.tag))).join(','))
|
||||
check('没有未捕获的运行时错误', page.errors.length === 0, page.errors[0] ?? '')
|
||||
await ctx.close()
|
||||
})
|
||||
|
||||
@@ -2901,7 +2901,8 @@ await section('应用平台:人工节点的流程配置、入口字段的类
|
||||
nodes: [
|
||||
node('in', 'input', 0, 200, '入口', { fields: [{ name: 'amount', required: true }, { name: 'kind' }] }),
|
||||
node('h', 'human', 300, 200, '财务审批', { mode: 'approve', title: '审批', message: '{{ input.amount }}' }),
|
||||
node('pay', 'tool', 600, 200, '付款', { args: {} }),
|
||||
// 选了工具:调用工具节点没选工具时「运行」按钮被挡住(错误级问题),发起表单就打不开
|
||||
node('pay', 'tool', 600, 200, '付款', { tool: 'http_request', args: {} }),
|
||||
node('out', 'output', 900, 200, '成果', { fields: [{ name: '结果', value: '{{ last_message }}' }] }),
|
||||
],
|
||||
edges: [edge('in', 'h'), edge('h', 'pay', 'approved'), edge('pay', 'out')],
|
||||
|
||||
@@ -118,7 +118,14 @@ async function countNotice(page, text, settle = 1200) {
|
||||
}
|
||||
|
||||
await section('画布助手栏', async () => {
|
||||
const { page, errors } = await visit('/studio')
|
||||
// 守的是空态(还没跟助手说过话):/studio 落到最近改过的那张图,真数据里它常常已经有几轮对话,空态就不出来。
|
||||
// 这张图的助手对话按「没有」回,和一张新图一样(建对话的 POST 照旧被写闸门掐掉)
|
||||
const { page, errors } = await newPage()
|
||||
await page.route((url) => url.pathname === '/api/conversations' && url.searchParams.get('kind') === 'canvas',
|
||||
(route) => (route.request().method() === 'GET'
|
||||
? route.fulfill({ status: 200, contentType: 'application/json', body: '[]' }) : route.fallback()))
|
||||
await page.goto(`${WEB}/studio`, { waitUntil: 'networkidle' })
|
||||
await page.waitForTimeout(700)
|
||||
check('没有运行时报错', errors.length === 0, errors.join(' | '))
|
||||
|
||||
const body = await page.locator('body').innerText()
|
||||
|
||||
Reference in New Issue
Block a user