From 9f5232ec5844e0807ea9fcb581a4a190728d97ef Mon Sep 17 00:00:00 2001 From: murashit Date: Tue, 26 May 2026 21:41:30 +0900 Subject: [PATCH] Normalize tooltip and ARIA labeling --- src/features/chat/ui/message-stream.ts | 1 - src/features/chat/ui/tool-result.ts | 2 +- src/features/chat/ui/toolbar.ts | 59 +++++++++---------- src/features/chat/ui/turn-diff.ts | 1 - src/features/chat/ui/work-items.ts | 2 - src/features/chat/view.ts | 1 - src/features/threads-view/renderer.ts | 2 +- src/settings/dynamic-sections.ts | 5 +- src/shared/ui/dom.ts | 1 - tests/features/chat/ui/view-renderers.test.ts | 30 +++++----- tests/features/threads-view/view.test.ts | 4 +- 11 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/features/chat/ui/message-stream.ts b/src/features/chat/ui/message-stream.ts index 1f415337..af91a723 100644 --- a/src/features/chat/ui/message-stream.ts +++ b/src/features/chat/ui/message-stream.ts @@ -323,7 +323,6 @@ function renderReferencedThread(parent: HTMLElement, item: Extract, context: MessageStreamContext): void { diff --git a/src/features/chat/ui/tool-result.ts b/src/features/chat/ui/tool-result.ts index 07a474c6..7c1e1194 100644 --- a/src/features/chat/ui/tool-result.ts +++ b/src/features/chat/ui/tool-result.ts @@ -16,7 +16,7 @@ export function renderToolResult(parent: HTMLElement, item: ToolResultDisplayIte const messageEl = root; applyExecutionStateClass(messageEl, view.state); createToolResultHeader(messageEl, view); - const content = messageEl.createDiv({ cls: "codex-panel__tool-summary", attr: { title: view.summary } }); + const content = messageEl.createDiv({ cls: "codex-panel__tool-summary" }); context.renderTextWithWikiLinks(content, view.summary); for (const section of view.details) { diff --git a/src/features/chat/ui/toolbar.ts b/src/features/chat/ui/toolbar.ts index 41342310..68734197 100644 --- a/src/features/chat/ui/toolbar.ts +++ b/src/features/chat/ui/toolbar.ts @@ -12,7 +12,6 @@ export interface ToolbarChoice { label: string; selected?: boolean; disabled?: boolean; - title?: string; meta?: string; onClick: () => void; } @@ -52,7 +51,6 @@ export interface ToolbarViewModel { fastActive: boolean; runtimeSummary: string; runtimeTitle: string; - runtimeAriaLabel: string; runtimeEmphasized: boolean; context: { level: "ok" | "warn" | "danger"; title: string; label: string; percent: number | null } | null; rateLimit: RateLimitSummary | null; @@ -139,7 +137,7 @@ export function renderToolbar(toolbar: HTMLElement, model: ToolbarViewModel, act } function renderHistoryButton(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { - const button = createToolbarButton(parent, "history", "Threads"); + const button = createToolbarButton(parent, "history", "Toggle thread list"); button.addClass("codex-panel__history-toggle"); if (model.historyOpen) button.addClass("is-active"); button.setAttr("aria-pressed", model.historyOpen ? "true" : "false"); @@ -147,7 +145,7 @@ function renderHistoryButton(parent: HTMLElement, model: ToolbarViewModel, actio } function renderAutoReviewButton(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { - const button = createToolbarButton(parent, "shield", `Auto-review: ${model.autoReviewActive ? "on" : "off"}`); + const button = createToolbarButton(parent, "shield", "Toggle auto-review"); button.addClass("codex-panel__auto-review-toggle"); if (model.autoReviewActive) button.addClass("is-active"); button.setAttr("aria-pressed", model.autoReviewActive ? "true" : "false"); @@ -160,7 +158,7 @@ function renderStatusButton(parent: HTMLElement, model: ToolbarViewModel, action cls: `clickable-icon nav-action-button codex-panel-ui__toolbar-control codex-panel__status-dot codex-panel__status-dot--${model.statusState}${alertClass} ${model.statusPanelOpen ? "is-active" : ""}`, attr: { type: "button", - "aria-label": statusButtonLabel(model), + "aria-label": "Toggle connection status", "aria-expanded": model.statusPanelOpen ? "true" : "false", }, }); @@ -173,20 +171,10 @@ function renderStatusButton(parent: HTMLElement, model: ToolbarViewModel, action button.onclick = actions.toggleStatusPanel; } -function statusButtonLabel(model: ToolbarViewModel): string { - const status = model.status.trim() || (model.connected ? "Connected" : "Not connected"); - const connection = model.connected ? "connected" : "not connected"; - const normalizedStatus = status.toLowerCase().replace(/[.!…]+$/u, ""); - const parts = [`Status: ${status}`]; - if (normalizedStatus !== connection) parts.push(`Connection: ${connection}`); - parts.push(`Diagnostics: ${model.diagnosticAlertLevel}`); - return parts.join("; "); -} - function renderRuntimeStatus(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { const row = parent.createDiv({ cls: "codex-panel__runtime-strip" }); - renderRuntimeIcon(row, "list-checks", `Plan mode: ${model.planActive ? "on" : "off"}`, model.planActive, actions.togglePlan); - renderRuntimeIcon(row, "zap", `Fast mode: ${model.fastActive ? "on" : "off"}`, model.fastActive, actions.toggleFast); + renderRuntimeIcon(row, "list-checks", "Toggle plan mode", model.planActive, actions.togglePlan); + renderRuntimeIcon(row, "zap", "Toggle fast mode", model.fastActive, actions.toggleFast); renderRuntimeModelControl(row, model, actions); } @@ -217,7 +205,7 @@ function renderRuntimeModelControl(parent: HTMLElement, model: ToolbarViewModel, cls, attr: { type: "button", - "aria-label": model.runtimeAriaLabel, + "aria-label": "Change runtime settings", "aria-expanded": model.runtimeOpen ? "true" : "false", }, }); @@ -231,7 +219,6 @@ function renderContextMeter(parent: HTMLElement, model: ToolbarViewModel): void const context = parent.createDiv({ cls: `codex-panel__meter-compact codex-panel__context-compact codex-panel__meter-compact--${model.context.level}`, }); - context.title = model.context.title; context.createSpan({ cls: "codex-panel__meter-compact-label codex-panel__context-compact-label", text: model.context.label }); const bar = context.createSpan({ cls: "codex-panel__meter-compact-bar codex-panel__context-compact-bar" }); bar.createSpan({ @@ -257,14 +244,20 @@ function renderToolbarPanel(toolbar: HTMLElement, model: ToolbarViewModel, actio function renderStatusPanel(parent: HTMLElement, model: ToolbarViewModel, actions: ToolbarActions): void { const statusItems = parent.createDiv({ cls: "codex-panel__status-panel-items", attr: { role: "menu" } }); - createToolbarPanelRow(statusItems, model.connectLabel, { onClick: actions.connect, className: "codex-panel__status-panel-item" }); + createToolbarPanelRow(statusItems, model.connectLabel, { + onClick: actions.connect, + className: "codex-panel__status-panel-item", + role: "menuitem", + }); createToolbarPanelRow(statusItems, "Refresh diagnostics", { onClick: actions.refreshDiagnostics, className: "codex-panel__status-panel-item", + role: "menuitem", }); createToolbarPanelRow(statusItems, "Refresh thread list", { onClick: actions.refreshThreads, className: "codex-panel__status-panel-item", + role: "menuitem", }); renderRateLimitPanel(parent, model.rateLimit); renderConnectionDiagnostics(parent, model.diagnostics); @@ -280,7 +273,6 @@ function renderRateLimitPanel(parent: HTMLElement, rateLimit: RateLimitSummary | for (const row of rateLimit.rows) { const item = list.createDiv({ cls: `codex-panel__limit-panel-row codex-panel__limit-panel-row--${row.level}`, - attr: { title: row.title }, }); item.createDiv({ cls: "codex-panel__limit-panel-label", text: row.label }); item.createDiv({ cls: "codex-panel__limit-panel-value", text: row.value }); @@ -313,18 +305,22 @@ function renderRuntimePicker(parent: HTMLElement, model: ToolbarViewModel): void const picker = parent.createDiv({ cls: "codex-panel__runtime-picker", attr: { role: "listbox" } }); picker.createDiv({ cls: "codex-panel__runtime-picker-label", text: "Reasoning effort" }); for (const choice of model.effortChoices) { - createToolbarPanelRow(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice" }); + createToolbarPanelRow(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice", role: "option" }); } picker.createDiv({ cls: "codex-panel__runtime-picker-label", text: "Model" }); for (const choice of model.modelChoices) { - createToolbarPanelRow(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice" }); + createToolbarPanelRow(picker, choice.label, { ...choice, className: "codex-panel__runtime-choice", role: "option" }); } } function renderThreadList(parent: HTMLElement, threads: ToolbarThreadRow[], actions: ToolbarActions): void { const threadsEl = parent.createDiv({ cls: "codex-panel__threads" }); if (threads.length === 0) { - createToolbarPanelRow(threadsEl, "No threads", { disabled: true, className: "codex-panel__thread codex-panel__thread--empty" }); + createToolbarPanelRow(threadsEl, "No threads", { + disabled: true, + className: "codex-panel__thread codex-panel__thread--empty", + interactive: false, + }); return; } @@ -340,7 +336,6 @@ function renderThreadList(parent: HTMLElement, threads: ToolbarThreadRow[], acti createToolbarPanelRow(row, thread.title, { selected: thread.selected, disabled: thread.disabled, - title: `${thread.title}\n${thread.threadId}`, className: "codex-panel__thread", onClick: () => { actions.resumeThread(thread.threadId); @@ -372,7 +367,6 @@ function renderThreadRenameRow(parent: HTMLElement, thread: ToolbarThreadRow, ac createToolbarPanelRow(parent, thread.title, { className: "codex-panel__thread codex-panel__thread-rename", interactive: false, - title: `Rename ${thread.title}`, renderContent: (row) => { const field = row.createDiv({ cls: "codex-panel__thread-rename-field" }); input = field.createEl("input", { @@ -436,10 +430,10 @@ function createToolbarPanelRow( options: { selected?: boolean; disabled?: boolean; - title?: string; meta?: string; className?: string; interactive?: boolean; + role?: "button" | "menuitem" | "option"; renderContent?: (parent: HTMLElement) => void; onClick?: () => void; } = {}, @@ -447,12 +441,17 @@ function createToolbarPanelRow( const selected = Boolean(options.selected); const disabled = Boolean(options.disabled); const interactive = options.interactive ?? true; - const attr: Record = { title: options.title ?? label }; + const role = options.role ?? "button"; + const attr: Record = {}; if (interactive) { - attr["role"] = "button"; + attr["role"] = role; attr["tabindex"] = disabled ? "-1" : "0"; attr["aria-disabled"] = disabled ? "true" : "false"; - attr["aria-selected"] = selected ? "true" : "false"; + if (role === "option") { + attr["aria-selected"] = selected ? "true" : "false"; + } else if (role === "button" && selected) { + attr["aria-current"] = "true"; + } } const item = parent.createDiv({ cls: ["codex-panel__toolbar-panel-item", options.className ?? "", selected ? "is-checked" : "", disabled ? "is-disabled" : ""] diff --git a/src/features/chat/ui/turn-diff.ts b/src/features/chat/ui/turn-diff.ts index 72e52359..2c800225 100644 --- a/src/features/chat/ui/turn-diff.ts +++ b/src/features/chat/ui/turn-diff.ts @@ -82,7 +82,6 @@ function renderTurnDiffHeader( titleBlock.createDiv({ cls: "codex-panel-chat-turn-diff__meta", text: `${shortThreadId(state.threadId)} / ${shortThreadId(state.turnId)} · ${fileCountLabel(state.files)}`, - attr: { title: `Thread ${state.threadId}\nTurn ${state.turnId}${state.cwd ? `\n${state.cwd}` : ""}` }, }); if (copyDiff) { const copyButton = createIconButton(header, "copy", "Copy diff", "codex-panel-chat-turn-diff__copy"); diff --git a/src/features/chat/ui/work-items.ts b/src/features/chat/ui/work-items.ts index 3d81b1d0..57e6d1dd 100644 --- a/src/features/chat/ui/work-items.ts +++ b/src/features/chat/ui/work-items.ts @@ -75,7 +75,6 @@ export function renderAgentItem(parent: HTMLElement, item: AgentDisplayItem, con const list = messageEl.createEl("ul", { cls: "codex-panel__agent-list" }); for (const agent of item.agents) { const row = list.createEl("li", { cls: "codex-panel__agent-row" }); - row.title = agent.threadId; row.createSpan({ cls: "codex-panel__agent-thread", text: shortThreadId(agent.threadId) }); row.createSpan({ cls: "codex-panel__agent-status", text: agentStatusLabel(agent.status, agent.message) }); } @@ -129,7 +128,6 @@ function renderAgentSummaryRows(parent: HTMLElement, summary: AgentRunSummary): const list = parent.createEl("ul", { cls: "codex-panel__agent-list codex-panel__agent-list--summary" }); for (const agent of summary.agents) { const row = list.createEl("li", { cls: "codex-panel__agent-row" }); - row.title = agent.threadId; row.createSpan({ cls: "codex-panel__agent-thread", text: shortThreadId(agent.threadId) }); row.createSpan({ cls: "codex-panel__agent-status", text: agentSummaryStatusLabel(agent) }); } diff --git a/src/features/chat/view.ts b/src/features/chat/view.ts index 4bb5d170..fc1e37a4 100644 --- a/src/features/chat/view.ts +++ b/src/features/chat/view.ts @@ -1277,7 +1277,6 @@ export class CodexChatView extends ItemView { fastActive: currentServiceTier(snapshot, config) === "fast", runtimeSummary: runtimeSummaryLabel(model, effort), runtimeTitle: `Model: ${model ?? "(from default)"}; Effort: ${effort ?? "(from default)"}`, - runtimeAriaLabel: `Runtime: ${model ?? "default"} ${effort ?? "default"}`, runtimeEmphasized: false, context: context ? { ...context, label: compactContextLabel(context.percent, context.label) } : null, rateLimit: limit, diff --git a/src/features/threads-view/renderer.ts b/src/features/threads-view/renderer.ts index 472d4b90..f10fa2a3 100644 --- a/src/features/threads-view/renderer.ts +++ b/src/features/threads-view/renderer.ts @@ -53,7 +53,7 @@ export function renderThreadsView(parent: HTMLElement, model: ThreadsViewModel, function renderThreadRow(parent: HTMLElement, row: ThreadsRowModel, actions: ThreadsViewActions): void { const item = parent.createDiv({ cls: "codex-panel-threads__row", - attr: { role: "button", tabindex: "0", "aria-label": `Open thread: ${row.title}` }, + attr: { role: "button", tabindex: "0" }, }); if (row.live) item.addClass(`codex-panel-threads__row--${row.live.status}`); if (row.rename.active) item.addClass("codex-panel-threads__row--renaming"); diff --git a/src/settings/dynamic-sections.ts b/src/settings/dynamic-sections.ts index 0cbb130d..8b8c634b 100644 --- a/src/settings/dynamic-sections.ts +++ b/src/settings/dynamic-sections.ts @@ -2,7 +2,7 @@ import { Setting } from "obsidian"; import type { HookMetadata } from "../generated/app-server/v2/HookMetadata"; import type { Thread } from "../generated/app-server/v2/Thread"; -import { archivedThreadDisplayTitle, fullThreadTitle } from "../domain/threads/model"; +import { archivedThreadDisplayTitle } from "../domain/threads/model"; import { shortThreadId } from "../utils"; export interface ArchivedThreadSectionState { @@ -152,7 +152,6 @@ function renderArchivedThreadList(containerEl: HTMLElement, state: ArchivedThrea button.extraSettingsEl.setAttr("aria-label", `Restore ${title}`); }); setting.settingEl.addClass("codex-panel-settings__archived-row"); - setting.settingEl.setAttr("title", fullThreadTitle(thread)); } } @@ -202,11 +201,9 @@ function renderHookRow(list: HTMLElement, hook: HookMetadata, state: HookSection }); }); setting.settingEl.addClass("codex-panel-settings__hook-row"); - setting.settingEl.setAttr("title", hook.command ?? hook.key); setting.descEl.createDiv({ cls: "codex-panel-settings__hook-hash", text: hook.currentHash, - attr: { title: hook.key }, }); } diff --git a/src/shared/ui/dom.ts b/src/shared/ui/dom.ts index 0524467b..3278fed9 100644 --- a/src/shared/ui/dom.ts +++ b/src/shared/ui/dom.ts @@ -33,7 +33,6 @@ export function renderTextWithWikiLinks(parent: HTMLElement, text: string, openL text: label, attr: { href: target, - title: target, }, }); link.onclick = (event) => { diff --git a/tests/features/chat/ui/view-renderers.test.ts b/tests/features/chat/ui/view-renderers.test.ts index d564424d..15bb1d49 100644 --- a/tests/features/chat/ui/view-renderers.test.ts +++ b/tests/features/chat/ui/view-renderers.test.ts @@ -752,7 +752,7 @@ describe("message stream block identity and message actions", () => { const element = block.render(); expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("npm run check (exit 1)"); - expect(element.querySelector(".codex-panel__tool-summary")?.getAttribute("title")).toBe("npm run check (exit 1)"); + expect(element.querySelector(".codex-panel__tool-summary")?.getAttribute("title")).toBeNull(); expect(topLevelDetailsSummaries(element)).toEqual(["command"]); expect([...element.querySelectorAll("details summary")].map((summary) => summary.textContent)).toEqual(["command"]); expect(element.textContent).not.toContain("Details"); @@ -947,7 +947,7 @@ describe("message stream block identity and message actions", () => { expect(user?.querySelector(".codex-panel__message-content")?.textContent).toBe("この続きです"); expect(user?.querySelector(".codex-panel__referenced-thread")?.textContent).toContain("Referenced 参照元"); expect(user?.querySelector(".codex-panel__referenced-thread")?.textContent).toContain("2/20 turns"); - expect(user?.querySelector(".codex-panel__referenced-thread")?.title).toBe("thread-reference"); + expect(user?.querySelector(".codex-panel__referenced-thread")?.getAttribute("title")).toBeNull(); }); it("renders resolved file mentions as a collapsed user message attachment", () => { @@ -1238,7 +1238,7 @@ describe("work log renderer decisions", () => { const element = block.render(); expect(element.querySelector(".codex-panel__tool-summary")?.textContent).toBe("assets/image.png"); - expect(element.querySelector(".codex-panel__tool-summary")?.getAttribute("title")).toBe("assets/image.png"); + expect(element.querySelector(".codex-panel__tool-summary")?.getAttribute("title")).toBeNull(); }); it("keeps path summary tools absolute outside the workspace root", () => { @@ -1651,20 +1651,18 @@ describe("toolbar renderer decisions", () => { const statusButton = parent.querySelector(".codex-panel__status-dot"); expect(statusButton?.tagName).toBe("BUTTON"); expect(statusButton?.getAttribute("role")).toBeNull(); - expect(statusButton?.getAttribute("aria-label")).toBe("Status: Connected.; Diagnostics: normal"); + expect(statusButton?.getAttribute("aria-label")).toBe("Toggle connection status"); parent.querySelector(".codex-panel__history-toggle")?.click(); expect(toggleHistory).toHaveBeenCalled(); const autoReviewButton = parent.querySelector(".codex-panel__auto-review-toggle"); - expect(autoReviewButton?.getAttribute("aria-label")).toBe("Auto-review: off"); + expect(autoReviewButton?.getAttribute("aria-label")).toBe("Toggle auto-review"); expect(autoReviewButton?.getAttribute("aria-pressed")).toBe("false"); autoReviewButton?.click(); expect(toggleAutoReview).toHaveBeenCalled(); parent.empty(); renderToolbar(parent, toolbarModel({ status: "Turn running...", statusState: "running", autoReviewActive: true }), toolbarActions()); - expect(parent.querySelector(".codex-panel__status-dot")?.getAttribute("aria-label")).toBe( - "Status: Turn running...; Connection: connected; Diagnostics: normal", - ); + expect(parent.querySelector(".codex-panel__status-dot")?.getAttribute("aria-label")).toBe("Toggle connection status"); expect(parent.querySelector(".codex-panel__auto-review-toggle")?.getAttribute("aria-pressed")).toBe("true"); expect(toolbarSignature(baseModel)).not.toBe(toolbarSignature({ ...baseModel, status: "Turn running..." })); @@ -1708,6 +1706,8 @@ describe("toolbar renderer decisions", () => { ]); expect([...parent.querySelectorAll(".codex-panel__runtime-choice")].map((choice) => choice.textContent)).toEqual(["high", "gpt-5.5"]); for (const choice of parent.querySelectorAll(".codex-panel__runtime-choice")) { + expect(choice.getAttribute("role")).toBe("option"); + expect(choice.getAttribute("aria-selected")).toBe("true"); expect(choice.querySelector(".codex-panel__toolbar-panel-check")?.dataset["icon"]).toBe("check"); expect(choice.classList.contains("selected")).toBe(false); expect(choice.classList.contains("is-selected")).toBe(false); @@ -1783,9 +1783,10 @@ describe("toolbar renderer decisions", () => { expect(parent.textContent).toContain("Refresh diagnostics"); expect(parent.textContent).toContain("codex-cli/1.2.3"); expect(parent.querySelector(".codex-panel__connection-diagnostics-row--error")?.textContent).toContain("model/list failed"); - [...parent.querySelectorAll(".codex-panel__status-panel-item")] - .find((item) => item.textContent.includes("Refresh diagnostics")) - ?.click(); + const statusItems = [...parent.querySelectorAll(".codex-panel__status-panel-item")]; + expect(statusItems.map((item) => item.getAttribute("role"))).toEqual(["menuitem", "menuitem", "menuitem"]); + expect(statusItems.every((item) => item.getAttribute("aria-selected") === null)).toBe(true); + statusItems.find((item) => item.textContent.includes("Refresh diagnostics"))?.click(); expect(refreshDiagnostics).toHaveBeenCalled(); }); @@ -1794,21 +1795,21 @@ describe("toolbar renderer decisions", () => { renderToolbar(normal, toolbarModel({ diagnosticAlertLevel: "normal" }), toolbarActions()); const normalStatus = normal.querySelector(".codex-panel__status-dot"); expect(normalStatus?.querySelector(".codex-panel__status-dot-diagnostic")).toBeNull(); - expect(normalStatus?.getAttribute("aria-label")).toContain("Diagnostics: normal"); + expect(normalStatus?.getAttribute("aria-label")).toBe("Toggle connection status"); const warning = document.createElement("div"); renderToolbar(warning, toolbarModel({ diagnosticAlertLevel: "warning" }), toolbarActions()); const warningStatus = warning.querySelector(".codex-panel__status-dot"); expect(warningStatus?.classList.contains("codex-panel__status-dot--diagnostic-warning")).toBe(true); expect(warningStatus?.querySelector(".codex-panel__status-dot-diagnostic--warning")).not.toBeNull(); - expect(warningStatus?.getAttribute("aria-label")).toContain("Diagnostics: warning"); + expect(warningStatus?.getAttribute("aria-label")).toBe("Toggle connection status"); const error = document.createElement("div"); renderToolbar(error, toolbarModel({ diagnosticAlertLevel: "error" }), toolbarActions()); const errorStatus = error.querySelector(".codex-panel__status-dot"); expect(errorStatus?.classList.contains("codex-panel__status-dot--diagnostic-error")).toBe(true); expect(errorStatus?.querySelector(".codex-panel__status-dot-diagnostic--error")).not.toBeNull(); - expect(errorStatus?.getAttribute("aria-label")).toContain("Diagnostics: error"); + expect(errorStatus?.getAttribute("aria-label")).toBe("Toggle connection status"); }); it("renders effective config inside the status menu without a separate toggle", () => { @@ -2426,7 +2427,6 @@ function toolbarModel(overrides: Partial = {}): ToolbarViewMod fastActive: false, runtimeSummary: "5.5 high", runtimeTitle: "Model: gpt-5.5; Effort: high", - runtimeAriaLabel: "Runtime: gpt-5.5 high", runtimeEmphasized: false, context: null, rateLimit: null, diff --git a/tests/features/threads-view/view.test.ts b/tests/features/threads-view/view.test.ts index 5bb66aeb..5d30c9fd 100644 --- a/tests/features/threads-view/view.test.ts +++ b/tests/features/threads-view/view.test.ts @@ -158,7 +158,9 @@ describe("CodexThreadsView", () => { const view = await threadsView(host); await view.refresh(); - view.containerEl.querySelector(".codex-panel-threads__row")?.click(); + const row = view.containerEl.querySelector(".codex-panel-threads__row"); + expect(row?.getAttribute("aria-label")).toBeNull(); + row?.click(); await vi.waitFor(() => { expect(host.openThreadInAvailableView).toHaveBeenCalledWith("thread");