From 425b6b31e804283bb42e5d7df99ef6284519569c Mon Sep 17 00:00:00 2001 From: Jason Ma Date: Thu, 28 May 2026 10:42:03 +0800 Subject: [PATCH] fix(cli): apply bracketed default on empty interactive prompt (#4387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `promptOrDefault` displayed the default value in the prompt label (e.g. `Choose [6]:`) but only substituted that default in non-interactive mode. In interactive mode it returned the raw `deps.prompt()` reply, which is the empty string on bare Enter, leaving every interactive caller to re-implement the fallback. The resource-profile prompt did not, so pressing Enter at `Choose [6]:` exited with `Invalid resource profile selection ''.` even though the prompt advertised 6 as the default. Fall back to `defaultValue` when the interactive reply is empty or whitespace-only. Callers that already had their own `|| "25%"`-style fallback continue to work unchanged — the helper-level default fires first and the caller's redundant branch never triggers. Add `prompt-helpers.test.ts` pinning the new behavior (empty → default, whitespace → default, non-empty → verbatim). The function previously had no direct tests; the interactive branch was the untested path that hid this regression. Fixes #4387. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Jason Ma --- src/lib/onboard/prompt-helpers.test.ts | 31 ++++++++++++++++++++++++++ src/lib/onboard/prompt-helpers.ts | 7 +++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/lib/onboard/prompt-helpers.test.ts diff --git a/src/lib/onboard/prompt-helpers.test.ts b/src/lib/onboard/prompt-helpers.test.ts new file mode 100644 index 0000000000..33c6ea43bb --- /dev/null +++ b/src/lib/onboard/prompt-helpers.test.ts @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it, vi } from "vitest"; +// Import from compiled dist/ so coverage is attributed correctly. +import { promptOrDefault } from "../../../dist/lib/onboard/prompt-helpers"; + +function makeDeps(promptReply: string) { + return { + isNonInteractive: () => false, + note: vi.fn(), + prompt: vi.fn().mockResolvedValue(promptReply), + }; +} + +describe("promptOrDefault interactive default fallback (#4387)", () => { + it("returns defaultValue when the user just presses Enter (empty reply)", async () => { + const deps = makeDeps(""); + expect(await promptOrDefault(deps, " Choose [6]: ", null, "6")).toBe("6"); + }); + + it("treats a whitespace-only reply as the default", async () => { + const deps = makeDeps(" "); + expect(await promptOrDefault(deps, " Choose [6]: ", null, "6")).toBe("6"); + }); + + it("returns the user's reply verbatim when non-empty", async () => { + const deps = makeDeps("3"); + expect(await promptOrDefault(deps, " Choose [6]: ", null, "6")).toBe("3"); + }); +}); diff --git a/src/lib/onboard/prompt-helpers.ts b/src/lib/onboard/prompt-helpers.ts index c99e92f828..4c335f2581 100644 --- a/src/lib/onboard/prompt-helpers.ts +++ b/src/lib/onboard/prompt-helpers.ts @@ -49,7 +49,12 @@ export async function promptOrDefault( deps.note(` [non-interactive] ${question.trim()} → ${result}`); return result; } - return deps.prompt(question); + // The prompt label advertises the default in brackets (e.g. `Choose [6]:`), + // so an empty/whitespace reply must resolve to that default. Without this, + // every interactive caller had to re-implement the empty-reply fallback, + // and any that forgot hard-rejected the displayed default (#4387). + const reply = await deps.prompt(question); + return reply.trim() === "" ? defaultValue : reply; } // Yes/no prompt with a typed default. The `[Y/n]` / `[y/N]` indicator and