mirror of
https://github.com/go-task/task.git
synced 2025-04-23 12:18:57 +02:00
221 lines
5.1 KiB
Go
221 lines
5.1 KiB
Go
package task_test
|
|
|
|
import (
|
|
"bytes"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/sebdah/goldie/v2"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/go-task/task/v3"
|
|
"github.com/go-task/task/v3/internal/experiments"
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
|
)
|
|
|
|
type (
|
|
// A FormatterTestOption is a function that configures an [FormatterTest].
|
|
FormatterTestOption interface {
|
|
applyToFormatterTest(*FormatterTest)
|
|
}
|
|
// A FormatterTest is a test wrapper around a [task.Executor] to make it
|
|
// easy to write tests for the task formatter. See [NewFormatterTest] for
|
|
// information on creating and running FormatterTests. These tests use
|
|
// fixture files to assert whether the result of the output is correct. If
|
|
// Task's behavior has been changed, the fixture files can be updated by
|
|
// running `task gen:fixtures`.
|
|
FormatterTest struct {
|
|
TaskTest
|
|
task string
|
|
vars map[string]any
|
|
executorOpts []task.ExecutorOption
|
|
listOptions task.ListOptions
|
|
wantSetupError bool
|
|
wantListError bool
|
|
}
|
|
)
|
|
|
|
// NewFormatterTest sets up a new [task.Executor] with the given options and
|
|
// runs a task with the given [FormatterTestOption]s. The output of the task is
|
|
// written to a set of fixture files depending on the configuration of the test.
|
|
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
|
t.Helper()
|
|
tt := &FormatterTest{
|
|
task: "default",
|
|
vars: map[string]any{},
|
|
TaskTest: TaskTest{
|
|
experiments: map[*experiments.Experiment]int{},
|
|
},
|
|
}
|
|
// Apply the functional options
|
|
for _, opt := range opts {
|
|
opt.applyToFormatterTest(tt)
|
|
}
|
|
// Enable any experiments that have been set
|
|
for x, v := range tt.experiments {
|
|
prev := *x
|
|
*x = experiments.Experiment{
|
|
Name: prev.Name,
|
|
AllowedValues: []int{v},
|
|
Value: v,
|
|
}
|
|
t.Cleanup(func() {
|
|
*x = prev
|
|
})
|
|
}
|
|
tt.run(t)
|
|
}
|
|
|
|
// Functional options
|
|
|
|
// WithListOptions sets the list options for the formatter.
|
|
func WithListOptions(opts task.ListOptions) FormatterTestOption {
|
|
return &listOptionsTestOption{opts}
|
|
}
|
|
|
|
type listOptionsTestOption struct {
|
|
listOptions task.ListOptions
|
|
}
|
|
|
|
func (opt *listOptionsTestOption) applyToFormatterTest(t *FormatterTest) {
|
|
t.listOptions = opt.listOptions
|
|
}
|
|
|
|
// WithListError tells the test to expect an error when running the formatter.
|
|
// A fixture will be created with the output of any errors.
|
|
func WithListError() FormatterTestOption {
|
|
return &listErrorTestOption{}
|
|
}
|
|
|
|
type listErrorTestOption struct{}
|
|
|
|
func (opt *listErrorTestOption) applyToFormatterTest(t *FormatterTest) {
|
|
t.wantListError = true
|
|
}
|
|
|
|
// Helpers
|
|
|
|
// writeFixtureErrList is a wrapper for writing the output of an error when
|
|
// running the formatter to a fixture file.
|
|
func (tt *FormatterTest) writeFixtureErrList(
|
|
t *testing.T,
|
|
g *goldie.Goldie,
|
|
err error,
|
|
) {
|
|
t.Helper()
|
|
tt.writeFixture(t, g, "err-list", []byte(err.Error()))
|
|
}
|
|
|
|
// run is the main function for running the test. It sets up the task executor,
|
|
// runs the task, and writes the output to a fixture file.
|
|
func (tt *FormatterTest) run(t *testing.T) {
|
|
t.Helper()
|
|
f := func(t *testing.T) {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
|
|
opts := append(
|
|
tt.executorOpts,
|
|
task.WithStdout(&buf),
|
|
task.WithStderr(&buf),
|
|
)
|
|
|
|
// Set up the task executor
|
|
e := task.NewExecutor(opts...)
|
|
|
|
// Create a golden fixture file for the output
|
|
g := goldie.New(t,
|
|
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
|
)
|
|
|
|
// Call setup and check for errors
|
|
if err := e.Setup(); tt.wantSetupError {
|
|
require.Error(t, err)
|
|
tt.writeFixtureErrSetup(t, g, err)
|
|
tt.writeFixtureBuffer(t, g, buf)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Create the task call
|
|
vars := ast.NewVars()
|
|
for key, value := range tt.vars {
|
|
vars.Set(key, ast.Var{Value: value})
|
|
}
|
|
|
|
// Run the formatter and check for errors
|
|
if _, err := e.ListTasks(tt.listOptions); tt.wantListError {
|
|
require.Error(t, err)
|
|
tt.writeFixtureErrList(t, g, err)
|
|
tt.writeFixtureBuffer(t, g, buf)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tt.writeFixtureBuffer(t, g, buf)
|
|
}
|
|
|
|
// Run the test (with a name if it has one)
|
|
if tt.name != "" {
|
|
t.Run(tt.name, f)
|
|
} else {
|
|
f(t)
|
|
}
|
|
}
|
|
|
|
func TestNoLabelInList(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
NewFormatterTest(t,
|
|
WithExecutorOptions(
|
|
task.WithDir("testdata/label_list"),
|
|
),
|
|
WithListOptions(task.ListOptions{
|
|
ListOnlyTasksWithDescriptions: true,
|
|
}),
|
|
)
|
|
}
|
|
|
|
// task -al case 1: listAll list all tasks
|
|
func TestListAllShowsNoDesc(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
NewFormatterTest(t,
|
|
WithExecutorOptions(
|
|
task.WithDir("testdata/list_mixed_desc"),
|
|
),
|
|
WithListOptions(task.ListOptions{
|
|
ListAllTasks: true,
|
|
}),
|
|
)
|
|
}
|
|
|
|
// task -al case 2: !listAll list some tasks (only those with desc)
|
|
func TestListCanListDescOnly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
NewFormatterTest(t,
|
|
WithExecutorOptions(
|
|
task.WithDir("testdata/list_mixed_desc"),
|
|
),
|
|
WithListOptions(task.ListOptions{
|
|
ListOnlyTasksWithDescriptions: true,
|
|
}),
|
|
)
|
|
}
|
|
|
|
func TestListDescInterpolation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
NewFormatterTest(t,
|
|
WithExecutorOptions(
|
|
task.WithDir("testdata/list_desc_interpolation"),
|
|
),
|
|
WithListOptions(task.ListOptions{
|
|
ListOnlyTasksWithDescriptions: true,
|
|
}),
|
|
)
|
|
}
|