fix(app): pretty-print JSON on a terminal and reconcile the foundation streams

writeJSON uses the indented, colored JSON writer when stdout is a
terminal. Help and JSON tests now expect the -L shorthand column and
compare terminal JSON without color sequences.

Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
bircni committed 2026-10-11 22:57:38 +02:00
1 parent 04d5f2aed0
commit 48a188ceac
4 files changed
+17 -8

No files matched your search

+2 -2
View File
@@ -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))
+8
View File
@@ -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, "")
}
+2 -1
View File
@@ -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.
+5 -5
View File
@@ -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)