diff --git a/internal/app/app.go b/internal/app/app.go
index 93f7f82..6303ecc 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -68,13 +68,14 @@ func Run(option Options) (int, error) {
defer option.FollowOutputWriter.Close()
}
+ progressWriter := newConsoleWriter(option.Output, option.Format, option.DisableColor)
summary, err := parse.Process(
reader,
parse.WithFollowOutput(option.FollowOutput),
parse.WithFollowVersboseOutput(option.FollowOutputVerbose),
parse.WithWriter(option.FollowOutputWriter),
parse.WithProgress(option.Progress),
- parse.WithProgressOutput(option.ProgressOutput),
+ parse.WithProgressOutput(progressWriter),
)
if err != nil {
return 1, err
diff --git a/internal/app/console_writer.go b/internal/app/console_writer.go
index 7871cbe..979424b 100644
--- a/internal/app/console_writer.go
+++ b/internal/app/console_writer.go
@@ -2,9 +2,12 @@ package app
import (
"io"
+ "strings"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
+
+ "github.com/mfridman/tparse/parse"
)
type OutputFormat int
@@ -19,8 +22,8 @@ const (
)
type consoleWriter struct {
+ io.Writer
format OutputFormat
- w io.Writer
red colorOptionFunc
green colorOptionFunc
@@ -52,7 +55,7 @@ func newConsoleWriter(w io.Writer, format OutputFormat, disableColor bool) *cons
format = OutputFormatBasic
}
cw := &consoleWriter{
- w: w,
+ Writer: w,
format: format,
}
cw.red = noColor()
@@ -81,3 +84,17 @@ func newConsoleWriter(w io.Writer, format OutputFormat, disableColor bool) *cons
}
return cw
}
+
+func (w *consoleWriter) FormatAction(action parse.Action) string {
+ s := strings.ToUpper(action.String())
+ switch action {
+ case parse.ActionPass:
+ return w.green(s)
+ case parse.ActionSkip:
+ return w.yellow(s)
+ case parse.ActionFail:
+ return w.red(s)
+ default:
+ return s
+ }
+}
diff --git a/internal/app/table_failed.go b/internal/app/table_failed.go
index 8bc8f57..1f11ef0 100644
--- a/internal/app/table_failed.go
+++ b/internal/app/table_failed.go
@@ -29,7 +29,7 @@ func (c *consoleWriter) printFailed(packages []*parse.Package) {
// TODO(mf): document why panics are handled separately. A panic may or may
// not be associated with tests, so we print it at the package level.
output := c.prepareStyledPanic(pkg.Summary.Package, pkg.Summary.Test, pkg.PanicEvents, width)
- fmt.Fprintln(c.w, output)
+ fmt.Fprintln(c, output)
continue
}
failedTests := pkg.TestsByAction(parse.ActionFail)
@@ -40,8 +40,8 @@ func (c *consoleWriter) printFailed(packages []*parse.Package) {
pkg.Summary.Action.String(),
pkg.Summary.Package,
)
- fmt.Fprintln(c.w, styledPackageHeader)
- fmt.Fprintln(c.w)
+ fmt.Fprintln(c, styledPackageHeader)
+ fmt.Fprintln(c)
/*
Failed tests are all the individual tests, where the subtests are not separated.
@@ -75,20 +75,20 @@ func (c *consoleWriter) printFailed(packages []*parse.Package) {
*/
if c.format == OutputFormatMarkdown {
- fmt.Fprintln(c.w, fencedCodeBlock)
+ fmt.Fprintln(c, fencedCodeBlock)
}
var key string
for i, t := range failedTests {
// Add top divider to all tests except first one.
base, _, _ := cut(t.Name, "/")
if i > 0 && key != base {
- fmt.Fprintln(c.w, divider.String())
+ fmt.Fprintln(c, divider.String())
}
key = base
- fmt.Fprintln(c.w, c.prepareStyledTest(t))
+ fmt.Fprintln(c, c.prepareStyledTest(t))
}
if c.format == OutputFormatMarkdown {
- fmt.Fprint(c.w, fencedCodeBlock+"\n\n")
+ fmt.Fprint(c, fencedCodeBlock+"\n\n")
}
}
}
diff --git a/internal/app/table_summary.go b/internal/app/table_summary.go
index 8a2741d..bb571c6 100644
--- a/internal/app/table_summary.go
+++ b/internal/app/table_summary.go
@@ -167,15 +167,7 @@ func (c *consoleWriter) summaryTable(
}
}
- status := strings.ToUpper(pkg.Summary.Action.String())
- switch pkg.Summary.Action {
- case parse.ActionPass:
- status = c.green(status)
- case parse.ActionSkip:
- status = c.yellow(status)
- case parse.ActionFail:
- status = c.red(status)
- }
+ status := c.FormatAction(pkg.Summary.Action)
// Skip packages with no coverage to mimic nocoverageredesign behavior (changed in github.com/golang/go/issues/24570)
totalTests := len(pkg.TestsByAction(parse.ActionPass)) + len(pkg.TestsByAction(parse.ActionFail)) + len(pkg.TestsByAction(parse.ActionSkip))
@@ -212,7 +204,7 @@ func (c *consoleWriter) summaryTable(
}
}
- fmt.Fprintln(c.w, tbl.Data(data).Render())
+ fmt.Fprintln(c, tbl.Data(data).Render())
}
type summaryRow struct {
diff --git a/internal/app/table_tests.go b/internal/app/table_tests.go
index 4eb7a0f..9eeb6fa 100644
--- a/internal/app/table_tests.go
+++ b/internal/app/table_tests.go
@@ -87,16 +87,7 @@ func (c *consoleWriter) testsTable(packages []*parse.Package, option TestTableOp
testName := shortenTestName(t.Name, option.Trim, 32)
- status := strings.ToUpper(t.Status().String())
- switch t.Status() {
- case parse.ActionPass:
- status = c.green(status)
- case parse.ActionSkip:
- status = c.yellow(status)
- case parse.ActionFail:
- status = c.red(status)
- }
-
+ status := c.FormatAction(t.Status())
packageName := shortenPackageName(t.Package, packagePrefix, 16, option.Trim, option.TrimPath)
row := testRow{
@@ -114,7 +105,7 @@ func (c *consoleWriter) testsTable(packages []*parse.Package, option TestTableOp
}
if data.Rows() > 0 {
- fmt.Fprintln(c.w, tbl.Data(data).Render())
+ fmt.Fprintln(c, tbl.Data(data).Render())
}
}
@@ -155,15 +146,7 @@ func (c *consoleWriter) testsTableMarkdown(packages []*parse.Package, option Tes
testName := shortenTestName(t.Name, option.Trim, 32)
- status := strings.ToUpper(t.Status().String())
- switch t.Status() {
- case parse.ActionPass:
- status = c.green(status)
- case parse.ActionSkip:
- status = c.yellow(status)
- case parse.ActionFail:
- status = c.red(status)
- }
+ status := c.FormatAction(t.Status())
data.Append([]string{
status,
strconv.FormatFloat(t.Elapsed(), 'f', 2, 64),
@@ -171,8 +154,8 @@ func (c *consoleWriter) testsTableMarkdown(packages []*parse.Package, option Tes
})
}
if data.Rows() > 0 {
- fmt.Fprintf(c.w, "## 📦 Package **`%s`**\n", pkg.Summary.Package)
- fmt.Fprintln(c.w)
+ fmt.Fprintf(c, "## 📦 Package **`%s`**\n", pkg.Summary.Package)
+ fmt.Fprintln(c)
msg := fmt.Sprintf("Tests: ✓ %d passed | %d skipped\n",
pkgTests.passedCount,
@@ -184,18 +167,18 @@ func (c *consoleWriter) testsTableMarkdown(packages []*parse.Package, option Tes
pkgTests.passedCount,
)
}
- fmt.Fprint(c.w, msg)
-
- fmt.Fprintln(c.w)
- fmt.Fprintln(c.w, "")
- fmt.Fprintln(c.w)
- fmt.Fprintln(c.w, "Click for test summary
")
- fmt.Fprintln(c.w)
- fmt.Fprintln(c.w, tbl.Data(data).Render())
- fmt.Fprintln(c.w, " ")
- fmt.Fprintln(c.w)
+ fmt.Fprint(c, msg)
+
+ fmt.Fprintln(c)
+ fmt.Fprintln(c, "")
+ fmt.Fprintln(c)
+ fmt.Fprintln(c, "Click for test summary
")
+ fmt.Fprintln(c)
+ fmt.Fprintln(c, tbl.Data(data).Render())
+ fmt.Fprintln(c, " ")
+ fmt.Fprintln(c)
}
- fmt.Fprintln(c.w)
+ fmt.Fprintln(c)
}
}
diff --git a/parse/process.go b/parse/process.go
index 8c28e99..3535b72 100644
--- a/parse/process.go
+++ b/parse/process.go
@@ -142,7 +142,7 @@ func Process(r io.Reader, optionsFunc ...OptionsFunc) (*GoTestSummary, error) {
// printProgress prints a single summary line for each PASS or FAIL package.
// This is useful for long-running test suites.
-func printProgress(w io.Writer, e *Event, summary map[string]*Package) {
+func printProgress(w progressWriter, e *Event, summary map[string]*Package) {
if !e.LastLine() {
return
}
@@ -174,7 +174,7 @@ func printProgress(w io.Writer, e *Event, summary map[string]*Package) {
//
// We modify this output slightly so it's more consistent and easier to parse.
fmt.Fprintf(w, "[%s]\t%10s\t%s%s\n",
- strings.ToUpper(action.String()),
+ w.FormatAction(action),
strconv.FormatFloat(e.Elapsed, 'f', 2, 64)+"s",
e.Package,
suffix,
diff --git a/parse/process_options.go b/parse/process_options.go
index 8a63b5c..1dab626 100644
--- a/parse/process_options.go
+++ b/parse/process_options.go
@@ -4,6 +4,11 @@ import (
"io"
)
+type progressWriter interface {
+ io.Writer
+ FormatAction(Action) string
+}
+
type options struct {
w io.Writer
follow bool
@@ -11,7 +16,7 @@ type options struct {
debug bool
progress bool
- progressOutput io.Writer
+ progressOutput progressWriter
}
type OptionsFunc func(o *options)
@@ -36,6 +41,6 @@ func WithProgress(b bool) OptionsFunc {
return func(o *options) { o.progress = b }
}
-func WithProgressOutput(w io.Writer) OptionsFunc {
+func WithProgressOutput(w progressWriter) OptionsFunc {
return func(o *options) { o.progressOutput = w }
}