- {followupAutoApproveTimeoutMs / 1000}s
+
+ {followupAutoApproveTimeoutMs === 0
+ ? "Disabled"
+ : `${followupAutoApproveTimeoutMs / 1000}s`}
+
{t("settings:autoApprove.followupQuestions.timeoutLabel")}
From 04aa5fad937595e2100c935d182aee0a67f737af Mon Sep 17 00:00:00 2001
From: Para <62068271+murd0cc@users.noreply.github.com>
Date: Tue, 11 Aug 2026 23:59:03 +0300
Subject: [PATCH 2/4] PR requested changes implemented
---
.../chat/__tests__/FollowUpSuggest.spec.tsx | 19 +++++++++
.../settings/AutoApproveSettings.tsx | 2 +-
.../__tests__/AutoApproveSettings.spec.tsx | 14 +++++++
.../AutoApproveSettings.visual.fixture.tsx | 36 ++++++++++++++++++
.../__tests__/AutoApproveSettings.visual.tsx | 18 +++++++++
...approve-followup-timeout-disabled-dark.png | Bin 0 -> 205 bytes
webview-ui/src/i18n/locales/ca/settings.json | 3 +-
webview-ui/src/i18n/locales/de/settings.json | 3 +-
webview-ui/src/i18n/locales/en/settings.json | 3 +-
webview-ui/src/i18n/locales/es/settings.json | 3 +-
webview-ui/src/i18n/locales/fr/settings.json | 3 +-
webview-ui/src/i18n/locales/hi/settings.json | 3 +-
webview-ui/src/i18n/locales/id/settings.json | 3 +-
webview-ui/src/i18n/locales/it/settings.json | 3 +-
webview-ui/src/i18n/locales/ja/settings.json | 3 +-
webview-ui/src/i18n/locales/ko/settings.json | 3 +-
webview-ui/src/i18n/locales/nl/settings.json | 3 +-
webview-ui/src/i18n/locales/pl/settings.json | 3 +-
.../src/i18n/locales/pt-BR/settings.json | 3 +-
webview-ui/src/i18n/locales/ru/settings.json | 3 +-
webview-ui/src/i18n/locales/tr/settings.json | 3 +-
webview-ui/src/i18n/locales/vi/settings.json | 3 +-
.../src/i18n/locales/zh-CN/settings.json | 3 +-
.../src/i18n/locales/zh-TW/settings.json | 3 +-
24 files changed, 124 insertions(+), 19 deletions(-)
create mode 100644 webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
create mode 100644 webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.tsx
create mode 100644 webview-ui/src/components/settings/__tests__/__screenshots__/auto-approve-followup-timeout-disabled-dark.png
diff --git a/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx b/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
index 2672e47306..169df1aee4 100644
--- a/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
+++ b/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
@@ -241,6 +241,25 @@ describe("FollowUpSuggest", () => {
expect(screen.queryByText(/\d+s/)).not.toBeInTheDocument()
})
+ it("should not show countdown when timeout is negative", () => {
+ const negativeTimeoutState: TestExtensionState = {
+ ...defaultTestState,
+ followupAutoApproveTimeoutMs: -1000,
+ }
+
+ renderWithTestProviders(
+ ,
+ negativeTimeoutState,
+ )
+
+ expect(screen.queryByText(/\d+s/)).not.toBeInTheDocument()
+ })
+
it("should not render when no suggestions are provided", () => {
const { container } = renderWithTestProviders(
{followupAutoApproveTimeoutMs === 0
- ? "Disabled"
+ ? t("settings:autoApprove.followupQuestions.timeoutDisabled")
: `${followupAutoApproveTimeoutMs / 1000}s`}
diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
index c798b7a4a9..5ee6026ed8 100644
--- a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
+++ b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
@@ -161,4 +161,18 @@ describe("AutoApproveSettings - Save/Discard contract", () => {
expect(screen.getByTestId("allowed-commands-heading")).toBeInTheDocument()
expect(screen.getByTestId("denied-commands-heading")).toBeInTheDocument()
})
+
+ it("renders disabled timeout label when follow-up auto-approve timeout is 0", () => {
+ renderSettings({ alwaysAllowFollowupQuestions: true, followupAutoApproveTimeoutMs: 0 })
+
+ expect(screen.getByTestId("followup-timeout-slider")).toBeInTheDocument()
+ expect(screen.getByText("settings:autoApprove.followupQuestions.timeoutDisabled")).toBeInTheDocument()
+ })
+
+ it("renders timeout in seconds when follow-up auto-approve timeout is non-zero", () => {
+ renderSettings({ alwaysAllowFollowupQuestions: true, followupAutoApproveTimeoutMs: 5000 })
+
+ expect(screen.getByTestId("followup-timeout-slider")).toBeInTheDocument()
+ expect(screen.getByText("5s")).toBeInTheDocument()
+ })
})
diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
new file mode 100644
index 0000000000..6f4e4707e3
--- /dev/null
+++ b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
@@ -0,0 +1,36 @@
+/* v8 ignore file -- Playwright component fixture is covered by the visual test. */
+import React from "react"
+
+import { TranslationContext } from "@/i18n/TranslationContext"
+import { ExtensionStateContext } from "@/context/ExtensionStateContext"
+import { AutoApproveSettings } from "../AutoApproveSettings"
+
+const extensionStateValue = {
+ autoApprovalEnabled: false,
+ setAutoApprovalEnabled: () => {},
+ alwaysAllowReadOnly: false,
+ alwaysAllowWrite: false,
+ alwaysAllowExecute: false,
+ alwaysAllowMcp: false,
+ alwaysAllowModeSwitch: false,
+ alwaysAllowSubtasks: false,
+ alwaysAllowFollowupQuestions: true,
+} as any
+
+export const AutoApproveSettingsFixture = () => (
+ key,
+ i18n: null as unknown as typeof import("../../../i18n/setup").default,
+ }}>
+
+
+
+
+)
diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.tsx
new file mode 100644
index 0000000000..9fea71d4e0
--- /dev/null
+++ b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.tsx
@@ -0,0 +1,18 @@
+import React from "react"
+
+import { expect, test } from "../../../../playwright/coverage-fixture"
+import { AutoApproveSettingsFixture } from "./AutoApproveSettings.visual.fixture"
+
+test("renders follow-up timeout row with disabled state label in the VS Code dark theme", async ({ mount, page }) => {
+ // The full settings bundle can leave a bare Zod reference after CT tree-shaking.
+ await page.evaluate(() => Object.assign(globalThis, { z: undefined }))
+
+ const component = await mount( )
+
+ await component.evaluate(async () => {
+ await document.fonts.ready
+ await new Promise((resolve) => requestAnimationFrame(() => resolve()))
+ })
+
+ await expect(component).toHaveScreenshot("auto-approve-followup-timeout-disabled-dark.png")
+})
diff --git a/webview-ui/src/components/settings/__tests__/__screenshots__/auto-approve-followup-timeout-disabled-dark.png b/webview-ui/src/components/settings/__tests__/__screenshots__/auto-approve-followup-timeout-disabled-dark.png
new file mode 100644
index 0000000000000000000000000000000000000000..db87f68911f60194136207b64222fe42540967af
GIT binary patch
literal 205
zcmeAS@N?(olHy`uVBq!ia0y~yVB!F>6*!oH*
k|8@~+mw~Qf1G$Eofo~1NT#m%>1wau7Pgg&ebxsLQ04Fjnga7~l
literal 0
HcmV?d00001
diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json
index 73827b1ec1..c8efd130c4 100644
--- a/webview-ui/src/i18n/locales/ca/settings.json
+++ b/webview-ui/src/i18n/locales/ca/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Pregunta",
"description": "Seleccionar automàticament la primera resposta suggerida per a preguntes de seguiment després del temps d'espera configurat",
- "timeoutLabel": "Temps d'espera abans de seleccionar automàticament la primera resposta"
+ "timeoutLabel": "Temps d'espera abans de seleccionar automàticament la primera resposta",
+ "timeoutDisabled": "Desactivat"
},
"execute": {
"label": "Executar",
diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json
index 8078037525..b650941fc5 100644
--- a/webview-ui/src/i18n/locales/de/settings.json
+++ b/webview-ui/src/i18n/locales/de/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Frage",
"description": "Automatisch die erste vorgeschlagene Antwort für Folgefragen nach der konfigurierten Zeitüberschreitung auswählen",
- "timeoutLabel": "Wartezeit vor der automatischen Auswahl der ersten Antwort"
+ "timeoutLabel": "Wartezeit vor der automatischen Auswahl der ersten Antwort",
+ "timeoutDisabled": "Deaktiviert"
},
"execute": {
"label": "Ausführen",
diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json
index 14a7476a75..6f4ac738f0 100644
--- a/webview-ui/src/i18n/locales/en/settings.json
+++ b/webview-ui/src/i18n/locales/en/settings.json
@@ -401,7 +401,8 @@
"followupQuestions": {
"label": "Question",
"description": "Automatically select the first suggested answer for follow-up questions after the configured timeout",
- "timeoutLabel": "Time to wait before auto-selecting the first answer"
+ "timeoutLabel": "Time to wait before auto-selecting the first answer",
+ "timeoutDisabled": "Disabled"
},
"execute": {
"label": "Execute",
diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json
index e629e43b50..529d5f7fad 100644
--- a/webview-ui/src/i18n/locales/es/settings.json
+++ b/webview-ui/src/i18n/locales/es/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Pregunta",
"description": "Seleccionar automáticamente la primera respuesta sugerida para preguntas de seguimiento después del tiempo de espera configurado",
- "timeoutLabel": "Tiempo de espera antes de seleccionar automáticamente la primera respuesta"
+ "timeoutLabel": "Tiempo de espera antes de seleccionar automáticamente la primera respuesta",
+ "timeoutDisabled": "Deshabilitado"
},
"execute": {
"label": "Ejecutar",
diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json
index 6048e2274c..9b2358bae8 100644
--- a/webview-ui/src/i18n/locales/fr/settings.json
+++ b/webview-ui/src/i18n/locales/fr/settings.json
@@ -324,7 +324,8 @@
"followupQuestions": {
"label": "Question",
"description": "Sélectionner automatiquement la première réponse suggérée pour les questions de suivi après le délai configuré",
- "timeoutLabel": "Temps d'attente avant la sélection automatique de la première réponse"
+ "timeoutLabel": "Temps d'attente avant la sélection automatique de la première réponse",
+ "timeoutDisabled": "Désactivé"
},
"execute": {
"label": "Exécuter",
diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json
index 28d0b8699b..2c03328615 100644
--- a/webview-ui/src/i18n/locales/hi/settings.json
+++ b/webview-ui/src/i18n/locales/hi/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "प्रश्न",
"description": "कॉन्फ़िगर किए गए टाइमआउट के बाद अनुवर्ती प्रश्नों के लिए पहले सुझाए गए उत्तर को स्वचालित रूप से चुनें",
- "timeoutLabel": "पहले उत्तर को स्वचालित रूप से चुनने से पहले प्रतीक्षा करने का समय"
+ "timeoutLabel": "पहले उत्तर को स्वचालित रूप से चुनने से पहले प्रतीक्षा करने का समय",
+ "timeoutDisabled": "अक्षम"
},
"execute": {
"label": "निष्पादित करें",
diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json
index bf049395c4..81c317214a 100644
--- a/webview-ui/src/i18n/locales/id/settings.json
+++ b/webview-ui/src/i18n/locales/id/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Pertanyaan",
"description": "Secara otomatis memilih jawaban pertama yang disarankan untuk pertanyaan lanjutan setelah batas waktu yang dikonfigurasi",
- "timeoutLabel": "Waktu tunggu sebelum otomatis memilih jawaban pertama"
+ "timeoutLabel": "Waktu tunggu sebelum otomatis memilih jawaban pertama",
+ "timeoutDisabled": "Dinonaktifkan"
},
"execute": {
"label": "Eksekusi",
diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json
index 577a74a77a..35259f3382 100644
--- a/webview-ui/src/i18n/locales/it/settings.json
+++ b/webview-ui/src/i18n/locales/it/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Domanda",
"description": "Seleziona automaticamente la prima risposta suggerita per le domande di follow-up dopo il timeout configurato",
- "timeoutLabel": "Tempo di attesa prima di selezionare automaticamente la prima risposta"
+ "timeoutLabel": "Tempo di attesa prima di selezionare automaticamente la prima risposta",
+ "timeoutDisabled": "Disabilitato"
},
"execute": {
"label": "Esegui",
diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json
index 1113ac32a6..169eb60858 100644
--- a/webview-ui/src/i18n/locales/ja/settings.json
+++ b/webview-ui/src/i18n/locales/ja/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "質問",
"description": "設定された時間が経過すると、フォローアップ質問の最初の提案回答を自動的に選択します",
- "timeoutLabel": "最初の回答を自動選択するまでの待機時間"
+ "timeoutLabel": "最初の回答を自動選択するまでの待機時間",
+ "timeoutDisabled": "無効"
},
"execute": {
"label": "実行",
diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json
index 27e8493bd4..39e780c3c7 100644
--- a/webview-ui/src/i18n/locales/ko/settings.json
+++ b/webview-ui/src/i18n/locales/ko/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "질문",
"description": "설정된 시간이 지나면 후속 질문에 대한 첫 번째 제안 답변을 자동으로 선택합니다",
- "timeoutLabel": "첫 번째 답변을 자동 선택하기 전 대기 시간"
+ "timeoutLabel": "첫 번째 답변을 자동 선택하기 전 대기 시간",
+ "timeoutDisabled": "사용 안 함"
},
"execute": {
"label": "실행",
diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json
index 394cdd48f2..27f6ebccfb 100644
--- a/webview-ui/src/i18n/locales/nl/settings.json
+++ b/webview-ui/src/i18n/locales/nl/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Vraag",
"description": "Selecteer automatisch het eerste voorgestelde antwoord voor vervolgvragen na de geconfigureerde time-out",
- "timeoutLabel": "Wachttijd voordat het eerste antwoord automatisch wordt geselecteerd"
+ "timeoutLabel": "Wachttijd voordat het eerste antwoord automatisch wordt geselecteerd",
+ "timeoutDisabled": "Uitgeschakeld"
},
"execute": {
"label": "Uitvoeren",
diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json
index 864e4ffde1..1e9fb88c1b 100644
--- a/webview-ui/src/i18n/locales/pl/settings.json
+++ b/webview-ui/src/i18n/locales/pl/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Pytanie",
"description": "Automatycznie wybierz pierwszą sugerowaną odpowiedź na pytania uzupełniające po skonfigurowanym limicie czasu",
- "timeoutLabel": "Czas oczekiwania przed automatycznym wybraniem pierwszej odpowiedzi"
+ "timeoutLabel": "Czas oczekiwania przed automatycznym wybraniem pierwszej odpowiedzi",
+ "timeoutDisabled": "Wyłączone"
},
"execute": {
"label": "Wykonaj",
diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json
index a1948a0218..078b78e815 100644
--- a/webview-ui/src/i18n/locales/pt-BR/settings.json
+++ b/webview-ui/src/i18n/locales/pt-BR/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Pergunta",
"description": "Selecionar automaticamente a primeira resposta sugerida para perguntas de acompanhamento após o tempo limite configurado",
- "timeoutLabel": "Tempo de espera antes de selecionar automaticamente a primeira resposta"
+ "timeoutLabel": "Tempo de espera antes de selecionar automaticamente a primeira resposta",
+ "timeoutDisabled": "Desativado"
},
"execute": {
"label": "Executar",
diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json
index f36fe62539..bb807c930f 100644
--- a/webview-ui/src/i18n/locales/ru/settings.json
+++ b/webview-ui/src/i18n/locales/ru/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Вопрос",
"description": "Автоматически выбирать первый предложенный ответ на дополнительные вопросы после настроенного тайм-аута",
- "timeoutLabel": "Время ожидания перед автоматическим выбором первого ответа"
+ "timeoutLabel": "Время ожидания перед автоматическим выбором первого ответа",
+ "timeoutDisabled": "Отключено"
},
"execute": {
"label": "Выполнение",
diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json
index 9099677679..638fd6c8b2 100644
--- a/webview-ui/src/i18n/locales/tr/settings.json
+++ b/webview-ui/src/i18n/locales/tr/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Soru",
"description": "Yapılandırılan zaman aşımından sonra takip sorularına ilişkin ilk önerilen yanıtı otomatik olarak seç",
- "timeoutLabel": "İlk yanıtı otomatik olarak seçmeden önce beklenecek süre"
+ "timeoutLabel": "İlk yanıtı otomatik olarak seçmeden önce beklenecek süre",
+ "timeoutDisabled": "Devre dışı"
},
"execute": {
"label": "Yürüt",
diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json
index c66b236165..11d368ea1e 100644
--- a/webview-ui/src/i18n/locales/vi/settings.json
+++ b/webview-ui/src/i18n/locales/vi/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "Câu hỏi",
"description": "Tự động chọn câu trả lời đầu tiên được đề xuất cho các câu hỏi tiếp theo sau thời gian chờ đã cấu hình",
- "timeoutLabel": "Thời gian chờ trước khi tự động chọn câu trả lời đầu tiên"
+ "timeoutLabel": "Thời gian chờ trước khi tự động chọn câu trả lời đầu tiên",
+ "timeoutDisabled": "Đã tắt"
},
"execute": {
"label": "Thực thi",
diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json
index 22742e0e0e..fec2653574 100644
--- a/webview-ui/src/i18n/locales/zh-CN/settings.json
+++ b/webview-ui/src/i18n/locales/zh-CN/settings.json
@@ -323,7 +323,8 @@
"followupQuestions": {
"label": "问题",
"description": "在配置的超时时间后自动选择后续问题的第一个建议答案",
- "timeoutLabel": "自动选择第一个答案前的等待时间"
+ "timeoutLabel": "自动选择第一个答案前的等待时间",
+ "timeoutDisabled": "已禁用"
},
"execute": {
"label": "执行",
diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json
index 4255a2e697..80a7a94dbd 100644
--- a/webview-ui/src/i18n/locales/zh-TW/settings.json
+++ b/webview-ui/src/i18n/locales/zh-TW/settings.json
@@ -348,7 +348,8 @@
"followupQuestions": {
"label": "後續提問",
"description": "在設定的逾時時間過後,自動選擇後續問題的第一個建議答案",
- "timeoutLabel": "自動選擇第一個答案前的等待時間"
+ "timeoutLabel": "自動選擇第一個答案前的等待時間",
+ "timeoutDisabled": "已停用"
},
"execute": {
"label": "執行命令",
From 9411e41853f512b970473d7cc0bbccc30a53a009 Mon Sep 17 00:00:00 2001
From: Para <62068271+murd0cc@users.noreply.github.com>
Date: Wed, 12 Aug 2026 00:13:19 +0300
Subject: [PATCH 3/4] Maintainability & Code Quality
---
webview-ui/eslint-suppressions.json | 1 +
.../__tests__/AutoApproveSettings.spec.tsx | 83 +++++++++++++++++--
.../AutoApproveSettings.visual.fixture.tsx | 24 ++----
3 files changed, 85 insertions(+), 23 deletions(-)
create mode 100644 webview-ui/eslint-suppressions.json
diff --git a/webview-ui/eslint-suppressions.json b/webview-ui/eslint-suppressions.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/webview-ui/eslint-suppressions.json
@@ -0,0 +1 @@
+{}
diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
index 5ee6026ed8..37fa40024d 100644
--- a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
+++ b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.spec.tsx
@@ -33,6 +33,24 @@ vi.mock("@/hooks/useAutoApprovalState", () => ({
useAutoApprovalState: () => ({ effectiveAutoApprovalEnabled: false, hasEnabledOptions: false }),
}))
+vi.mock("@/components/ui", async (importOriginal) => {
+ const actual = await importOriginal()
+
+ return {
+ ...actual,
+ Button: ({ children, ...props }: any) => {children} ,
+ Input: (props: any) => ,
+ Slider: ({ value, onValueChange, ...props }: any) => (
+ onValueChange?.([Number((event.target as HTMLInputElement).value)])}
+ {...props}
+ />
+ ),
+ }
+})
+
const renderSettings = (overrides = {}) => {
const setCachedStateField = vi.fn()
const props = {
@@ -163,16 +181,69 @@ describe("AutoApproveSettings - Save/Discard contract", () => {
})
it("renders disabled timeout label when follow-up auto-approve timeout is 0", () => {
- renderSettings({ alwaysAllowFollowupQuestions: true, followupAutoApproveTimeoutMs: 0 })
-
- expect(screen.getByTestId("followup-timeout-slider")).toBeInTheDocument()
+ const { setCachedStateField } = renderSettings({
+ alwaysAllowFollowupQuestions: true,
+ followupAutoApproveTimeoutMs: 0,
+ })
+
+ const slider = screen.getByTestId("followup-timeout-slider") as HTMLInputElement
+ expect(slider).toBeInTheDocument()
+ expect(slider.value).toBe("0")
expect(screen.getByText("settings:autoApprove.followupQuestions.timeoutDisabled")).toBeInTheDocument()
+
+ fireEvent.change(slider, { target: { value: "4000" } })
+
+ expect(setCachedStateField).toHaveBeenCalledWith("followupAutoApproveTimeoutMs", 4000)
+ expectNoImmediateUpdateSettings()
})
it("renders timeout in seconds when follow-up auto-approve timeout is non-zero", () => {
- renderSettings({ alwaysAllowFollowupQuestions: true, followupAutoApproveTimeoutMs: 5000 })
-
- expect(screen.getByTestId("followup-timeout-slider")).toBeInTheDocument()
+ const { setCachedStateField } = renderSettings({
+ alwaysAllowFollowupQuestions: true,
+ followupAutoApproveTimeoutMs: 5000,
+ })
+
+ const slider = screen.getByTestId("followup-timeout-slider") as HTMLInputElement
+ expect(slider).toBeInTheDocument()
+ expect(slider.value).toBe("5000")
expect(screen.getByText("5s")).toBeInTheDocument()
+
+ fireEvent.change(slider, { target: { value: "0" } })
+
+ expect(setCachedStateField).toHaveBeenCalledWith("followupAutoApproveTimeoutMs", 0)
+ expectNoImmediateUpdateSettings()
+ })
+
+ it("uses the default timeout value when timeout is unset and follow-up auto-approve is enabled", () => {
+ renderSettings({ alwaysAllowFollowupQuestions: true })
+
+ const slider = screen.getByTestId("followup-timeout-slider") as HTMLInputElement
+ expect(slider.value).toBe("60000")
+ expect(screen.getByText("60s")).toBeInTheDocument()
+ })
+
+ it("does not render the follow-up timeout controls when follow-up auto-approve is disabled or unset", () => {
+ const { rerender } = render(
+ ,
+ )
+
+ expect(screen.queryByTestId("followup-timeout-slider")).not.toBeInTheDocument()
+
+ rerender(
+ ,
+ )
+
+ expect(screen.queryByTestId("followup-timeout-slider")).not.toBeInTheDocument()
})
})
diff --git a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
index 6f4e4707e3..0002607348 100644
--- a/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
+++ b/webview-ui/src/components/settings/__tests__/AutoApproveSettings.visual.fixture.tsx
@@ -2,28 +2,18 @@
import React from "react"
import { TranslationContext } from "@/i18n/TranslationContext"
-import { ExtensionStateContext } from "@/context/ExtensionStateContext"
+import i18next from "@/i18n/setup"
+import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext"
import { AutoApproveSettings } from "../AutoApproveSettings"
-const extensionStateValue = {
- autoApprovalEnabled: false,
- setAutoApprovalEnabled: () => {},
- alwaysAllowReadOnly: false,
- alwaysAllowWrite: false,
- alwaysAllowExecute: false,
- alwaysAllowMcp: false,
- alwaysAllowModeSwitch: false,
- alwaysAllowSubtasks: false,
- alwaysAllowFollowupQuestions: true,
-} as any
-
export const AutoApproveSettingsFixture = () => (
key,
- i18n: null as unknown as typeof import("../../../i18n/setup").default,
+ t: (key) => (key === "settings:autoApprove.followupQuestions.timeoutDisabled" ? "Disabled" : key),
+ i18n: i18next,
}}>
-
+
(
setCachedStateField={() => {}}
/>
-
+
)
From ca2ed956a63a6389e27c5031e9e38d3f0a65880e Mon Sep 17 00:00:00 2001
From: Para <62068271+murd0cc@users.noreply.github.com>
Date: Wed, 12 Aug 2026 00:38:51 +0300
Subject: [PATCH 4/4] coverage improvements
---
.../chat/__tests__/FollowUpSuggest.spec.tsx | 94 ++++++++++++++++++-
1 file changed, 92 insertions(+), 2 deletions(-)
diff --git a/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx b/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
index 169df1aee4..e6d623b389 100644
--- a/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
+++ b/webview-ui/src/components/chat/__tests__/FollowUpSuggest.spec.tsx
@@ -1,5 +1,5 @@
import React, { createContext, useContext } from "react"
-import { render, screen, act } from "@testing-library/react"
+import { render, screen, act, fireEvent } from "@testing-library/react"
import { TooltipProvider } from "@radix-ui/react-tooltip"
import { FollowUpSuggest } from "../FollowUpSuggest"
@@ -28,7 +28,7 @@ vi.mock("@src/i18n/TranslationContext", () => ({
interface TestExtensionState {
autoApprovalEnabled: boolean
alwaysAllowFollowupQuestions: boolean
- followupAutoApproveTimeoutMs: number
+ followupAutoApproveTimeoutMs?: number
}
const TestExtensionStateContext = createContext(undefined)
@@ -749,4 +749,94 @@ describe("FollowUpSuggest", () => {
expect(mockOnCancelAutoApproval).toHaveBeenCalled()
})
})
+
+ describe("suggestion interactions", () => {
+ it("cancels countdown and forwards click when user clicks a suggestion", () => {
+ renderWithTestProviders(
+ ,
+ defaultTestState,
+ )
+
+ fireEvent.click(screen.getByText("First suggestion"))
+
+ expect(mockOnSuggestionClick).toHaveBeenCalledWith(
+ expect.objectContaining({ answer: "First suggestion" }),
+ expect.objectContaining({ shiftKey: false }),
+ )
+ expect(mockOnCancelAutoApproval).toHaveBeenCalled()
+ expect(screen.queryByText(/Selecting in \d+s/)).not.toBeInTheDocument()
+ })
+
+ it("keeps countdown when shift-clicking a suggestion", () => {
+ renderWithTestProviders(
+ ,
+ defaultTestState,
+ )
+
+ mockOnCancelAutoApproval.mockClear()
+ fireEvent.click(screen.getByText("First suggestion"), { shiftKey: true })
+
+ expect(mockOnSuggestionClick).toHaveBeenCalledWith(
+ expect.objectContaining({ answer: "First suggestion" }),
+ expect.objectContaining({ shiftKey: true }),
+ )
+ expect(mockOnCancelAutoApproval).not.toHaveBeenCalled()
+ expect(screen.getByText(/Selecting in 3s/)).toBeInTheDocument()
+ })
+
+ it("copies suggestion into input when the copy affordance is clicked", () => {
+ const { container } = renderWithTestProviders(
+ ,
+ defaultTestState,
+ )
+
+ const copyAffordance = container.querySelector(
+ ".absolute.cursor-pointer.top-1\\.5.right-1\\.5",
+ ) as HTMLElement
+
+ expect(copyAffordance).toBeTruthy()
+ fireEvent.click(copyAffordance)
+
+ expect(mockOnSuggestionClick).toHaveBeenCalledWith(
+ expect.objectContaining({ answer: "First suggestion" }),
+ expect.objectContaining({ shiftKey: true }),
+ )
+ expect(mockOnCancelAutoApproval).toHaveBeenCalled()
+ expect(screen.queryByText(/Selecting in \d+s/)).not.toBeInTheDocument()
+ })
+
+ it("uses default timeout when extension state timeout is undefined", () => {
+ const stateWithUndefinedTimeout = {
+ ...defaultTestState,
+ followupAutoApproveTimeoutMs: undefined,
+ }
+
+ renderWithTestProviders(
+ ,
+ stateWithUndefinedTimeout,
+ )
+
+ expect(screen.getByText(/Selecting in 60s/)).toBeInTheDocument()
+ })
+ })
})