test: correct HeartbeatCenter nested navigation assertions
Fix the two Chinese and English test failures by scoping page and graph tabs to their accessible names. Cover selection, tab/panel associations, keyboard focus and graph content while preserving non-graph navigation assertions. Clarify the top-level navigation design and record the follow-up validation in the quality and workbar docs. No production code changes. Prior validation: 96 tests passed across HeartbeatCenter (42), SupervisorWorkspace and WorkspacePrimitives; npm run typecheck and npm run lint passed. The full repository suite was not rerun. Commit-time staged diff review and whitespace checks passed.
This commit is contained in:
@@ -78,12 +78,17 @@ without npm's launcher environment; its OpenCode setup error did not recur throu
|
||||
the npm entry point. The layout timeout also did not recur. These focused results
|
||||
do not replace the failed full-suite result.
|
||||
|
||||
Both HeartbeatCenter language cases still fail independently. Source inspection
|
||||
Both HeartbeatCenter language cases failed independently at that point. Source inspection
|
||||
shows the graph contains a separate, named PageTabs for events/entities/relations
|
||||
inside the page-level tabs; the older test assumes there can only be one tablist
|
||||
anywhere on the page. Neither that feature nor its test was changed for this
|
||||
Continue fix. No unrelated UI change or assertion relaxation was made.
|
||||
|
||||
A separate 2026-09-27 follow-up corrected the navigation scope assertions after
|
||||
checking the graph design. HeartbeatCenter, SupervisorWorkspace and shared tab
|
||||
tests passed together (96 tests); the full suite was not rerun. See the
|
||||
[validation record](../../quality/responsive-controls-review-2026-09-27.md#heartbeatcenter-断言修复).
|
||||
|
||||
The retained full-App log contains no matching unresponsive, crash, timeout or
|
||||
maximum-update-depth errors. This does not establish the cause of the reported
|
||||
freeze. This follow-up made zero external model requests (the fix-validation total
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
应用中心中的智能心跳入口升级为“监督者”,沿用既有启用、常驻与排序能力。未来应用市场使用同一应用身份。应用已启用时,停用单条自动心跳计划仍可手动回顾;关闭应用后,已打开页面显示关闭状态并禁用工作区操作。默认值及运行边界见[应用启停与执行](./logic-design.md#应用启停与执行)。
|
||||
|
||||
当前应用页只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。
|
||||
当前应用页的顶层导航只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。故事线图谱面板内另有分类页签,见[故事线图谱](#故事线图谱)。
|
||||
|
||||
工作回顾只读取统一监督结果。顶部紧凑排列范围、时间、发起与刷新控件,不重复显示“新回顾”标题和来源说明,不新增 Modal。历史结果选择与图谱入口位于正文之前,即使只有一条结果也显示选择器。新回顾工具栏、状态、历史选择和结果面板铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设阅读宽度上限;结果内部按面板内边距排版,不再单独居中窄正文。正文使用“监督结果”标题,常显生成日期、真实范围和覆盖区间,历史结果不标为最新。工作总结按原有空行分段并保留换行;本次变化和未解决事项直接使用已有字段,不补写结论或截断正文。390px 下新请求字段与操作各占两列,结果元数据可换行。
|
||||
|
||||
|
||||
@@ -97,7 +97,23 @@ Electron 浮层回归共 331 项通过,包含窄侧栏 Escape 隔离与 MCP
|
||||
1 个 tablist,并要求内容面板内没有 tablist;但图谱有事件数据时,
|
||||
`SupervisorWorkspace.tsx:677` 已渲染事件、实体、关系的内层 `PageTabs`,实际总数为 2。
|
||||
未带本轮改动的 HEAD `11facf0` 使用相同依赖独立执行该文件,也得到 40 通过、2 失败。
|
||||
本次保留组件与断言,后续需按图谱导航设计确认测试范围;没有将预期数量直接改为 2。
|
||||
当时保留组件与断言;后续修复及定向验证见下节。
|
||||
|
||||
### HeartbeatCenter 断言修复
|
||||
|
||||
2026-09-27 后续核对[监督者 UI 设计](../features/conversation-supervision/ui-design.md#故事线图谱)、
|
||||
`HeartbeatCenter`、`SupervisorWorkspace` 和共享 `PageTabs`:顶层导航与图谱分类导航各有
|
||||
独立可访问名称,图谱内层页签符合设计。修改前按测试名称复跑,中英文均在原第 213 行失败。
|
||||
|
||||
测试现按 role 和可访问名称分别定位两层导航,核对顶层五页签、图谱事件/实体/关系及数量、
|
||||
唯一选中态和 tab/panel 双向关联;方向键切换内层页签时检查焦点、事件内容或分类空态,
|
||||
并确认顶层仍选中故事线图谱。非图谱页面继续断言没有内层 tablist,原有标题、范围和操作
|
||||
断言保留。生产代码未改;设计文档澄清“一套 PageTabs”指顶层导航。
|
||||
|
||||
`npm test -- src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx`
|
||||
通过 3 文件、96 项,包含 HeartbeatCenter 全部 42 项。`npm run typecheck` 通过;
|
||||
`npm run lint` 首次超过 60 秒执行时限,提高时限重跑后通过。本轮未重新运行全仓测试,
|
||||
上表历史全量结果及跳过项保持原记录。
|
||||
|
||||
### Electron 结果与未测范围
|
||||
|
||||
@@ -107,7 +123,7 @@ overlay 文件 3 项通过,包含 MCP 四种主题/窗口/浏览器场景、
|
||||
这些样式与 MCP 驱动的模型请求尝试为 0。全仓另有隔离的 403 fixture 输出,每种凭据来源
|
||||
各记录 1 次请求;这不作为真实模型生成证据,也不据此声称全仓外部调用总数为 0。
|
||||
|
||||
剩余失败为上述两项 HeartbeatCenter 既有断言。全仓跳过的 11 文件、83 项仍未形成通过证据。
|
||||
当时剩余的两项 HeartbeatCenter 断言已由上述定向验证确认修复。全仓跳过的 11 文件、83 项仍未形成通过证据。
|
||||
本次没有运行生产构建、打包、macOS/Linux 验证、远程 Agent 实机或外部 MCP 连接;
|
||||
未覆盖强制颜色、完整系统缩放/200% 字号矩阵、原生 select 弹出菜单的鼠标选项选择,
|
||||
也未将组件 fixture 当作全部 App 业务入口验收。部分 Electron 驱动关闭了 Windows
|
||||
|
||||
@@ -205,13 +205,23 @@ describe('HeartbeatCenter', () => {
|
||||
expect(screen.getByRole('button', { name: t('supervisor.run') })).toBeEnabled()
|
||||
expect(screen.getByRole('combobox', { name: t('supervisor.scope') })).toBeVisible()
|
||||
expect(screen.queryByText(t('supervisor.sourcesHint'))).not.toBeInTheDocument()
|
||||
for (const key of ['supervisor.recap', 'supervisor.graph', 'supervisor.automatic', 'activity.title', 'supervisor.settings']) {
|
||||
fireEvent.click(screen.getByRole('tab', { name: t(key) }))
|
||||
const pageTabKeys = ['supervisor.recap', 'supervisor.graph', 'supervisor.automatic', 'activity.title', 'supervisor.settings']
|
||||
for (const key of pageTabKeys) {
|
||||
const navigation = screen.getByRole('tablist', { name: t('supervisor.navigation') })
|
||||
expect(within(navigation).getAllByRole('tab')).toEqual(pageTabKeys.map((tabKey) => within(navigation).getByRole('tab', { name: t(tabKey) })))
|
||||
const activeTab = within(navigation).getByRole('tab', { name: t(key) })
|
||||
fireEvent.click(activeTab)
|
||||
const panel = screen.getByRole('tabpanel', { name: t(key) })
|
||||
expect(within(navigation).getAllByRole('tab', { selected: true })).toEqual([activeTab])
|
||||
expect(activeTab).toHaveAttribute('aria-controls', panel.id)
|
||||
expect(panel).toHaveAttribute('aria-labelledby', activeTab.id)
|
||||
expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1)
|
||||
expect(within(panel).queryByRole('heading', { name: t(key) })).not.toBeInTheDocument()
|
||||
expect(screen.getAllByRole('tablist')).toHaveLength(1)
|
||||
expect(within(panel).queryByRole('tablist')).not.toBeInTheDocument()
|
||||
expect(within(panel).queryByRole('tablist', { name: t('supervisor.navigation') })).not.toBeInTheDocument()
|
||||
if (key !== 'supervisor.graph') {
|
||||
expect(screen.getAllByRole('tablist')).toEqual([navigation])
|
||||
expect(within(panel).queryByRole('tablist')).not.toBeInTheDocument()
|
||||
}
|
||||
if (key === 'supervisor.settings') {
|
||||
expect(within(panel).queryByRole('button', { name: t('center.actions.refreshAriaLabel') })).not.toBeInTheDocument()
|
||||
expect(panel.querySelector('.scope-badge')).not.toBeInTheDocument()
|
||||
@@ -220,6 +230,30 @@ describe('HeartbeatCenter', () => {
|
||||
expect(within(panel).getByRole('button', { name: t('center.actions.refresh') })).toBeVisible()
|
||||
}
|
||||
if (key === 'supervisor.graph') {
|
||||
const graphNavigation = within(panel).getByRole('tablist', { name: t('supervisor.selection') })
|
||||
expect(screen.getAllByRole('tablist')).toEqual([navigation, graphNavigation])
|
||||
expect(within(panel).getAllByRole('tablist')).toEqual([graphNavigation])
|
||||
const graphTabs = ['event', 'entity', 'relation'].map((kind) =>
|
||||
within(graphNavigation).getByRole('tab', { name: `${t(`supervisor.listTabs.${kind}`)} ${kind === 'event' ? 1 : 0}` })
|
||||
)
|
||||
expect(within(graphNavigation).getAllByRole('tab')).toEqual(graphTabs)
|
||||
expect(within(graphNavigation).getAllByRole('tab', { selected: true })).toEqual([graphTabs[0]])
|
||||
for (const [index, graphTab] of graphTabs.entries()) {
|
||||
if (index > 0) fireEvent.keyDown(graphTabs[index - 1]!, { key: 'ArrowRight' })
|
||||
expect(within(graphNavigation).getAllByRole('tab', { selected: true })).toEqual([graphTab])
|
||||
expect(graphTab).toHaveAttribute('tabindex', '0')
|
||||
if (index > 0) expect(graphTab).toHaveFocus()
|
||||
const graphPanel = within(panel).getByRole('tabpanel')
|
||||
expect(graphPanel).toHaveAccessibleName(graphTab.textContent!)
|
||||
expect(graphTab).toHaveAttribute('aria-controls', graphPanel.id)
|
||||
expect(graphPanel).toHaveAttribute('aria-labelledby', graphTab.id)
|
||||
if (index === 0) {
|
||||
expect(within(graphPanel).getByRole('button', { name: /Review event/ })).toBeVisible()
|
||||
} else {
|
||||
expect(within(graphPanel).getByText(t('supervisor.listEmpty'))).toBeVisible()
|
||||
}
|
||||
expect(within(navigation).getAllByRole('tab', { selected: true })).toEqual([activeTab])
|
||||
}
|
||||
expect(within(panel).getByRole('region', { name: t('supervisor.canvas') })).toBeVisible()
|
||||
expect(within(panel).getByRole('heading', { name: t('supervisor.inspector') })).toBeVisible()
|
||||
expect(within(panel).getByText(`${t('supervisor.graphScope')}: ${t('center.scope.global')}`)).toBeVisible()
|
||||
|
||||
Reference in New Issue
Block a user