feat: test ssh ui output

Ayman Bagabas created

Still wip

wip

feat(tests): run against ssh ui

Change summary

cmd/soft/browse/browse.go           |  3 
cmd/soft/main.go                    |  7 +++
pkg/ssh/session.go                  |  3 
pkg/ui/common/common.go             |  5 +
testscript/script_test.go           | 64 +++++++++++++++++++++++++++++++
testscript/testdata/repo-blob.txtar |  2 
testscript/testdata/ui-home.txtar   | 53 +++++++++++++++++++++++++
7 files changed, 131 insertions(+), 6 deletions(-)

Detailed changes

cmd/soft/browse/browse.go 🔗

@@ -13,7 +13,6 @@ import (
 	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/charmbracelet/soft-serve/pkg/ui/components/footer"
 	"github.com/charmbracelet/soft-serve/pkg/ui/pages/repo"
-	"github.com/muesli/termenv"
 	"github.com/spf13/cobra"
 )
 
@@ -40,7 +39,7 @@ var Command = &cobra.Command{
 
 		// Bubble Tea uses Termenv default output so we have to use the same
 		// thing here.
-		output := termenv.DefaultOutput()
+		output := lipgloss.DefaultRenderer()
 		ctx := cmd.Context()
 		c := common.NewCommon(ctx, output, 0, 0)
 		c.HideCloneCmd = true

cmd/soft/main.go 🔗

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"os"
 	"runtime/debug"
+	"strconv"
 
 	"github.com/charmbracelet/log"
 	"github.com/charmbracelet/soft-serve/cmd/soft/admin"
@@ -13,9 +14,11 @@ import (
 	"github.com/charmbracelet/soft-serve/cmd/soft/serve"
 	"github.com/charmbracelet/soft-serve/pkg/config"
 	logr "github.com/charmbracelet/soft-serve/pkg/log"
+	"github.com/charmbracelet/soft-serve/pkg/ui/common"
 	"github.com/charmbracelet/soft-serve/pkg/version"
 	mcobra "github.com/muesli/mango-cobra"
 	"github.com/muesli/roff"
+	"github.com/muesli/termenv"
 	"github.com/spf13/cobra"
 	"go.uber.org/automaxprocs/maxprocs"
 )
@@ -63,6 +66,10 @@ var (
 )
 
 func init() {
+	if noColor, _ := strconv.ParseBool(os.Getenv("SOFT_SERVE_NO_COLOR")); noColor {
+		common.DefaultColorProfile = termenv.Ascii
+	}
+
 	rootCmd.AddCommand(
 		manCmd,
 		serve.Command,

pkg/ssh/session.go 🔗

@@ -5,6 +5,7 @@ import (
 	"time"
 
 	tea "github.com/charmbracelet/bubbletea"
+	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/soft-serve/pkg/access"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
 	"github.com/charmbracelet/soft-serve/pkg/config"
@@ -54,7 +55,7 @@ func SessionHandler(s ssh.Session) *tea.Program {
 	}
 
 	envs := &sessionEnv{s}
-	output := termenv.NewOutput(s, termenv.WithColorCache(true), termenv.WithEnvironment(envs))
+	output := lipgloss.NewRenderer(s, termenv.WithColorCache(true), termenv.WithEnvironment(envs))
 	c := common.NewCommon(ctx, output, pty.Window.Width, pty.Window.Height)
 	c.SetValue(common.ConfigKey, cfg)
 	m := NewUI(c, initialRepo)

pkg/ui/common/common.go 🔗

@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/charmbracelet/lipgloss"
 	"github.com/charmbracelet/log"
 	"github.com/charmbracelet/soft-serve/git"
 	"github.com/charmbracelet/soft-serve/pkg/backend"
@@ -38,7 +39,7 @@ type Common struct {
 }
 
 // NewCommon returns a new Common struct.
-func NewCommon(ctx context.Context, out *termenv.Output, width, height int) Common {
+func NewCommon(ctx context.Context, out *lipgloss.Renderer, width, height int) Common {
 	if ctx == nil {
 		ctx = context.TODO()
 	}
@@ -46,7 +47,7 @@ func NewCommon(ctx context.Context, out *termenv.Output, width, height int) Comm
 		ctx:    ctx,
 		Width:  width,
 		Height: height,
-		Output: out,
+		Output: out.Output(),
 		Styles: styles.DefaultStyles(),
 		KeyMap: keymap.DefaultKeyMap(),
 		Zone:   zone.New(),

testscript/script_test.go 🔗

@@ -15,6 +15,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"runtime"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -92,6 +93,8 @@ func TestScript(t *testing.T) {
 			"new-webhook":   cmdNewWebhook,
 			"waitforserver": cmdWaitforserver,
 			"stopserver":    cmdStopserver,
+			"ui":            cmdUI(admin1.Signer()),
+			"uui":           cmdUI(user1.Signer()),
 		},
 		Setup: func(e *testscript.Env) error {
 			// Add binPath to PATH
@@ -120,6 +123,9 @@ func TestScript(t *testing.T) {
 			// This is used to set up test specific configuration and http endpoints
 			e.Setenv("SOFT_SERVE_TESTRUN", "1")
 
+			// This will disable the default lipgloss renderer colors
+			e.Setenv("SOFT_SERVE_NO_COLOR", "1")
+
 			// Soft Serve debug environment variables
 			for _, env := range []string{
 				"SOFT_SERVE_DEBUG",
@@ -199,6 +205,64 @@ func cmdSoft(key ssh.Signer) func(ts *testscript.TestScript, neg bool, args []st
 	}
 }
 
+func cmdUI(key ssh.Signer) func(ts *testscript.TestScript, neg bool, args []string) {
+	return func(ts *testscript.TestScript, neg bool, args []string) {
+		if len(args) < 1 {
+			ts.Fatalf("usage: ui <quoted string input>")
+			return
+		}
+
+		cli, err := ssh.Dial(
+			"tcp",
+			net.JoinHostPort("localhost", ts.Getenv("SSH_PORT")),
+			&ssh.ClientConfig{
+				User:            "git",
+				Auth:            []ssh.AuthMethod{ssh.PublicKeys(key)},
+				HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+			},
+		)
+		check(ts, err, neg)
+		defer cli.Close()
+
+		sess, err := cli.NewSession()
+		check(ts, err, neg)
+		defer sess.Close()
+
+		// XXX: this is a hack to make the UI tests work
+		// cmp command always complains about an extra newline
+		// in the output
+		defer ts.Stdout().Write([]byte("\n"))
+
+		sess.Stdout = ts.Stdout()
+		sess.Stderr = ts.Stderr()
+
+		stdin, err := sess.StdinPipe()
+		check(ts, err, neg)
+
+		in, err := strconv.Unquote(args[0])
+		check(ts, err, neg)
+		reader := strings.NewReader(in)
+		go func() {
+			defer stdin.Close()
+			for {
+				r, _, err := reader.ReadRune()
+				if err == io.EOF {
+					break
+				}
+				check(ts, err, neg)
+				stdin.Write([]byte(string(r))) // nolint: errcheck
+
+				// Wait for the UI to process the input
+				time.Sleep(100 * time.Millisecond)
+			}
+		}()
+
+		err = sess.RequestPty("dumb", 40, 80, ssh.TerminalModes{})
+		check(ts, err, neg)
+		check(ts, sess.Run(""), neg)
+	}
+}
+
 // P.S. Windows sucks!
 func cmdDos2Unix(ts *testscript.TestScript, neg bool, args []string) {
 	if neg {

testscript/testdata/repo-blob.txtar 🔗

@@ -54,6 +54,6 @@ stderr 'revision does not exist'
 -- blob1.txt --
 # Hello\n\nwelcome
 -- blob2.txt --
- 1 │ package main\nconst foo = 2\n
+ 1 │ package main\nconst foo = 2\n
 -- blob3.txt --
  1 │ //#include <stdio.h>

testscript/testdata/ui-home.txtar 🔗

@@ -0,0 +1,53 @@
+# vi: set ft=conf
+
+# start soft serve
+exec soft serve &
+# wait for server to start
+waitforserver
+
+# test repositories tab
+ui '"    q"'
+cp stdout home.txt
+grep 'Test Soft Serve' home.txt
+grep '• Repositories' home.txt
+grep 'No items found' home.txt
+
+# test about tab
+ui '"\t    q"'
+cp stdout about.txt
+grep 'Create a `.soft-serve` repository and add a `README.md` file' about.txt
+
+# add a new repo
+soft repo create .soft-serve -n 'Config' -d '"Test Soft Serve"'
+soft repo description .soft-serve
+stdout 'Test Soft Serve'
+soft repo project-name .soft-serve
+stdout 'Config'
+
+# clone repo
+git clone ssh://localhost:$SSH_PORT/.soft-serve config
+
+# create readme file
+mkfile ./config/README.md '# Hello World\nTest Soft Serve'
+git -C config add -A
+git -C config commit -m 'Initial commit'
+git -C config push origin HEAD
+
+# test repositories tab
+ui '"    q"'
+cp stdout home2.txt
+grep 'Config' home2.txt
+grep 'Test Soft Serve' home2.txt
+grep 'git clone ssh://localhost:.*/.soft-serve' home2.txt
+
+# test about tab
+ui '"\t      q"'
+cp stdout about2.txt
+grep '• About' about2.txt
+grep 'Hello World' about2.txt
+grep 'Test Soft Serve' about2.txt
+
+# stop the server
+[windows] stopserver
+[windows] ! stderr .
+