Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f188b79
cmdio: pin promptui prompt behavior with golden tests
pietern May 8, 2026
225f3a7
Merge remote-tracking branch 'origin/main' into cmdiotest-only
pietern May 8, 2026
b618887
cmdiotest: pin RunSelect post-Enter rendering with Selected template
pietern May 8, 2026
cc51622
cmdiotest: replace Delete with Backspace in cursor-editing test, pin …
pietern May 8, 2026
190c2b5
cmdiotest/termtest: hold back partial UTF-8 from vt10x in pump
pietern May 8, 2026
72f9d7b
cmdiotest: pin RunPrompt's HideEntered post-Enter rendering
pietern May 8, 2026
b4e90e2
cmdiotest: pin Alt-prefixed keys as no-ops in RunPrompt
pietern May 8, 2026
5a62855
cmdiotest: pin Ctrl+F and Ctrl+B as cursor-movement aliases in RunPrompt
pietern May 8, 2026
98596cc
cmdiotest: pin Ctrl+N/P (item) and Ctrl+F/B (page) navigation in Select
pietern May 8, 2026
cf62d35
cmdiotest: cover three Select code paths the existing baselines missed
pietern May 8, 2026
a6057b9
cmdiotest: pin RunSelect's default-template rendering
pietern May 8, 2026
1689377
cmdiotest: pin Ctrl+H (backspace) and Ctrl+J (enter) aliases in promp…
pietern May 11, 2026
1664096
cmdiotest: loosen Ctrl+J Select assertion to allow future bug fix
pietern May 11, 2026
03d9b8c
cmdiotest: pin PromptOptions.Default cursor-movement quirk
pietern May 11, 2026
b478dc6
cmdiotest: fix pre-existing lint issues in baseline tests
pietern May 11, 2026
3f5a50d
cmdiotest: pin pending-default first-key transitions in Prompt
pietern May 11, 2026
9e7f8ff
Drop unused Default field from cmdio.PromptOptions
pietern May 11, 2026
d4b3e68
Merge branch 'prompt-drop-default' into cmdiotest-only
pietern May 11, 2026
400f9ca
Merge remote-tracking branch 'origin/main' into cmdiotest
pietern May 11, 2026
ac30dac
Merge remote-tracking branch 'origin/main' into cmdiotest
pietern May 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ golang.org/x/sys - https://github.com/golang/sys
Copyright 2009 The Go Authors.
License - https://github.com/golang/sys/blob/master/LICENSE

golang.org/x/term - https://github.com/golang/term
Copyright 2009 The Go Authors.
License - https://github.com/golang/term/blob/master/LICENSE

golang.org/x/text - https://github.com/golang/text
Copyright 2009 The Go Authors.
License - https://github.com/golang/text/blob/master/LICENSE
Expand Down Expand Up @@ -171,3 +175,11 @@ License - https://github.com/yaml/go-yaml/blob/v3/LICENSE
zalando/go-keyring - https://github.com/zalando/go-keyring
Copyright (c) 2016 Zalando SE
License - https://github.com/zalando/go-keyring/blob/master/LICENSE

creack/pty - https://github.com/creack/pty
Copyright (c) 2019 Christopher Koch
License - https://github.com/creack/pty/blob/master/LICENSE

