Merge pull request #10846 from cli/kw/accessible-prompter-and-disable-spinners-config
`gh config`: add config settings for accessible prompter and disabling spinner
This commit is contained in:
commit
c0f993aca0
11 changed files with 330 additions and 43 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
// they are defined here to avoid `cli/cli` being changed unexpectedly.
|
||||
const (
|
||||
accessibleColorsKey = "accessible_colors" // used by cli/go-gh to enable the use of customizable, accessible 4-bit colors.
|
||||
accessiblePrompterKey = "accessible_prompter"
|
||||
aliasesKey = "aliases"
|
||||
browserKey = "browser" // used by cli/go-gh to open URLs in web browsers
|
||||
colorLabelsKey = "color_labels"
|
||||
|
|
@ -29,6 +30,7 @@ const (
|
|||
pagerKey = "pager"
|
||||
promptKey = "prompt"
|
||||
preferEditorPromptKey = "prefer_editor_prompt"
|
||||
spinnerKey = "spinner"
|
||||
userKey = "user"
|
||||
usersKey = "users"
|
||||
versionKey = "version"
|
||||
|
|
@ -117,6 +119,11 @@ func (c *cfg) AccessibleColors(hostname string) gh.ConfigEntry {
|
|||
return c.GetOrDefault(hostname, accessibleColorsKey).Unwrap()
|
||||
}
|
||||
|
||||
func (c *cfg) AccessiblePrompter(hostname string) gh.ConfigEntry {
|
||||
// Intentionally panic if there is no user provided value or default value (which would be a programmer error)
|
||||
return c.GetOrDefault(hostname, accessiblePrompterKey).Unwrap()
|
||||
}
|
||||
|
||||
func (c *cfg) Browser(hostname string) gh.ConfigEntry {
|
||||
// Intentionally panic if there is no user provided value or default value (which would be a programmer error)
|
||||
return c.GetOrDefault(hostname, browserKey).Unwrap()
|
||||
|
|
@ -157,6 +164,11 @@ func (c *cfg) PreferEditorPrompt(hostname string) gh.ConfigEntry {
|
|||
return c.GetOrDefault(hostname, preferEditorPromptKey).Unwrap()
|
||||
}
|
||||
|
||||
func (c *cfg) Spinner(hostname string) gh.ConfigEntry {
|
||||
// Intentionally panic if there is no user provided value or default value (which would be a programmer error)
|
||||
return c.GetOrDefault(hostname, spinnerKey).Unwrap()
|
||||
}
|
||||
|
||||
func (c *cfg) Version() o.Option[string] {
|
||||
return c.get("", versionKey)
|
||||
}
|
||||
|
|
@ -550,6 +562,10 @@ browser:
|
|||
color_labels: disabled
|
||||
# Whether customizable, 4-bit accessible colors should be used. Supported values: enabled, disabled
|
||||
accessible_colors: disabled
|
||||
# Whether an accessible prompter should be used. Supported values: enabled, disabled
|
||||
accessible_prompter: disabled
|
||||
# Whether to use a animated spinner as a progress indicator. If disabled, a textual progress indicator is used instead. Supported values: enabled, disabled
|
||||
spinner: enabled
|
||||
`
|
||||
|
||||
type ConfigOption struct {
|
||||
|
|
@ -638,6 +654,24 @@ var Options = []ConfigOption{
|
|||
return c.AccessibleColors(hostname).Value
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: accessiblePrompterKey,
|
||||
Description: "whether an accessible prompter should be used",
|
||||
DefaultValue: "disabled",
|
||||
AllowedValues: []string{"enabled", "disabled"},
|
||||
CurrentValue: func(c gh.Config, hostname string) string {
|
||||
return c.AccessiblePrompter(hostname).Value
|
||||
},
|
||||
},
|
||||
{
|
||||
Key: spinnerKey,
|
||||
Description: "whether to use a animated spinner as a progress indicator",
|
||||
DefaultValue: "enabled",
|
||||
AllowedValues: []string{"enabled", "disabled"},
|
||||
CurrentValue: func(c gh.Config, hostname string) string {
|
||||
return c.Spinner(hostname).Value
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func HomeDirPath(subdir string) (string, error) {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,9 @@ func NewFromString(cfgStr string) *ghmock.ConfigMock {
|
|||
mock.AccessibleColorsFunc = func(hostname string) gh.ConfigEntry {
|
||||
return cfg.AccessibleColors(hostname)
|
||||
}
|
||||
mock.AccessiblePrompterFunc = func(hostname string) gh.ConfigEntry {
|
||||
return cfg.AccessiblePrompter(hostname)
|
||||
}
|
||||
mock.BrowserFunc = func(hostname string) gh.ConfigEntry {
|
||||
return cfg.Browser(hostname)
|
||||
}
|
||||
|
|
@ -79,6 +82,9 @@ func NewFromString(cfgStr string) *ghmock.ConfigMock {
|
|||
mock.PreferEditorPromptFunc = func(hostname string) gh.ConfigEntry {
|
||||
return cfg.PreferEditorPrompt(hostname)
|
||||
}
|
||||
mock.SpinnerFunc = func(hostname string) gh.ConfigEntry {
|
||||
return cfg.Spinner(hostname)
|
||||
}
|
||||
mock.VersionFunc = func() o.Option[string] {
|
||||
return cfg.Version()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ type Config interface {
|
|||
|
||||
// AccessibleColors returns the configured accessible_colors setting, optionally scoped by host.
|
||||
AccessibleColors(hostname string) ConfigEntry
|
||||
// AccessiblePrompter returns the configured accessible_prompter setting, optionally scoped by host.
|
||||
AccessiblePrompter(hostname string) ConfigEntry
|
||||
// Browser returns the configured browser, optionally scoped by host.
|
||||
Browser(hostname string) ConfigEntry
|
||||
// ColorLabels returns the configured color_label setting, optionally scoped by host.
|
||||
|
|
@ -53,6 +55,8 @@ type Config interface {
|
|||
Prompt(hostname string) ConfigEntry
|
||||
// PreferEditorPrompt returns the configured editor-based prompt, optionally scoped by host.
|
||||
PreferEditorPrompt(hostname string) ConfigEntry
|
||||
// Spinner returns the configured spinner setting, optionally scoped by host.
|
||||
Spinner(hostname string) ConfigEntry
|
||||
|
||||
// Aliases provides persistent storage and modification of command aliases.
|
||||
Aliases() AliasConfig
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ var _ gh.Config = &ConfigMock{}
|
|||
// AccessibleColorsFunc: func(hostname string) gh.ConfigEntry {
|
||||
// panic("mock out the AccessibleColors method")
|
||||
// },
|
||||
// AccessiblePrompterFunc: func(hostname string) gh.ConfigEntry {
|
||||
// panic("mock out the AccessiblePrompter method")
|
||||
// },
|
||||
// AliasesFunc: func() gh.AliasConfig {
|
||||
// panic("mock out the Aliases method")
|
||||
// },
|
||||
|
|
@ -64,6 +67,9 @@ var _ gh.Config = &ConfigMock{}
|
|||
// SetFunc: func(hostname string, key string, value string) {
|
||||
// panic("mock out the Set method")
|
||||
// },
|
||||
// SpinnerFunc: func(hostname string) gh.ConfigEntry {
|
||||
// panic("mock out the Spinner method")
|
||||
// },
|
||||
// VersionFunc: func() o.Option[string] {
|
||||
// panic("mock out the Version method")
|
||||
// },
|
||||
|
|
@ -80,6 +86,9 @@ type ConfigMock struct {
|
|||
// AccessibleColorsFunc mocks the AccessibleColors method.
|
||||
AccessibleColorsFunc func(hostname string) gh.ConfigEntry
|
||||
|
||||
// AccessiblePrompterFunc mocks the AccessiblePrompter method.
|
||||
AccessiblePrompterFunc func(hostname string) gh.ConfigEntry
|
||||
|
||||
// AliasesFunc mocks the Aliases method.
|
||||
AliasesFunc func() gh.AliasConfig
|
||||
|
||||
|
|
@ -122,6 +131,9 @@ type ConfigMock struct {
|
|||
// SetFunc mocks the Set method.
|
||||
SetFunc func(hostname string, key string, value string)
|
||||
|
||||
// SpinnerFunc mocks the Spinner method.
|
||||
SpinnerFunc func(hostname string) gh.ConfigEntry
|
||||
|
||||
// VersionFunc mocks the Version method.
|
||||
VersionFunc func() o.Option[string]
|
||||
|
||||
|
|
@ -135,6 +147,11 @@ type ConfigMock struct {
|
|||
// Hostname is the hostname argument value.
|
||||
Hostname string
|
||||
}
|
||||
// AccessiblePrompter holds details about calls to the AccessiblePrompter method.
|
||||
AccessiblePrompter []struct {
|
||||
// Hostname is the hostname argument value.
|
||||
Hostname string
|
||||
}
|
||||
// Aliases holds details about calls to the Aliases method.
|
||||
Aliases []struct {
|
||||
}
|
||||
|
|
@ -205,6 +222,11 @@ type ConfigMock struct {
|
|||
// Value is the value argument value.
|
||||
Value string
|
||||
}
|
||||
// Spinner holds details about calls to the Spinner method.
|
||||
Spinner []struct {
|
||||
// Hostname is the hostname argument value.
|
||||
Hostname string
|
||||
}
|
||||
// Version holds details about calls to the Version method.
|
||||
Version []struct {
|
||||
}
|
||||
|
|
@ -213,6 +235,7 @@ type ConfigMock struct {
|
|||
}
|
||||
}
|
||||
lockAccessibleColors sync.RWMutex
|
||||
lockAccessiblePrompter sync.RWMutex
|
||||
lockAliases sync.RWMutex
|
||||
lockAuthentication sync.RWMutex
|
||||
lockBrowser sync.RWMutex
|
||||
|
|
@ -227,6 +250,7 @@ type ConfigMock struct {
|
|||
lockPreferEditorPrompt sync.RWMutex
|
||||
lockPrompt sync.RWMutex
|
||||
lockSet sync.RWMutex
|
||||
lockSpinner sync.RWMutex
|
||||
lockVersion sync.RWMutex
|
||||
lockWrite sync.RWMutex
|
||||
}
|
||||
|
|
@ -263,6 +287,38 @@ func (mock *ConfigMock) AccessibleColorsCalls() []struct {
|
|||
return calls
|
||||
}
|
||||
|
||||
// AccessiblePrompter calls AccessiblePrompterFunc.
|
||||
func (mock *ConfigMock) AccessiblePrompter(hostname string) gh.ConfigEntry {
|
||||
if mock.AccessiblePrompterFunc == nil {
|
||||
panic("ConfigMock.AccessiblePrompterFunc: method is nil but Config.AccessiblePrompter was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Hostname string
|
||||
}{
|
||||
Hostname: hostname,
|
||||
}
|
||||
mock.lockAccessiblePrompter.Lock()
|
||||
mock.calls.AccessiblePrompter = append(mock.calls.AccessiblePrompter, callInfo)
|
||||
mock.lockAccessiblePrompter.Unlock()
|
||||
return mock.AccessiblePrompterFunc(hostname)
|
||||
}
|
||||
|
||||
// AccessiblePrompterCalls gets all the calls that were made to AccessiblePrompter.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedConfig.AccessiblePrompterCalls())
|
||||
func (mock *ConfigMock) AccessiblePrompterCalls() []struct {
|
||||
Hostname string
|
||||
} {
|
||||
var calls []struct {
|
||||
Hostname string
|
||||
}
|
||||
mock.lockAccessiblePrompter.RLock()
|
||||
calls = mock.calls.AccessiblePrompter
|
||||
mock.lockAccessiblePrompter.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Aliases calls AliasesFunc.
|
||||
func (mock *ConfigMock) Aliases() gh.AliasConfig {
|
||||
if mock.AliasesFunc == nil {
|
||||
|
|
@ -708,6 +764,38 @@ func (mock *ConfigMock) SetCalls() []struct {
|
|||
return calls
|
||||
}
|
||||
|
||||
// Spinner calls SpinnerFunc.
|
||||
func (mock *ConfigMock) Spinner(hostname string) gh.ConfigEntry {
|
||||
if mock.SpinnerFunc == nil {
|
||||
panic("ConfigMock.SpinnerFunc: method is nil but Config.Spinner was just called")
|
||||
}
|
||||
callInfo := struct {
|
||||
Hostname string
|
||||
}{
|
||||
Hostname: hostname,
|
||||
}
|
||||
mock.lockSpinner.Lock()
|
||||
mock.calls.Spinner = append(mock.calls.Spinner, callInfo)
|
||||
mock.lockSpinner.Unlock()
|
||||
return mock.SpinnerFunc(hostname)
|
||||
}
|
||||
|
||||
// SpinnerCalls gets all the calls that were made to Spinner.
|
||||
// Check the length with:
|
||||
//
|
||||
// len(mockedConfig.SpinnerCalls())
|
||||
func (mock *ConfigMock) SpinnerCalls() []struct {
|
||||
Hostname string
|
||||
} {
|
||||
var calls []struct {
|
||||
Hostname string
|
||||
}
|
||||
mock.lockSpinner.RLock()
|
||||
calls = mock.calls.Spinner
|
||||
mock.lockSpinner.RUnlock()
|
||||
return calls
|
||||
}
|
||||
|
||||
// Version calls VersionFunc.
|
||||
func (mock *ConfigMock) Version() o.Option[string] {
|
||||
if mock.VersionFunc == nil {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/Netflix/go-expect"
|
||||
"github.com/cli/cli/v2/internal/prompter"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/creack/pty"
|
||||
"github.com/hinshun/vt10x"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -33,7 +34,7 @@ import (
|
|||
func TestAccessiblePrompter(t *testing.T) {
|
||||
t.Run("Select", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -52,7 +53,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("MultiSelect", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -77,7 +78,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("Input", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyText := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -97,7 +98,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("Input - blank input returns default value", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyDefaultValue := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -117,7 +118,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("Password", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyPassword := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -137,7 +138,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("Confirm", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -156,7 +157,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("Confirm - blank input returns default", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -175,7 +176,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("AuthToken", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyAuthToken := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -195,7 +196,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("AuthToken - blank input returns error", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
dummyAuthTokenForAfterFailure := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -223,7 +224,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("ConfirmDeletion", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
requiredValue := "test"
|
||||
go func() {
|
||||
|
|
@ -243,7 +244,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("ConfirmDeletion - bad input", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
requiredValue := "test"
|
||||
badInputValue := "garbage"
|
||||
|
||||
|
|
@ -272,7 +273,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("InputHostname", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
hostname := "example.com"
|
||||
|
||||
go func() {
|
||||
|
|
@ -292,7 +293,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("MarkdownEditor - blank allowed with blank input returns blank", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -311,7 +312,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("MarkdownEditor - blank disallowed with default value returns default value", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
defaultValue := "12345abcdefg"
|
||||
|
||||
go func() {
|
||||
|
|
@ -339,7 +340,7 @@ func TestAccessiblePrompter(t *testing.T) {
|
|||
|
||||
t.Run("MarkdownEditor - blank disallowed no default value returns error", func(t *testing.T) {
|
||||
console := newTestVirtualTerminal(t)
|
||||
p := newTestAcessiblePrompter(t, console)
|
||||
p := newTestAccessiblePrompter(t, console)
|
||||
|
||||
go func() {
|
||||
// Wait for prompt to appear
|
||||
|
|
@ -419,21 +420,40 @@ func newTestVirtualTerminal(t *testing.T) *expect.Console {
|
|||
return console
|
||||
}
|
||||
|
||||
func newTestAcessiblePrompter(t *testing.T, console *expect.Console) prompter.Prompter {
|
||||
func newTestVirtualTerminalIOStreams(t *testing.T, console *expect.Console) *iostreams.IOStreams {
|
||||
t.Helper()
|
||||
io := &iostreams.IOStreams{
|
||||
In: console.Tty(),
|
||||
Out: console.Tty(),
|
||||
ErrOut: console.Tty(),
|
||||
}
|
||||
io.SetStdinTTY(false)
|
||||
io.SetStdoutTTY(false)
|
||||
io.SetStderrTTY(false)
|
||||
return io
|
||||
}
|
||||
|
||||
// `echo` is chosen as the editor command because it immediately returns
|
||||
// a success exit code, returns an empty string, doesn't require any user input,
|
||||
// and since this file is only built on Linux, it is near guaranteed to be available.
|
||||
var editorCmd = "echo"
|
||||
|
||||
func newTestAccessiblePrompter(t *testing.T, console *expect.Console) prompter.Prompter {
|
||||
t.Helper()
|
||||
|
||||
t.Setenv("GH_ACCESSIBLE_PROMPTER", "true")
|
||||
// `echo`` is chose as the editor command because it immediately returns
|
||||
// a success exit code, returns an empty string, doesn't require any user input,
|
||||
// and since this file is only built on Linux, it is near guaranteed to be available.
|
||||
return prompter.New("echo", console.Tty(), console.Tty(), console.Tty())
|
||||
io := newTestVirtualTerminalIOStreams(t, console)
|
||||
io.SetAccessiblePrompterEnabled(true)
|
||||
|
||||
return prompter.New(editorCmd, io)
|
||||
}
|
||||
|
||||
func newTestSurveyPrompter(t *testing.T, console *expect.Console) prompter.Prompter {
|
||||
t.Helper()
|
||||
|
||||
t.Setenv("GH_ACCESSIBLE_PROMPTER", "false")
|
||||
return prompter.New("echo", console.Tty(), console.Tty(), console.Tty())
|
||||
io := newTestVirtualTerminalIOStreams(t, console)
|
||||
io.SetAccessiblePrompterEnabled(false)
|
||||
|
||||
return prompter.New(editorCmd, io)
|
||||
}
|
||||
|
||||
// failOnExpectError adds an observer that will fail the test in a standardised way
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ package prompter
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/cli/cli/v2/internal/ghinstance"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/cli/cli/v2/pkg/surveyext"
|
||||
ghPrompter "github.com/cli/go-gh/v2/pkg/prompter"
|
||||
)
|
||||
|
|
@ -43,24 +42,21 @@ type Prompter interface {
|
|||
MarkdownEditor(prompt string, defaultValue string, blankAllowed bool) (string, error)
|
||||
}
|
||||
|
||||
func New(editorCmd string, stdin ghPrompter.FileReader, stdout ghPrompter.FileWriter, stderr ghPrompter.FileWriter) Prompter {
|
||||
accessiblePrompterValue, accessiblePrompterIsSet := os.LookupEnv("GH_ACCESSIBLE_PROMPTER")
|
||||
falseyValues := []string{"false", "0", "no", ""}
|
||||
|
||||
if accessiblePrompterIsSet && !slices.Contains(falseyValues, accessiblePrompterValue) {
|
||||
func New(editorCmd string, io *iostreams.IOStreams) Prompter {
|
||||
if io.AccessiblePrompterEnabled() {
|
||||
return &accessiblePrompter{
|
||||
stdin: stdin,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
stdin: io.In,
|
||||
stdout: io.Out,
|
||||
stderr: io.ErrOut,
|
||||
editorCmd: editorCmd,
|
||||
}
|
||||
}
|
||||
|
||||
return &surveyPrompter{
|
||||
prompter: ghPrompter.New(stdin, stdout, stderr),
|
||||
stdin: stdin,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
prompter: ghPrompter.New(io.In, io.Out, io.ErrOut),
|
||||
stdin: io.In,
|
||||
stdout: io.Out,
|
||||
stderr: io.ErrOut,
|
||||
editorCmd: editorCmd,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ func Test_listRun(t *testing.T) {
|
|||
browser=brave
|
||||
color_labels=disabled
|
||||
accessible_colors=disabled
|
||||
accessible_prompter=disabled
|
||||
spinner=enabled
|
||||
`),
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ func newBrowser(f *cmdutil.Factory) browser.Browser {
|
|||
func newPrompter(f *cmdutil.Factory) prompter.Prompter {
|
||||
editor, _ := cmdutil.DetermineEditor(f.Config)
|
||||
io := f.IOStreams
|
||||
return prompter.New(editor, io.In, io.Out, io.ErrOut)
|
||||
return prompter.New(editor, io)
|
||||
}
|
||||
|
||||
func configFunc() func() (gh.Config, error) {
|
||||
|
|
@ -284,9 +284,23 @@ func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams {
|
|||
io.SetNeverPrompt(true)
|
||||
}
|
||||
|
||||
ghSpinnerDisabledValue, ghSpinnerDisabledIsSet := os.LookupEnv("GH_SPINNER_DISABLED")
|
||||
falseyValues := []string{"false", "0", "no", ""}
|
||||
if ghSpinnerDisabledIsSet && !slices.Contains(falseyValues, ghSpinnerDisabledValue) {
|
||||
|
||||
accessiblePrompterValue, accessiblePrompterIsSet := os.LookupEnv("GH_ACCESSIBLE_PROMPTER")
|
||||
if accessiblePrompterIsSet {
|
||||
if !slices.Contains(falseyValues, accessiblePrompterValue) {
|
||||
io.SetAccessiblePrompterEnabled(true)
|
||||
}
|
||||
} else if prompt := cfg.AccessiblePrompter(""); prompt.Value == "enabled" {
|
||||
io.SetAccessiblePrompterEnabled(true)
|
||||
}
|
||||
|
||||
ghSpinnerDisabledValue, ghSpinnerDisabledIsSet := os.LookupEnv("GH_SPINNER_DISABLED")
|
||||
if ghSpinnerDisabledIsSet {
|
||||
if !slices.Contains(falseyValues, ghSpinnerDisabledValue) {
|
||||
io.SetSpinnerDisabled(true)
|
||||
}
|
||||
} else if spinnerDisabled := cfg.Spinner(""); spinnerDisabled.Value == "disabled" {
|
||||
io.SetSpinnerDisabled(true)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -435,6 +435,7 @@ func Test_ioStreams_prompt(t *testing.T) {
|
|||
func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
spinnerDisabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
|
|
@ -442,6 +443,16 @@ func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
|||
name: "default config",
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with spinner disabled",
|
||||
config: disableSpinnersConfig(),
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config with spinner enabled",
|
||||
config: enableSpinnersConfig(),
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
{
|
||||
name: "spinner disabled via GH_SPINNER_DISABLED env var = 0",
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "0"},
|
||||
|
|
@ -467,6 +478,18 @@ func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
|||
env: map[string]string{"GH_SPINNER_DISABLED": "true"},
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config enabled but env disabled, respects env",
|
||||
config: enableSpinnersConfig(),
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "true"},
|
||||
spinnerDisabled: true,
|
||||
},
|
||||
{
|
||||
name: "config disabled but env enabled, respects env",
|
||||
config: disableSpinnersConfig(),
|
||||
env: map[string]string{"GH_SPINNER_DISABLED": "false"},
|
||||
spinnerDisabled: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
@ -474,12 +497,87 @@ func Test_ioStreams_spinnerDisabled(t *testing.T) {
|
|||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.spinnerDisabled, io.GetSpinnerDisabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_accessiblePrompterEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config gh.Config
|
||||
accessiblePrompterEnabled bool
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "default config",
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config with accessible prompter enabled",
|
||||
config: enableAccessiblePrompterConfig(),
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config with accessible prompter disabled",
|
||||
config: disableAccessiblePrompterConfig(),
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter enabled via GH_ACCESSIBLE_PROMPTER env var = 1",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "1"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter enabled via GH_ACCESSIBLE_PROMPTER env var = true",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "true"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "accessible prompter disabled via GH_ACCESSIBLE_PROMPTER env var = 0",
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "0"},
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config disabled but env enabled, respects env",
|
||||
config: disableAccessiblePrompterConfig(),
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "true"},
|
||||
accessiblePrompterEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config enabled but env disabled, respects env",
|
||||
config: enableAccessiblePrompterConfig(),
|
||||
env: map[string]string{"GH_ACCESSIBLE_PROMPTER": "false"},
|
||||
accessiblePrompterEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
for k, v := range tt.env {
|
||||
t.Setenv(k, v)
|
||||
}
|
||||
f := New("1")
|
||||
f.Config = func() (gh.Config, error) {
|
||||
if tt.config == nil {
|
||||
return config.NewBlankConfig(), nil
|
||||
} else {
|
||||
return tt.config, nil
|
||||
}
|
||||
}
|
||||
io := ioStreams(f)
|
||||
assert.Equal(t, tt.accessiblePrompterEnabled, io.AccessiblePrompterEnabled())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ioStreams_colorLabels(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
@ -664,6 +762,22 @@ func disablePromptConfig() gh.Config {
|
|||
return config.NewFromString("prompt: disabled")
|
||||
}
|
||||
|
||||
func enableAccessiblePrompterConfig() gh.Config {
|
||||
return config.NewFromString("accessible_prompter: enabled")
|
||||
}
|
||||
|
||||
func disableAccessiblePrompterConfig() gh.Config {
|
||||
return config.NewFromString("accessible_prompter: disabled")
|
||||
}
|
||||
|
||||
func disableSpinnersConfig() gh.Config {
|
||||
return config.NewFromString("spinner: disabled")
|
||||
}
|
||||
|
||||
func enableSpinnersConfig() gh.Config {
|
||||
return config.NewFromString("spinner: enabled")
|
||||
}
|
||||
|
||||
func disableColorLabelsConfig() gh.Config {
|
||||
return config.NewFromString("color_labels: disabled")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ func NewClient(httpClient *http.Client, hostname string, ios *iostreams.IOStream
|
|||
return &Client{
|
||||
apiClient: apiClient,
|
||||
io: ios,
|
||||
prompter: prompter.New("", ios.In, ios.Out, ios.ErrOut),
|
||||
prompter: prompter.New("", ios),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ type IOStreams struct {
|
|||
progressIndicatorEnabled bool
|
||||
progressIndicator *spinner.Spinner
|
||||
progressIndicatorMu sync.Mutex
|
||||
spinnerDisabled bool
|
||||
|
||||
alternateScreenBufferEnabled bool
|
||||
alternateScreenBufferActive bool
|
||||
|
|
@ -78,8 +79,8 @@ type IOStreams struct {
|
|||
pagerCommand string
|
||||
pagerProcess *os.Process
|
||||
|
||||
neverPrompt bool
|
||||
spinnerDisabled bool
|
||||
neverPrompt bool
|
||||
accessiblePrompterEnabled bool
|
||||
|
||||
TempFileOverride *os.File
|
||||
}
|
||||
|
|
@ -457,6 +458,14 @@ func (s *IOStreams) AccessibleColorsEnabled() bool {
|
|||
return s.accessibleColorsEnabled
|
||||
}
|
||||
|
||||
func (s *IOStreams) SetAccessiblePrompterEnabled(enabled bool) {
|
||||
s.accessiblePrompterEnabled = enabled
|
||||
}
|
||||
|
||||
func (s *IOStreams) AccessiblePrompterEnabled() bool {
|
||||
return s.accessiblePrompterEnabled
|
||||
}
|
||||
|
||||
func System() *IOStreams {
|
||||
terminal := ghTerm.FromEnv()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue