mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
test: merge duplicate terminal, git and Actions service tests
Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
6 files changed
+38
-136
No files matched your search
@@ -145,6 +145,10 @@ func TestSyncBranchRefusesWhatItCannotFastForward(t *testing.T) {
|
|||||||
f.git(f.work, "switch", "experiment")
|
f.git(f.work, "switch", "experiment")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "checked out in another worktree", message: "main is checked out in the worktree",
|
||||||
|
prepare: func(f syncFixture) { f.git(f.work, "worktree", "add", filepath.Join(t.TempDir(), "linked"), "main") },
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
f := newSyncFixture(t)
|
f := newSyncFixture(t)
|
||||||
@@ -158,24 +162,6 @@ func TestSyncBranchRefusesWhatItCannotFastForward(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSyncBranchRefusesABranchCheckedOutInAnotherWorktree(t *testing.T) {
|
|
||||||
f := newSyncFixture(t)
|
|
||||||
linked := filepath.Join(t.TempDir(), "linked")
|
|
||||||
f.git(f.work, "worktree", "add", linked, "main")
|
|
||||||
before := f.git(f.work, "rev-parse", "main")
|
|
||||||
_, err := f.sync("main")
|
|
||||||
require.ErrorContains(t, err, "main is checked out in the worktree")
|
|
||||||
require.Equal(t, before, f.git(f.work, "rev-parse", "main"))
|
|
||||||
require.Equal(t, "experiment", f.git(f.work, "branch", "--show-current"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWorktreeWithBranch(t *testing.T) {
|
|
||||||
porcelain := "worktree /src\nHEAD abc\nbranch refs/heads/experiment\n\nworktree /linked\nHEAD def\nbranch refs/heads/main\n\nworktree /detached\nHEAD 123\ndetached\n"
|
|
||||||
require.Equal(t, "/linked", worktreeWithBranch(porcelain, "refs/heads/main"))
|
|
||||||
require.Equal(t, "/src", worktreeWithBranch(porcelain, "refs/heads/experiment"))
|
|
||||||
require.Empty(t, worktreeWithBranch(porcelain, "refs/heads/other"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSyncBranchValidatesItsInput(t *testing.T) {
|
func TestSyncBranchValidatesItsInput(t *testing.T) {
|
||||||
f := newSyncFixture(t)
|
f := newSyncFixture(t)
|
||||||
_, err := SyncBranch(context.Background(), f.runner, SyncOptions{Directory: f.work, Remote: "origin"})
|
_, err := SyncBranch(context.Background(), f.runner, SyncOptions{Directory: f.work, Remote: "origin"})
|
||||||
|
|||||||
@@ -111,31 +111,6 @@ func TestWatchReportsEveryPollWithJobsUntilTheRunCompletes(t *testing.T) {
|
|||||||
require.True(t, errors.Is(err, context.Canceled))
|
require.True(t, errors.Is(err, context.Canceled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunCancellationPreservesHistory(t *testing.T) {
|
|
||||||
for _, force := range []bool{false, true} {
|
|
||||||
t.Run(fmt.Sprint(force), func(t *testing.T) {
|
|
||||||
mutations := 0
|
|
||||||
action := "cancel"
|
|
||||||
if force {
|
|
||||||
action = "force-cancel"
|
|
||||||
}
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
mutations++
|
|
||||||
require.Equal(t, http.MethodPost, r.Method)
|
|
||||||
require.Equal(t, "/api/v1/repos/owner/repo/actions/runs/7/"+action, r.URL.Path)
|
|
||||||
_, _ = w.Write([]byte(`{"id":7,"status":"completed","conclusion":"cancelled"}`)) //nolint:misspell // Gitea API spelling.
|
|
||||||
}))
|
|
||||||
defer server.Close()
|
|
||||||
service := Actions{Client: transport.New(server.URL, "", false), Repository: "owner/repo"}
|
|
||||||
result, err := service.CancelRun(context.Background(), 7, force)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.EqualValues(t, 7, result.ID)
|
|
||||||
require.Equal(t, "cancelled", result.Conclusion) //nolint:misspell // Gitea API spelling.
|
|
||||||
require.Equal(t, 1, mutations)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestActionMutationsUseTheirEndpoints(t *testing.T) {
|
func TestActionMutationsUseTheirEndpoints(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -158,6 +133,15 @@ func TestActionMutationsUseTheirEndpoints(t *testing.T) {
|
|||||||
{"delete", "/api/v1/repos/owner/repo/actions/runs/7", http.MethodDelete, func(s Actions) error {
|
{"delete", "/api/v1/repos/owner/repo/actions/runs/7", http.MethodDelete, func(s Actions) error {
|
||||||
return s.DeleteRun(context.Background(), 7)
|
return s.DeleteRun(context.Background(), 7)
|
||||||
}},
|
}},
|
||||||
|
{"cancel", "/api/v1/repos/owner/repo/actions/runs/7/cancel", http.MethodPost, func(s Actions) error {
|
||||||
|
run, err := s.CancelRun(context.Background(), 7, false)
|
||||||
|
require.EqualValues(t, 7, run.ID)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
|
{"force-cancel", "/api/v1/repos/owner/repo/actions/runs/7/force-cancel", http.MethodPost, func(s Actions) error {
|
||||||
|
_, err := s.CancelRun(context.Background(), 7, true)
|
||||||
|
return err
|
||||||
|
}},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
mutations := 0
|
mutations := 0
|
||||||
@@ -165,8 +149,7 @@ func TestActionMutationsUseTheirEndpoints(t *testing.T) {
|
|||||||
mutations++
|
mutations++
|
||||||
require.Equal(t, tc.method, r.Method)
|
require.Equal(t, tc.method, r.Method)
|
||||||
require.Equal(t, tc.path, r.URL.Path)
|
require.Equal(t, tc.path, r.URL.Path)
|
||||||
if tc.name == "rerun" {
|
if tc.method == http.MethodPost {
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
_, _ = w.Write([]byte(`{"id":7,"status":"waiting"}`))
|
_, _ = w.Write([]byte(`{"id":7,"status":"waiting"}`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,12 +57,3 @@ func TestFormsRefuseTerminalsWithoutSize(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "Title", values.Title)
|
require.Equal(t, "Title", values.Title)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPullFormNamesNoIssueFields(t *testing.T) {
|
|
||||||
p, rendered := accessiblePrompter(t, "n\nmain\nalice\nfeature\ny\nTitle\nBody\n")
|
|
||||||
_, err := p.CreatePull(context.Background(), PullFormChoices{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Contains(t, rendered.String(), "Title (required):")
|
|
||||||
require.Contains(t, rendered.String(), BodyTitle)
|
|
||||||
require.NotContains(t, rendered.String(), "Issue")
|
|
||||||
}
|
|
||||||
@@ -6,11 +6,9 @@ package terminal
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -31,55 +29,28 @@ func TestCollectIssuePropertiesUsesEditBodyDraft(t *testing.T) {
|
|||||||
require.NotEmpty(t, values.Draft.Path)
|
require.NotEmpty(t, values.Draft.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lineReader returns one line per read, so that each accessible prompt reads only its answer.
|
|
||||||
type lineReader struct{ text string }
|
|
||||||
|
|
||||||
func (r *lineReader) Read(p []byte) (int, error) {
|
|
||||||
if r.text == "" {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
line, _, _ := strings.Cut(r.text, "\n")
|
|
||||||
n := copy(p, line+"\n")
|
|
||||||
r.text = r.text[min(n, len(r.text)):]
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIssueFormAsksWhatsNextBeforeMetadata(t *testing.T) {
|
func TestIssueFormAsksWhatsNextBeforeMetadata(t *testing.T) {
|
||||||
choices := IssueFormChoices{AskNext: true, CanPush: true, Assignees: []string{"alice"}, EditBody: noEdit}
|
choices := IssueFormChoices{AskNext: true, CanPush: true, Assignees: []string{"alice"}, EditBody: noEdit}
|
||||||
for _, test := range []struct {
|
for _, tc := range []struct {
|
||||||
name, input string
|
name, input string
|
||||||
canceled bool
|
want IssueFormValues
|
||||||
|
err error
|
||||||
}{
|
}{
|
||||||
{"submit", "Title\n1\n", false},
|
{name: "submit", input: "Title\n1\n", want: IssueFormValues{Title: "Title"}},
|
||||||
{"cancel", "Title\n3\n", true},
|
{name: "cancel", input: "Title\n3\n", err: ErrCanceled},
|
||||||
|
{name: "metadata", input: "Title\n2\n1\n0\n2028-01-15\n", want: IssueFormValues{Title: "Title", Assignees: []string{"alice"}, Deadline: mustDueDate(t, "2028-01-15")}},
|
||||||
} {
|
} {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
t.Setenv("TERM", "dumb")
|
p, rendered := linePrompter(t, tc.input)
|
||||||
p, rendered := testPrompter(&lineReader{text: test.input})
|
|
||||||
values, err := p.collectIssueProperties(context.Background(), "", "", choices)
|
values, err := p.collectIssueProperties(context.Background(), "", "", choices)
|
||||||
|
require.ErrorIs(t, err, tc.err)
|
||||||
require.Contains(t, rendered.String(), "What's next?")
|
require.Contains(t, rendered.String(), "What's next?")
|
||||||
require.Contains(t, rendered.String(), "Add metadata")
|
require.Equal(t, tc.name == "metadata", strings.Contains(rendered.String(), "Assignees:"))
|
||||||
require.NotContains(t, rendered.String(), "Assignees:")
|
if tc.err == nil {
|
||||||
if test.canceled {
|
require.Equal(t, tc.want, values)
|
||||||
require.ErrorIs(t, err, ErrCanceled)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "Title", values.Title)
|
|
||||||
require.Empty(t, values.Assignees)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
t.Setenv("TERM", "dumb")
|
|
||||||
p, rendered := testPrompter(&lineReader{text: "Title\n2\n1\n0\n2028-01-15\n"})
|
|
||||||
values, err := p.collectIssueProperties(context.Background(), "", "", choices)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Contains(t, rendered.String(), "Assignees:")
|
|
||||||
require.Equal(t, []string{"alice"}, values.Assignees)
|
|
||||||
require.Equal(t, "2028-01-15", values.Deadline.Format(time.DateOnly))
|
|
||||||
p, rendered = accessiblePrompter(t, "Title\n")
|
|
||||||
_, err = p.collectIssueProperties(context.Background(), "", "", IssueFormChoices{EditBody: noEdit})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotContains(t, rendered.String(), "What's next?")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssueFormMarksTitleRequired(t *testing.T) {
|
func TestIssueFormMarksTitleRequired(t *testing.T) {
|
||||||
@@ -87,4 +58,5 @@ func TestIssueFormMarksTitleRequired(t *testing.T) {
|
|||||||
_, err := p.collectIssueProperties(context.Background(), "", "", IssueFormChoices{EditBody: noEdit})
|
_, err := p.collectIssueProperties(context.Background(), "", "", IssueFormChoices{EditBody: noEdit})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Contains(t, rendered.String(), "Title (required):")
|
require.Contains(t, rendered.String(), "Title (required):")
|
||||||
|
require.NotContains(t, rendered.String(), "What's next?")
|
||||||
}
|
}
|
||||||
@@ -45,15 +45,6 @@ func TestPromptPolicy(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
require.Equal(t, tc.expected, tc.policy.CanPrompt(tc.streams))
|
require.Equal(t, tc.expected, tc.policy.CanPrompt(tc.streams))
|
||||||
}
|
}
|
||||||
prompt := &askingPrompt{}
|
|
||||||
value := "supplied"
|
|
||||||
asked, err := RequireInput(context.Background(), &value, "title", "title", nil, Streams{}, Policy{}, prompt)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, asked)
|
|
||||||
require.Empty(t, prompt.titles)
|
|
||||||
value = ""
|
|
||||||
_, err = RequireInput(context.Background(), &value, "title", "title", nil, Streams{}, Policy{}, prompt)
|
|
||||||
require.ErrorContains(t, err, "--title")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// askingPrompt answers Asker prompts from scripted answers. Ask skips answers that validate rejects,
|
// askingPrompt answers Asker prompts from scripted answers. Ask skips answers that validate rejects,
|
||||||
@@ -99,17 +90,19 @@ func TestConfirmAction(t *testing.T) {
|
|||||||
c Confirmation
|
c Confirmation
|
||||||
yes bool
|
yes bool
|
||||||
streams Streams
|
streams Streams
|
||||||
prompt Prompter
|
prompt *askingPrompt
|
||||||
code int
|
code int
|
||||||
message string
|
message string
|
||||||
rejected []string
|
rejected []string
|
||||||
|
defaults []bool
|
||||||
}{
|
}{
|
||||||
{name: "accepted by flag", c: deleteLabel, yes: true, prompt: &askingPrompt{}},
|
{name: "accepted by flag", c: deleteLabel, yes: true, prompt: &askingPrompt{}},
|
||||||
{name: "names the action without a terminal", c: deleteLabel, prompt: &askingPrompt{}, code: clierr.Usage, message: `confirmation required to delete label "bug" from alice/x; rerun with --yes`},
|
{name: "names the action without a terminal", c: deleteLabel, prompt: &askingPrompt{}, code: clierr.Usage, message: `confirmation required to delete label "bug" from alice/x; rerun with --yes`},
|
||||||
{name: "names the prompted action without a terminal", c: Confirmation{Prompt: "Delete alice/x? It becomes read-only."}, prompt: &askingPrompt{}, code: clierr.Usage, message: "confirmation required to delete alice/x; rerun with --yes"},
|
{name: "names the prompted action without a terminal", c: Confirmation{Prompt: "Delete alice/x? It becomes read-only."}, prompt: &askingPrompt{}, code: clierr.Usage, message: "confirmation required to delete alice/x; rerun with --yes"},
|
||||||
{name: "without a terminal or action", c: Confirmation{Prompt: "Go?", DefaultYes: true}, prompt: &askingPrompt{}, code: clierr.Usage, message: "confirmation required; rerun with --yes"},
|
{name: "without a terminal or action", c: Confirmation{Prompt: "Go?", DefaultYes: true}, prompt: &askingPrompt{}, code: clierr.Usage, message: "confirmation required; rerun with --yes"},
|
||||||
{name: "accepted", c: deleteLabel, streams: terminal, prompt: &askingPrompt{accept: true}},
|
{name: "accepted", c: deleteLabel, streams: terminal, prompt: &askingPrompt{accept: true}, defaults: []bool{false}},
|
||||||
{name: "declined", c: deleteLabel, streams: terminal, prompt: &askingPrompt{}, code: clierr.Canceled, message: "canceled"},
|
{name: "passes the default", c: Confirmation{Prompt: "Create pull request?", DefaultYes: true}, streams: terminal, prompt: &askingPrompt{accept: true}, defaults: []bool{true}},
|
||||||
|
{name: "declined", c: deleteLabel, streams: terminal, prompt: &askingPrompt{}, code: clierr.Canceled, message: "canceled", defaults: []bool{false}},
|
||||||
{name: "re-prompts until the name is typed", c: typed, streams: terminal, prompt: &askingPrompt{answers: []string{"alice/y", " Alice/X "}}, rejected: []string{"alice/y"}},
|
{name: "re-prompts until the name is typed", c: typed, streams: terminal, prompt: &askingPrompt{answers: []string{"alice/y", " Alice/X "}}, rejected: []string{"alice/y"}},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
@@ -121,22 +114,12 @@ func TestConfirmAction(t *testing.T) {
|
|||||||
if tc.code == clierr.Canceled {
|
if tc.code == clierr.Canceled {
|
||||||
require.ErrorIs(t, err, ErrCanceled)
|
require.ErrorIs(t, err, ErrCanceled)
|
||||||
}
|
}
|
||||||
if asker, ok := tc.prompt.(*askingPrompt); ok {
|
require.Equal(t, tc.rejected, tc.prompt.rejected)
|
||||||
require.Equal(t, tc.rejected, asker.rejected)
|
require.Equal(t, tc.defaults, tc.prompt.defaults)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfirmActionPassesTheDefault(t *testing.T) {
|
|
||||||
prompt := &askingPrompt{accept: true}
|
|
||||||
streams := Streams{InTTY: true, ErrTTY: true}
|
|
||||||
require.NoError(t, ConfirmAction(context.Background(), Confirmation{Prompt: "Create pull request?", DefaultYes: true}, false, streams, Policy{Enabled: true}, prompt))
|
|
||||||
require.NoError(t, ConfirmAction(context.Background(), Confirmation{Prompt: "Delete branch?"}, false, streams, Policy{Enabled: true}, prompt))
|
|
||||||
require.Equal(t, []bool{true, false}, prompt.defaults)
|
|
||||||
require.Equal(t, []string{"Create pull request?", "Delete branch?"}, prompt.titles)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequireInput(t *testing.T) {
|
func TestRequireInput(t *testing.T) {
|
||||||
streams, policy := Streams{InTTY: true, ErrTTY: true}, Policy{Enabled: true}
|
streams, policy := Streams{InTTY: true, ErrTTY: true}, Policy{Enabled: true}
|
||||||
hex := func(s string) error {
|
hex := func(s string) error {
|
||||||
@@ -155,6 +138,9 @@ func TestRequireInput(t *testing.T) {
|
|||||||
require.Equal(t, []string{"", "red"}, asker.rejected)
|
require.Equal(t, []string{"", "red"}, asker.rejected)
|
||||||
require.Equal(t, []string{"Color (hex)"}, asker.titles)
|
require.Equal(t, []string{"Color (hex)"}, asker.titles)
|
||||||
|
|
||||||
|
asked, err = RequireInput(context.Background(), &value, "color", "Color (hex)", hex, Streams{}, policy, asker)
|
||||||
|
require.NoError(t, err, "a supplied value needs no prompt")
|
||||||
|
require.False(t, asked)
|
||||||
value = ""
|
value = ""
|
||||||
_, err = RequireInput(context.Background(), &value, "color", "Color (hex)", hex, Streams{}, policy, asker)
|
_, err = RequireInput(context.Background(), &value, "color", "Color (hex)", hex, Streams{}, policy, asker)
|
||||||
require.EqualError(t, err, "missing required input: supply --color")
|
require.EqualError(t, err, "missing required input: supply --color")
|
||||||
|
|||||||
@@ -4,11 +4,9 @@
|
|||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-runewidth"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,8 +27,7 @@ func TestTable(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"empty", issues, nil, tty, ""},
|
{"empty", issues, nil, tty, ""},
|
||||||
{"aligns legacy rows without header", nil, [][]Cell{row("7", "Fix login", "open"), row("12", "Crash", "closed")}, tty, "7 Fix login open\n12 Crash closed\n"},
|
{"aligns legacy rows without header", nil, [][]Cell{row("7", "Fix login", "open"), row("12", "Crash", "closed")}, tty, "7 Fix login open\n12 Crash closed\n"},
|
||||||
{"counts runes, not bytes", nil, [][]Cell{row("1", "äöü", "x"), row("2", "abcd", "y")}, tty, "1 äöü x\n2 abcd y\n"},
|
{"aligns by display width", nil, [][]Cell{row("1", "äöü", "x"), row("2", "修复", "y"), row("3", "abcd", "z")}, tty, "1 äöü x\n2 修复 y\n3 abcd z\n"},
|
||||||
{"counts wide runes twice", nil, [][]Cell{row("1", "修复", "x"), row("2", "abcd", "y")}, tty, "1 修复 x\n2 abcd y\n"},
|
|
||||||
{"trims empty trailing cells", nil, [][]Cell{row("1", "bug", ""), row("22", "feature", "new work")}, tty, "1 bug\n22 feature new work\n"},
|
{"trims empty trailing cells", nil, [][]Cell{row("1", "bug", ""), row("22", "feature", "new work")}, tty, "1 bug\n22 feature new work\n"},
|
||||||
{"pads ragged rows", nil, [][]Cell{row("1"), row("2", "two")}, tty, "1\n2 two\n"},
|
{"pads ragged rows", nil, [][]Cell{row("1"), row("2", "two")}, tty, "1\n2 two\n"},
|
||||||
{
|
{
|
||||||
@@ -122,19 +119,6 @@ func TestTable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableFitsWidthWithWideRunes(t *testing.T) {
|
|
||||||
titles := []string{"Emoji in title 🚀 works?", "支持中文路径 (support CJK paths)", strings.Repeat("long ", 40)}
|
|
||||||
rows := make([][]Cell, len(titles))
|
|
||||||
for i, title := range titles {
|
|
||||||
rows[i] = row("#1", title, "alice")
|
|
||||||
}
|
|
||||||
for _, width := range []int{30, 50, 80} {
|
|
||||||
for _, line := range strings.Split(strings.TrimSuffix(Table([]Column{{}, {Flex: true}, {}}, rows, Display{TTY: true, Width: width}), "\n"), "\n") {
|
|
||||||
require.LessOrEqual(t, runewidth.StringWidth(line), width, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSafeText(t *testing.T) {
|
func TestSafeText(t *testing.T) {
|
||||||
for _, tc := range []struct{ name, in, text, line string }{
|
for _, tc := range []struct{ name, in, text, line string }{
|
||||||
{"plain", "fix login", "fix login", "fix login"},
|
{"plain", "fix login", "fix login", "fix login"},
|
||||||
|
|||||||
Reference in new issue
Block a user