hinshun/vt10x - https://github.com/hinshun/vt10x
Copyright (c) 2017 ActiveState Software Inc.
License - https://github.com/hinshun/vt10x/blob/master/LICENSE
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/charmbracelet/bubbletea v1.3.10 // MIT
github.com/charmbracelet/huh v1.0.0 // MIT
github.com/charmbracelet/lipgloss v1.1.0 // MIT
github.com/creack/pty v1.1.24 // MIT
github.com/databricks/databricks-sdk-go v0.128.0 // Apache-2.0
github.com/google/jsonschema-go v0.4.3 // MIT
github.com/google/uuid v1.6.0 // BSD-3-Clause
Expand All @@ -21,6 +22,7 @@ require (
github.com/hashicorp/terraform-exec v0.25.0 // MPL-2.0
github.com/hashicorp/terraform-json v0.27.2 // MPL-2.0
github.com/hexops/gotextdiff v1.0.3 // BSD-3-Clause
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 // MIT
github.com/jackc/pgx/v5 v5.9.2 // MIT
github.com/manifoldco/promptui v0.9.0 // BSD-3-Clause
github.com/mattn/go-isatty v0.0.21 // MIT
Expand All @@ -39,6 +41,7 @@ require (
golang.org/x/oauth2 v0.36.0 // BSD-3-Clause
golang.org/x/sync v0.20.0 // BSD-3-Clause
golang.org/x/sys v0.43.0 // BSD-3-Clause
golang.org/x/term v0.42.0 // BSD-3-Clause
golang.org/x/text v0.36.0 // BSD-3-Clause
gopkg.in/ini.v1 v1.67.1 // Apache-2.0
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ github.com/hashicorp/terraform-json v0.27.2 h1:BwGuzM6iUPqf9JYM/Z4AF1OJ5VVJEEzoK
github.com/hashicorp/terraform-json v0.27.2/go.mod h1:GzPLJ1PLdUG5xL6xn1OXWIjteQRT2CNT9o/6A9mi9hE=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY=
github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
Expand Down
79 changes: 79 additions & 0 deletions libs/cmdio/cmdiotest/prompt_alt_key_noop_baseline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cmdiotest_test

import (
"runtime"
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/cmdio/cmdiotest/termtest"
"github.com/databricks/cli/libs/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestPromptBaseline_AltKeyNoop pins that Alt-prefixed keys are silent
// no-ops in [cmdio.RunPrompt]. Specifically, Alt+f (the readline binding
// for "move forward by word") must neither move the cursor nor insert a
// literal 'f' into the buffer.
//
// Why: chzyer/readline does process Alt+f — it calls o.buf.MoveToNextWord
// and fires the listener with key=MetaForward — but promptui's
// Cursor.Listen has no case for MetaForward and falls to a default branch
// that only does anything in erase-default mode. The listener wrapper
// then returns (nil, 0, true), which makes readline overwrite its buffer
// with empty. Net effect on the user-visible state (promptui's own
// `cur`): nothing changes.
//
// The same shape applies to Alt+b, Alt+d, Alt+Backspace and any other
// modified key promptui doesn't handle. Pinning Alt+f covers the class.
func TestPromptBaseline_AltKeyNoop(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("pty-based prompt tests are unix-only")
}

tm := termtest.New(t)
defer tm.Close()

pts := tm.Pty()
t.Setenv("NO_COLOR", "")
t.Setenv("TERM", "xterm-256color")

ctx := t.Context()
io := cmdio.NewIO(ctx, flags.OutputText, pts, pts, pts, "", "")
ctx = cmdio.InContext(ctx, io)

require.True(t, cmdio.IsPromptSupported(ctx), "prompt support must be detected on the pty")

type result struct {
value string
err error
}
resCh := make(chan result, 1)
go func() {
v, err := cmdio.RunPrompt(ctx, cmdio.PromptOptions{
Label: "Workspace name",
})
resCh <- result{value: v, err: err}
}()

tm.WaitFor("Workspace name")

// Type "hello" and move cursor two places left so it sits mid-word.
// If Alt+f moved the cursor (or inserted), goldens 01 and 02 would
// diverge.
tm.Type("hello")
tm.Type(termtest.KeyLeft)
tm.Type(termtest.KeyLeft)
tm.Golden("01-cursor-mid")

tm.Type("\x1bf")
tm.Golden("02-after-alt-f")

tm.Type(termtest.KeyEnter)
res := <-resCh
require.NoError(t, res.err, "raw output: %q", tm.Raw())
// Final guard: the returned value must be exactly "hello". A literal
// 'f' insertion would surface here even if the goldens above somehow
// missed it.
assert.Equal(t, "hello", res.value)
}
59 changes: 59 additions & 0 deletions libs/cmdio/cmdiotest/prompt_ctrl_c_baseline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cmdiotest_test

import (
"runtime"
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/cmdio/cmdiotest/termtest"
"github.com/databricks/cli/libs/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestPromptBaseline_CtrlC pins Ctrl+C cancellation for RunPrompt. Mirrors
// the equivalent Secret test: error is returned, value is empty, snapshot
// captures any "^C" that the terminal echoed.
func TestPromptBaseline_CtrlC(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("pty-based prompt tests are unix-only")
}

tm := termtest.New(t)
defer tm.Close()

pts := tm.Pty()
t.Setenv("NO_COLOR", "")
t.Setenv("TERM", "xterm-256color")

ctx := t.Context()
io := cmdio.NewIO(ctx, flags.OutputText, pts, pts, pts, "", "")
ctx = cmdio.InContext(ctx, io)

require.True(t, cmdio.IsPromptSupported(ctx), "prompt support must be detected on the pty")

type result struct {
value string
err error
}
resCh := make(chan result, 1)
go func() {
v, err := cmdio.RunPrompt(ctx, cmdio.PromptOptions{
Label: "Workspace name",
})
resCh <- result{value: v, err: err}
}()

tm.WaitFor("Workspace name")
tm.Golden("01-empty")

tm.Type("partial input")
tm.Golden("02-after-typing")

tm.Type(termtest.KeyCtrlC)

res := <-resCh
require.Error(t, res.err)
assert.Empty(t, res.value)
t.Logf("error: %v", res.err)
}
68 changes: 68 additions & 0 deletions libs/cmdio/cmdiotest/prompt_ctrl_fb_baseline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package cmdiotest_test

import (
"runtime"
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/cmdio/cmdiotest/termtest"
"github.com/databricks/cli/libs/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestPromptBaseline_CtrlFCtrlB pins that Ctrl+F and Ctrl+B move the cursor
// one character forward and backward in [cmdio.RunPrompt], the same as the
// right and left arrow keys.
//
// chzyer/readline maps Ctrl+F to CharForward and Ctrl+B to CharBackward —
// the same runes the arrow keys decode to — and promptui's Cursor.Listen
// dispatches both via its KeyForward / KeyBackward cases. So the emacs-
// style bindings are de-facto aliases for the arrow keys; this test pins
// that equivalence.
func TestPromptBaseline_CtrlFCtrlB(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("pty-based prompt tests are unix-only")
}

tm := termtest.New(t)
defer tm.Close()

pts := tm.Pty()
t.Setenv("NO_COLOR", "")
t.Setenv("TERM", "xterm-256color")

ctx := t.Context()
io := cmdio.NewIO(ctx, flags.OutputText, pts, pts, pts, "", "")
ctx = cmdio.InContext(ctx, io)

require.True(t, cmdio.IsPromptSupported(ctx), "prompt support must be detected on the pty")

type result struct {
value string
err error
}
resCh := make(chan result, 1)
go func() {
v, err := cmdio.RunPrompt(ctx, cmdio.PromptOptions{
Label: "Workspace name",
})
resCh <- result{value: v, err: err}
}()

tm.WaitFor("Workspace name")
tm.Type("hello")
tm.Golden("01-cursor-end")

tm.Type(termtest.KeyCtrlB)
tm.Type(termtest.KeyCtrlB)
tm.Golden("02-after-ctrl-b-twice")

tm.Type(termtest.KeyCtrlF)
tm.Golden("03-after-ctrl-f")

tm.Type(termtest.KeyEnter)
res := <-resCh
require.NoError(t, res.err, "raw output: %q", tm.Raw())
assert.Equal(t, "hello", res.value)
}
64 changes: 64 additions & 0 deletions libs/cmdio/cmdiotest/prompt_ctrl_h_baseline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cmdiotest_test

import (
"runtime"
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/cmdio/cmdiotest/termtest"
"github.com/databricks/cli/libs/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestPromptBaseline_CtrlH pins that Ctrl+H deletes the character to the
// left of the cursor in [cmdio.RunPrompt] — the same as the Backspace key.
//
// Ctrl+H sends BS (0x08) and Backspace sends DEL (0x7f). chzyer/readline
// maps both to CharBackspace, and promptui's Cursor.Listen handles them
// uniformly via its KeyBackspace case. So the control-character form is
// a de-facto alias for the Backspace key; this test pins that equivalence
// so a future hand-rolled prompt implementation can't silently drop it.
func TestPromptBaseline_CtrlH(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("pty-based prompt tests are unix-only")
}

tm := termtest.New(t)
defer tm.Close()

pts := tm.Pty()
t.Setenv("NO_COLOR", "")
t.Setenv("TERM", "xterm-256color")

ctx := t.Context()
io := cmdio.NewIO(ctx, flags.OutputText, pts, pts, pts, "", "")
ctx = cmdio.InContext(ctx, io)

require.True(t, cmdio.IsPromptSupported(ctx), "prompt support must be detected on the pty")

type result struct {
value string
err error
}
resCh := make(chan result, 1)
go func() {
v, err := cmdio.RunPrompt(ctx, cmdio.PromptOptions{
Label: "Workspace name",
})
resCh <- result{value: v, err: err}
}()

tm.WaitFor("Workspace name")
tm.Type("hello")
tm.Golden("01-typed-hello")

tm.Type(termtest.KeyCtrlH)
tm.Type(termtest.KeyCtrlH)
tm.Golden("02-after-ctrl-h-twice")

tm.Type(termtest.KeyEnter)
res := <-resCh
require.NoError(t, res.err, "raw output: %q", tm.Raw())
assert.Equal(t, "hel", res.value)
}
59 changes: 59 additions & 0 deletions libs/cmdio/cmdiotest/prompt_ctrl_j_baseline_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cmdiotest_test

import (
"runtime"
"testing"

"github.com/databricks/cli/libs/cmdio"
"github.com/databricks/cli/libs/cmdio/cmdiotest/termtest"
"github.com/databricks/cli/libs/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestPromptBaseline_CtrlJ pins that Ctrl+J submits the prompt in
// [cmdio.RunPrompt] — the same as the Enter (Return) key.
//
// Enter sends CR (0x0d) and Ctrl+J sends LF (0x0a). chzyer/readline maps
// both to CharEnter and ends the read loop, so promptui returns the
// current buffer either way. A future hand-rolled prompt that only
// reacts to CR would silently swallow Ctrl+J; this test pins the parity.
func TestPromptBaseline_CtrlJ(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("pty-based prompt tests are unix-only")
}

tm := termtest.New(t)
defer tm.Close()

pts := tm.Pty()
t.Setenv("NO_COLOR", "")
t.Setenv("TERM", "xterm-256color")

ctx := t.Context()
io := cmdio.NewIO(ctx, flags.OutputText, pts, pts, pts, "", "")
ctx = cmdio.InContext(ctx, io)

require.True(t, cmdio.IsPromptSupported(ctx), "prompt support must be detected on the pty")

type result struct {
value string
err error
}
resCh := make(chan result, 1)
go func() {
v, err := cmdio.RunPrompt(ctx, cmdio.PromptOptions{
Label: "Workspace name",
})
resCh <- result{value: v, err: err}
}()

tm.WaitFor("Workspace name")
tm.Type("hello")
tm.Golden("01-typed-hello")

tm.Type(termtest.KeyCtrlJ)
res := <-resCh
require.NoError(t, res.err, "raw output: %q", tm.Raw())
assert.Equal(t, "hello", res.value)
}
Loading
Loading