diff --git a/internal/app/app_test.go b/internal/app/app_test.go index d34cbab6..da0d49c3 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -250,7 +250,7 @@ func TestCommandHelpShowsRelevantFlagsOnly(t *testing.T) { for _, want := range []string{ "\nUSAGE\n tea issue list [flags]\n", "\nALIASES\n tea issue ls\n", - "\nFLAGS\n --assignee string ", + "\nFLAGS\n --assignee string ", "\nINHERITED FLAGS\n -R, --repo OWNER/REPO ", "\n --json fields ", "\n -q, --jq expression ", @@ -264,7 +264,7 @@ func TestCommandHelpShowsRelevantFlagsOnly(t *testing.T) { } out.Reset() require.NoError(t, Execute(context.Background(), deps, []string{"label", "list", "--help"})) - require.Contains(t, out.String(), "\nFLAGS\n --limit int Maximum results (1..100) (default 30)\n --org string ") + require.Contains(t, out.String(), "\nFLAGS\n -L, --limit int Maximum number of results (1..1000) (default 30)\n --org string ") for _, args := range [][]string{{"version", "--help"}, {"alias", "list", "--help"}, {"auth", "status", "--help"}} { out.Reset() require.NoError(t, Execute(context.Background(), deps, args)) diff --git a/internal/app/helpers_test.go b/internal/app/helpers_test.go index d609e21f..d026bdd5 100644 --- a/internal/app/helpers_test.go +++ b/internal/app/helpers_test.go @@ -15,6 +15,7 @@ import ( "net/http/httptest" "os" "path/filepath" + "regexp" "strings" "testing" @@ -291,3 +292,10 @@ func selectablesHandler(t *testing.T, push bool, next http.HandlerFunc) http.Han } } } + +var ansiSequence = regexp.MustCompile("\x1b\\[[0-9;]*m") + +// withoutANSI drops SGR color sequences, such as those in pretty JSON on a terminal. +func withoutANSI(s string) string { + return ansiSequence.ReplaceAllString(s, "") +} diff --git a/internal/app/human_output.go b/internal/app/human_output.go index 92071082..17debb5c 100644 --- a/internal/app/human_output.go +++ b/internal/app/human_output.go @@ -55,7 +55,8 @@ func (a *application) writeHuman(cmd *cobra.Command, text string) error { // writeJSON writes v as JSON, filtered by --json fields and --jq. func (a *application) writeJSON(cmd *cobra.Command, v any) error { - return terminal.WriteJSON(cmd.OutOrStdout(), v, a.options.json, a.options.jq) + pretty := a.deps.Streams.OutTTY + return terminal.JSONOutput{Fields: a.options.json, Query: a.options.jq, Pretty: pretty, Color: pretty && a.colorEnabled()}.Write(cmd.OutOrStdout(), v) } // listSpec describes how a list command shows its items to people. diff --git a/internal/app/human_output_test.go b/internal/app/human_output_test.go index 360b7cc4..79b5bbbc 100644 --- a/internal/app/human_output_test.go +++ b/internal/app/human_output_test.go @@ -93,7 +93,7 @@ func TestIssueViewJSONDoesNotPage(t *testing.T) { deps, out, _, pager := pagedDeps(t, server.URL, map[string]string{"PAGER": "fake-pager"}) require.NoError(t, Execute(context.Background(), deps, []string{"issue", "view", "7", "-R", "alice/project", "--json=number,title", "--no-input"})) require.Zero(t, pager.calls) - require.JSONEq(t, `{"number":7,"title":"Title"}`, out.String()) + require.JSONEq(t, `{"number":7,"title":"Title"}`, withoutANSI(out.String())) } func TestConfiguredPagerOverridesPagerEnvironment(t *testing.T) { @@ -121,10 +121,10 @@ func TestTeaPagerAndColorEnvironmentOverrides(t *testing.T) { require.Contains(t, pager.input, "\x1b[38;5;196mbug\x1b[0m") out.Reset() require.NoError(t, Execute(context.Background(), deps, []string{"config", "get", "pager", "--show-source", "--json"})) - require.JSONEq(t, `{"pager":{"value":"tea-pager","source":"TEA_PAGER"}}`, out.String()) + require.JSONEq(t, `{"pager":{"value":"tea-pager","source":"TEA_PAGER"}}`, withoutANSI(out.String())) out.Reset() require.NoError(t, Execute(context.Background(), deps, []string{"config", "get", "color", "--show-source", "--json"})) - require.JSONEq(t, `{"color":{"value":"always","source":"TEA_COLOR"}}`, out.String()) + require.JSONEq(t, `{"color":{"value":"always","source":"TEA_COLOR"}}`, withoutANSI(out.String())) } func TestColorAutoUsesStdoutTTY(t *testing.T) { @@ -308,7 +308,7 @@ func TestWriteTable(t *testing.T) { }, {name: "empty message on a terminal", tty: true, page: listPage{Number: 1}, stderr: "no open issues in alice/x\n"}, {name: "nothing when empty and piped", page: listPage{Number: 1}}, - {name: "JSON", tty: true, json: "number", items: items[:1], page: listPage{Number: 1, More: true}, out: `[{"number":12}]` + "\n"}, + {name: "JSON", tty: true, json: "number", items: items[:1], page: listPage{Number: 1, More: true}, out: "[\n {\n \"number\": 12\n }\n]\n"}, } { t.Run(tc.name, func(t *testing.T) { deps, out, diagnostics := testDeps(t) @@ -336,7 +336,7 @@ func TestWriteView(t *testing.T) { }{ {name: "terminal", tty: true, want: "Login fails #1\nOpen • alice opened about 2 hours ago\n\nSteps\n\nView this issue on Gitea: https://example.org/1\n"}, {name: "piped", want: "title:\tLogin fails\nurl:\thttps://example.org/1\n--\nSteps\n"}, - {name: "JSON", tty: true, json: "title", want: `{"title":"Login fails"}` + "\n"}, + {name: "JSON", tty: true, json: "title", want: "{\n \"title\": \"Login fails\"\n}\n"}, } { t.Run(tc.name, func(t *testing.T) { deps, out, _ := testDeps(t)