mirror of
https://github.com/docker/cli.git
synced 2026-11-06 13:10:41 -04:00
Allow plugins to return informational messages without the next-steps heading or indentation. Invoke each hook once and render generic messages before the shared next-steps block. Centralize grouping and formatting in PrintMessages, with a callback for template evaluation and error handling. This avoids the need for an intermediate EvaluatedMessage type and simplifies hook collection to return the original responses. Skip messages containing only whitespace while preserving empty lines within non-empty messages. Keep PrintNextSteps as a deprecated wrapper to preserve existing callers and output. Update the printer tests and remove the obsolete appendMessages helper and its tests. Co-authored-by: Nicolas Beck <nicolas.beck@docker.com> Signed-off-by: Nicolas Beck <nicolas.beck@docker.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
136 lines
4.1 KiB
Go
136 lines
4.1 KiB
Go
package hooks_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/docker/cli/cli-plugins/hooks"
|
|
"github.com/spf13/cobra"
|
|
"gotest.tools/v3/assert"
|
|
)
|
|
|
|
// TestParseTemplate tests parsing templates as returned by plugins.
|
|
//
|
|
// It uses fixed string fixtures to lock in compatibility with existing
|
|
// plugin templates, so older formats continue to work even if we add new
|
|
// template forms.
|
|
//
|
|
// For helper-backed cases, it also verifies that templates produced by the
|
|
// current TemplateReplace* helpers parse to the same output. This lets us
|
|
// evolve the emitted template format without breaking older plugins.
|
|
func TestParseTemplate(t *testing.T) {
|
|
type testFlag struct {
|
|
name string
|
|
value string
|
|
}
|
|
tests := []struct {
|
|
doc string
|
|
template string // compatibility fixture; keep even if helpers emit a newer form
|
|
templateFunc func() string
|
|
flags []testFlag
|
|
args []string
|
|
expectedOutput []string
|
|
}{
|
|
{
|
|
doc: "empty template",
|
|
template: "",
|
|
expectedOutput: nil,
|
|
},
|
|
{
|
|
doc: "only empty lines",
|
|
template: "\n\n",
|
|
expectedOutput: nil,
|
|
},
|
|
{
|
|
doc: "whitespace-only lines",
|
|
template: " \n\t\n ",
|
|
expectedOutput: nil,
|
|
},
|
|
{
|
|
doc: "plain message",
|
|
template: "a plain template message",
|
|
expectedOutput: []string{"a plain template message"},
|
|
},
|
|
{
|
|
doc: "subcommand name",
|
|
template: "hello {{.Name}}", // NOTE: fixture; do not modify without considering plugin compatibility
|
|
templateFunc: func() string { return "hello " + hooks.TemplateReplaceSubcommandName() },
|
|
|
|
expectedOutput: []string{"hello pull"},
|
|
},
|
|
{
|
|
doc: "single flag",
|
|
template: `{{flag . "tag"}}`, // NOTE: fixture; do not modify without considering plugin compatibility
|
|
templateFunc: func() string { return hooks.TemplateReplaceFlagValue("tag") },
|
|
flags: []testFlag{
|
|
{name: "tag", value: "my-tag"},
|
|
},
|
|
expectedOutput: []string{"my-tag"},
|
|
},
|
|
{
|
|
doc: "multiple flags",
|
|
template: `{{flag . "test-one"}} {{flag . "test2"}}`, // NOTE: fixture; do not modify without considering plugin compatibility
|
|
templateFunc: func() string {
|
|
return hooks.TemplateReplaceFlagValue("test-one") + " " + hooks.TemplateReplaceFlagValue("test2")
|
|
},
|
|
flags: []testFlag{
|
|
{
|
|
name: "test-one",
|
|
value: "value",
|
|
},
|
|
{
|
|
name: "test2",
|
|
value: "value2",
|
|
},
|
|
},
|
|
expectedOutput: []string{"value value2"},
|
|
},
|
|
{
|
|
doc: "multiple args",
|
|
template: `{{arg . 0}} {{arg . 1}}`, // NOTE: fixture; do not modify without considering plugin compatibility
|
|
templateFunc: func() string { return hooks.TemplateReplaceArg(0) + " " + hooks.TemplateReplaceArg(1) },
|
|
args: []string{"zero", "one"},
|
|
expectedOutput: []string{"zero one"},
|
|
},
|
|
{
|
|
doc: "arg in sentence",
|
|
template: "You just pulled {{arg . 0}}", // NOTE: fixture; do not modify without considering plugin compatibility
|
|
templateFunc: func() string { return "You just pulled " + hooks.TemplateReplaceArg(0) },
|
|
args: []string{"alpine"},
|
|
expectedOutput: []string{"You just pulled alpine"},
|
|
},
|
|
{
|
|
doc: "multiline output",
|
|
template: "one line\nanother line!",
|
|
expectedOutput: []string{"one line", "another line!"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.doc, func(t *testing.T) {
|
|
testCmd := &cobra.Command{
|
|
Use: "pull",
|
|
Args: cobra.ExactArgs(len(tc.args)),
|
|
}
|
|
for _, f := range tc.flags {
|
|
_ = testCmd.Flags().String(f.name, "", "")
|
|
err := testCmd.Flag(f.name).Value.Set(f.value)
|
|
assert.NilError(t, err)
|
|
}
|
|
err := testCmd.Flags().Parse(tc.args)
|
|
assert.NilError(t, err)
|
|
|
|
// Validate using fixtures.
|
|
out, err := hooks.ParseTemplate(tc.template, testCmd)
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, out, tc.expectedOutput)
|
|
|
|
if tc.templateFunc != nil {
|
|
// Validate using the current template function equivalent.
|
|
out, err = hooks.ParseTemplate(tc.templateFunc(), testCmd)
|
|
assert.NilError(t, err)
|
|
assert.DeepEqual(t, out, tc.expectedOutput)
|
|
}
|
|
})
|
|
}
|
|
}
|