From a59fc754b6f1f274a939c81a12f86eb4199c8030 Mon Sep 17 00:00:00 2001 From: can1357 Date: Sun, 16 Aug 2026 01:53:08 +0200 Subject: [PATCH] test(tui): covered welcome model synchronization --- ...interactive-mode-default-plan-mode.test.ts | 27 +++++++++++++ .../test/modes/components/welcome.test.ts | 40 ------------------- 2 files changed, 27 insertions(+), 40 deletions(-) diff --git a/packages/coding-agent/test/interactive-mode-default-plan-mode.test.ts b/packages/coding-agent/test/interactive-mode-default-plan-mode.test.ts index 868484a40..f49748752 100644 --- a/packages/coding-agent/test/interactive-mode-default-plan-mode.test.ts +++ b/packages/coding-agent/test/interactive-mode-default-plan-mode.test.ts @@ -142,6 +142,33 @@ describe("InteractiveMode plan.defaultOnStartup", () => { expect(session?.getActiveToolNames()).toContain("read"); }); + it("keeps the welcome banner synchronized across startup and later model switches", async () => { + Settings.instance.set("startup.quiet", false); + const settings = Settings.isolated({ "plan.defaultOnStartup": true, "compaction.enabled": false }); + settings.setModelRole("plan", "anthropic/claude-haiku-4-5:high"); + const created = createHarness(settings); + const initialModel = session?.model; + if (!initialModel) throw new Error("Expected initial model"); + + await created.init({ suppressWelcomeIntro: true }); + + const planModel = session?.model; + if (!planModel) throw new Error("Expected plan model"); + expect(planModel.id).toBe("claude-haiku-4-5"); + const rendered = Bun.stripANSI(created.ui.render(120).join("\n")); + expect(rendered).toContain(planModel.name); + expect(rendered).not.toContain(initialModel.name); + + const requestRenderSpy = vi.spyOn(created.ui, "requestRender"); + requestRenderSpy.mockClear(); + await session!.setModel(initialModel); + + expect(requestRenderSpy).toHaveBeenCalled(); + const switched = Bun.stripANSI(created.ui.render(120).join("\n")); + expect(switched).toContain(initialModel.name); + expect(switched).not.toContain(planModel.name); + }); + it("activates write when entering plan mode even if it was hidden by discoveryMode (issue #3165)", async () => { // `plan-mode-active.md` instructs the agent to draft the plan file with // `write` and refine it with `edit`. Under `tools.discoveryMode === "all"` diff --git a/packages/coding-agent/test/modes/components/welcome.test.ts b/packages/coding-agent/test/modes/components/welcome.test.ts index bb8adfec2..f1d5997d1 100644 --- a/packages/coding-agent/test/modes/components/welcome.test.ts +++ b/packages/coding-agent/test/modes/components/welcome.test.ts @@ -62,43 +62,3 @@ describe("WelcomeComponent tips", () => { expect(pickWeightedTip([], 0.5)).toBe(""); }); }); - -// Regression coverage for the stale welcome banner bug: the component is -// constructed with the session model at init time, but init-time model changes -// (plan.defaultOnStartup, #reconcileModeFromSession, delayed config load) can -// swap the model before the model_changed subscription exists. The catch-up -// resync in InteractiveMode.init() calls setModel() after those steps; these -// tests verify setModel() produces the correct rendered output. -describe("WelcomeComponent model name", () => { - beforeAll(async () => { - await Settings.init({ inMemory: true }); - await initTheme(false); - }); - - it("updates the rendered model name after setModel()", () => { - const welcome = new WelcomeComponent("1.0.0", "DeepSeek V4 Pro", "deepseek"); - const before = welcome.render(100).join("\n"); - expect(before).toContain("DeepSeek V4 Pro"); - - welcome.setModel("GLM-5.2", "zhipu-coding-plan"); - const after = welcome.render(100).join("\n"); - expect(after).toContain("GLM-5.2"); - expect(after).not.toContain("DeepSeek V4 Pro"); - }); - - it("resyncs to the live model after init-time model switches (catch-up path)", () => { - // Banner constructed with the pre-config fallback model (the startup race - // picks an alphabetically-first provider before modelRoles.default loads). - const welcome = new WelcomeComponent("1.0.0", "DeepSeek V4 Pro", "deepseek"); - expect(welcome.render(100).join("\n")).toContain("DeepSeek V4 Pro"); - - // Config loads, session switches to the real default — the model_changed - // event fires before the subscription exists, so InteractiveMode.init() - // calls setModel() explicitly as a catch-up resync. - welcome.setModel("GLM-5.2", "zhipu-coding-plan"); - const rendered = welcome.render(100).join("\n"); - expect(rendered).toContain("GLM-5.2"); - expect(rendered).toContain("zhipu-coding-plan"); - expect(rendered).not.toContain("DeepSeek"); - }); -});