mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
refactor: add terminal output and forms package (#1124)
- `internal/terminal`: streams and the prompting policy, interactive forms (issue, PR, review, merge, milestone), editor / pager / browser launching, JSON and `--jq` output. - An aligned table renderer and a record renderer for human output; both strip control and escape characters from server-supplied text so titles and bodies cannot inject terminal sequences. Reviewed-on: https://gitea.com/gitea/tea/pulls/1124 Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
1 parent
88b91faa54
commit
faea86cd94
27 files changed
+1941
-1
No files matched your search
@@ -0,0 +1,63 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testPrompter returns a form prompter reading in and the buffer its forms render to.
|
||||
func testPrompter(in io.Reader) (*FormPrompter, *bytes.Buffer) {
|
||||
rendered := &bytes.Buffer{}
|
||||
return &FormPrompter{Streams: Streams{In: in, Out: &bytes.Buffer{}, ErrOut: rendered, InTTY: true, ErrTTY: true}}, rendered
|
||||
}
|
||||
|
||||
// accessiblePrompter is testPrompter in accessible mode, which renders field titles as plain text.
|
||||
func accessiblePrompter(t *testing.T, input string) (*FormPrompter, *bytes.Buffer) {
|
||||
t.Helper()
|
||||
t.Setenv("TERM", "dumb")
|
||||
return testPrompter(strings.NewReader(input))
|
||||
}
|
||||
|
||||
func noEdit(context.Context, string) (Draft, error) { return Draft{}, nil }
|
||||
|
||||
type fakePrompt struct{ inputs int }
|
||||
|
||||
func (f *fakePrompt) Input(context.Context, string) (string, error) {
|
||||
f.inputs++
|
||||
return "provided", nil
|
||||
}
|
||||
func (f *fakePrompt) SecretInput(context.Context, string) (string, error) { return "", nil }
|
||||
func (f *fakePrompt) Confirm(context.Context, string) (bool, error) { return false, nil }
|
||||
|
||||
func TestPromptPolicy(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
policy Policy
|
||||
streams Streams
|
||||
expected bool
|
||||
}{
|
||||
{Policy{Enabled: true}, Streams{InTTY: true, ErrTTY: true}, true},
|
||||
{Policy{Enabled: true}, Streams{InTTY: true, OutTTY: true}, false},
|
||||
{Policy{Enabled: true, NoInput: true}, Streams{InTTY: true, ErrTTY: true}, false},
|
||||
{Policy{Enabled: true, Machine: true}, Streams{InTTY: true, ErrTTY: true}, false},
|
||||
{Policy{Enabled: true, Machine: true, Interactive: true}, Streams{InTTY: true, ErrTTY: true}, true},
|
||||
} {
|
||||
require.Equal(t, tc.expected, tc.policy.CanPrompt(tc.streams))
|
||||
}
|
||||
prompt := &fakePrompt{}
|
||||
value := "supplied"
|
||||
asked, err := Require(context.Background(), &value, "title", Streams{}, Policy{}, prompt)
|
||||
require.NoError(t, err)
|
||||
require.False(t, asked)
|
||||
require.Zero(t, prompt.inputs)
|
||||
value = ""
|
||||
_, err = Require(context.Background(), &value, "title", Streams{}, Policy{}, prompt)
|
||||
require.ErrorContains(t, err, "--title")
|
||||
}
|
||||
Reference in new issue
Block a user