diff --git a/features/snippets.feature b/features/snippets.feature index f2bc607c..4550c05a 100644 --- a/features/snippets.feature +++ b/features/snippets.feature @@ -105,17 +105,17 @@ Feature: undefined step snippets When I run feature suite And the undefined step snippets should be: """ - func thereIsAWhichCosts(arg1 string, arg2 int) error { + func iAddTheToTheBasket(arg1 string) error { return godog.ErrPending } - func iAddTheToTheBasket(arg1 string) error { + func thereIsAWhichCosts(arg1 string, arg2 int) error { return godog.ErrPending } func FeatureContext(s *godog.Suite) { - s.Step(`^there is a "([^"]*)", which costs £(\d+)$`, thereIsAWhichCosts) s.Step(`^I add the "([^"]*)" to the basket$`, iAddTheToTheBasket) + s.Step(`^there is a "([^"]*)", which costs £(\d+)$`, thereIsAWhichCosts) } """ @@ -131,16 +131,16 @@ Feature: undefined step snippets When I run feature suite And the undefined step snippets should be: """ - func whichCosts(arg1 string, arg2 int) error { + func godogs(arg1 int) error { return godog.ErrPending } - func godogs(arg1 int) error { + func whichCosts(arg1 string, arg2 int) error { return godog.ErrPending } func FeatureContext(s *godog.Suite) { - s.Step(`^"([^"]*)", which costs £(\d+)$`, whichCosts) s.Step(`^(\d+) godogs$`, godogs) + s.Step(`^"([^"]*)", which costs £(\d+)$`, whichCosts) } """ diff --git a/fmt.go b/fmt.go index 7aa35d53..97796361 100644 --- a/fmt.go +++ b/fmt.go @@ -5,47 +5,17 @@ import ( "fmt" "io" "os" - "reflect" - "regexp" + "sort" "strconv" "strings" "sync" - "text/template" "time" "unicode" - "github.com/cucumber/godog/colors" - "github.com/cucumber/messages-go/v10" -) - -// some snippet formatting regexps -var snippetExprCleanup = regexp.MustCompile("([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+\\'])") -var snippetExprQuoted = regexp.MustCompile("(\\W|^)\"(?:[^\"]*)\"(\\W|$)") -var snippetMethodName = regexp.MustCompile("[^a-zA-Z\\_\\ ]") -var snippetNumbers = regexp.MustCompile("(\\d+)") -var snippetHelperFuncs = template.FuncMap{ - "backticked": func(s string) string { - return "`" + s + "`" - }, -} - -var undefinedSnippetsTpl = template.Must(template.New("snippets").Funcs(snippetHelperFuncs).Parse(` -{{ range . }}func {{ .Method }}({{ .Args }}) error { - return godog.ErrPending -} - -{{end}}func FeatureContext(s *godog.Suite) { {{ range . }} - s.Step({{ backticked .Expr }}, {{ .Method }}){{end}} -} -`)) - -type undefinedSnippet struct { - Method string - Expr string - argument *messages.PickleStepArgument -} + "github.com/cucumber/godog/colors" +) type registeredFormatter struct { name string @@ -438,55 +408,6 @@ func (f *basefmt) Copy(cf ConcurrentFormatter) { } } -func (s *undefinedSnippet) Args() (ret string) { - var ( - args []string - pos int - breakLoop bool - ) - for !breakLoop { - part := s.Expr[pos:] - ipos := strings.Index(part, "(\\d+)") - spos := strings.Index(part, "\"([^\"]*)\"") - switch { - case spos == -1 && ipos == -1: - breakLoop = true - case spos == -1: - pos += ipos + len("(\\d+)") - args = append(args, reflect.Int.String()) - case ipos == -1: - pos += spos + len("\"([^\"]*)\"") - args = append(args, reflect.String.String()) - case ipos < spos: - pos += ipos + len("(\\d+)") - args = append(args, reflect.Int.String()) - case spos < ipos: - pos += spos + len("\"([^\"]*)\"") - args = append(args, reflect.String.String()) - } - } - - if s.argument != nil { - if s.argument.GetDocString() != nil { - args = append(args, "*messages.PickleStepArgument_PickleDocString") - } - if s.argument.GetDataTable() != nil { - args = append(args, "*messages.PickleStepArgument_PickleTable") - } - } - - var last string - for i, arg := range args { - if last == "" || last == arg { - ret += fmt.Sprintf("arg%d, ", i+1) - } else { - ret = strings.TrimRight(ret, ", ") + fmt.Sprintf(" %s, arg%d, ", last, i+1) - } - last = arg - } - return strings.TrimSpace(strings.TrimRight(ret, ", ") + " " + last) -} - func (f *basefmt) findStepResults(status stepResultStatus) (res []*stepResult) { for _, feat := range f.features { for _, pr := range feat.pickleResults { @@ -508,7 +429,7 @@ func (f *basefmt) snippets() string { } var index int - var snips []*undefinedSnippet + var snips []undefinedSnippet // build snippets for _, u := range undefinedStepResults { steps := []string{u.step.Text} @@ -550,11 +471,13 @@ func (f *basefmt) snippets() string { } } if !found { - snips = append(snips, &undefinedSnippet{Method: name, Expr: expr, argument: arg}) + snips = append(snips, undefinedSnippet{Method: name, Expr: expr, argument: arg}) } } } + sort.Sort(snippetSortByMethod(snips)) + var buf bytes.Buffer if err := undefinedSnippetsTpl.Execute(&buf, snips); err != nil { panic(err) diff --git a/fmt_progress_test.go b/fmt_progress_test.go index 54dfc819..53780be4 100644 --- a/fmt_progress_test.go +++ b/fmt_progress_test.go @@ -67,17 +67,17 @@ func TestProgressFormatterOutput(t *testing.T) { You can implement step definitions for undefined steps with these snippets: -func undefined() error { +func nextUndefined() error { return godog.ErrPending } -func nextUndefined() error { +func undefined() error { return godog.ErrPending } func FeatureContext(s *godog.Suite) { - s.Step(` + "`^undefined$`" + `, undefined) s.Step(` + "`^next undefined$`" + `, nextUndefined) + s.Step(` + "`^undefined$`" + `, undefined) } ` @@ -237,7 +237,7 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) { You can implement step definitions for undefined steps with these snippets: -func undef() error { +func three() error { return godog.ErrPending } @@ -245,14 +245,14 @@ func unavailableCost(arg1 string, arg2 int) error { return godog.ErrPending } -func three() error { +func undef() error { return godog.ErrPending } func FeatureContext(s *godog.Suite) { - s.Step(` + "`^undef$`" + `, undef) - s.Step(` + "`^unavailable \"([^\"]*)\" cost (\\d+)$`" + `, unavailableCost) s.Step(` + "`^three$`" + `, three) + s.Step(` + "`^unavailable \"([^\"]*)\" cost (\\d+)$`" + `, unavailableCost) + s.Step(` + "`^undef$`" + `, undef) } ` diff --git a/undefined_snippets_gen.go b/undefined_snippets_gen.go new file mode 100644 index 00000000..456b50dc --- /dev/null +++ b/undefined_snippets_gen.go @@ -0,0 +1,108 @@ +package godog + +import ( + "fmt" + "reflect" + "regexp" + "strings" + "text/template" + + "github.com/cucumber/messages-go/v10" +) + +// some snippet formatting regexps +var snippetExprCleanup = regexp.MustCompile("([\\/\\[\\]\\(\\)\\\\^\\$\\.\\|\\?\\*\\+\\'])") +var snippetExprQuoted = regexp.MustCompile("(\\W|^)\"(?:[^\"]*)\"(\\W|$)") +var snippetMethodName = regexp.MustCompile("[^a-zA-Z\\_\\ ]") +var snippetNumbers = regexp.MustCompile("(\\d+)") + +var snippetHelperFuncs = template.FuncMap{ + "backticked": func(s string) string { + return "`" + s + "`" + }, +} + +var undefinedSnippetsTpl = template.Must(template.New("snippets").Funcs(snippetHelperFuncs).Parse(` +{{ range . }}func {{ .Method }}({{ .Args }}) error { + return godog.ErrPending +} + +{{end}}func FeatureContext(s *godog.Suite) { {{ range . }} + s.Step({{ backticked .Expr }}, {{ .Method }}){{end}} +} +`)) + +type undefinedSnippet struct { + Method string + Expr string + argument *messages.PickleStepArgument +} + +func (s undefinedSnippet) Args() (ret string) { + var ( + args []string + pos int + breakLoop bool + ) + + for !breakLoop { + part := s.Expr[pos:] + ipos := strings.Index(part, "(\\d+)") + spos := strings.Index(part, "\"([^\"]*)\"") + + switch { + case spos == -1 && ipos == -1: + breakLoop = true + case spos == -1: + pos += ipos + len("(\\d+)") + args = append(args, reflect.Int.String()) + case ipos == -1: + pos += spos + len("\"([^\"]*)\"") + args = append(args, reflect.String.String()) + case ipos < spos: + pos += ipos + len("(\\d+)") + args = append(args, reflect.Int.String()) + case spos < ipos: + pos += spos + len("\"([^\"]*)\"") + args = append(args, reflect.String.String()) + } + } + + if s.argument != nil { + if s.argument.GetDocString() != nil { + args = append(args, "*messages.PickleStepArgument_PickleDocString") + } + + if s.argument.GetDataTable() != nil { + args = append(args, "*messages.PickleStepArgument_PickleTable") + } + } + + var last string + + for i, arg := range args { + if last == "" || last == arg { + ret += fmt.Sprintf("arg%d, ", i+1) + } else { + ret = strings.TrimRight(ret, ", ") + fmt.Sprintf(" %s, arg%d, ", last, i+1) + } + + last = arg + } + + return strings.TrimSpace(strings.TrimRight(ret, ", ") + " " + last) +} + +type snippetSortByMethod []undefinedSnippet + +func (s snippetSortByMethod) Len() int { + return len(s) +} + +func (s snippetSortByMethod) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s snippetSortByMethod) Less(i, j int) bool { + return s[i].Method < s[j].Method +}