diff --git a/flags.go b/flags.go index 68ba3fa..e93567c 100644 --- a/flags.go +++ b/flags.go @@ -53,9 +53,10 @@ func usage() { } // --> tags fmt.Println(opt("-t, --tags", "Filter scenarios by tags. Expression can be:")) - fmt.Println(opt("", s(4)+"- "+cl(`"wip"`, yellow)+": run all scenarios with wip tag")) - fmt.Println(opt("", s(4)+"- "+cl(`"!wip"`, yellow)+": exclude all scenarios with wip tag")) - fmt.Println(opt("", s(4)+"- "+cl(`"wip !new"`, yellow)+": run wip scenarios, but exclude new")) + fmt.Println(opt("", s(4)+"- "+cl(`"@wip"`, yellow)+": run all scenarios with wip tag")) + fmt.Println(opt("", s(4)+"- "+cl(`"~@wip"`, yellow)+": exclude all scenarios with wip tag")) + fmt.Println(opt("", s(4)+"- "+cl(`"@wip && ~@new"`, yellow)+": run wip scenarios, but exclude new")) + fmt.Println(opt("", s(4)+"- "+cl(`"@wip,@undone"`, yellow)+": run wip or undone scenarios")) // --> stop on failure fmt.Println(opt("--stop-on-failure", "Stop processing on first failed scenario.")) // --> version diff --git a/suite.go b/suite.go index b9847f1..8e74202 100644 --- a/suite.go +++ b/suite.go @@ -477,20 +477,30 @@ func (s *suite) applyTagFilter(ft *gherkin.Feature) { return } - for _, tag := range strings.Split(s.tags, " ") { - var scenarios []*gherkin.Scenario - var inverse bool - if tag[0] == '!' { - tag = tag[1:] - inverse = true + var scenarios []*gherkin.Scenario + for _, scenario := range ft.Scenarios { + if s.matchesTags(scenario.Tags) { + scenarios = append(scenarios, scenario) } - for _, scenario := range ft.Scenarios { - if inverse && !scenario.Tags.Has(gherkin.Tag(tag)) { - scenarios = append(scenarios, scenario) - } else if !inverse && scenario.Tags.Has(gherkin.Tag(tag)) { - scenarios = append(scenarios, scenario) + } + ft.Scenarios = scenarios +} + +// based on http://behat.readthedocs.org/en/v2.5/guides/6.cli.html#gherkin-filters +func (s *suite) matchesTags(tags gherkin.Tags) (ok bool) { + ok = true + for _, andTags := range strings.Split(s.tags, "&&") { + var okComma bool + for _, tag := range strings.Split(andTags, ",") { + tag = strings.Replace(strings.TrimSpace(tag), "@", "", -1) + if tag[0] == '~' { + tag = tag[1:] + okComma = !tags.Has(gherkin.Tag(tag)) || okComma + } else { + okComma = tags.Has(gherkin.Tag(tag)) || okComma } } - ft.Scenarios = scenarios + ok = (false != okComma && ok && okComma) || false } + return } diff --git a/tag_filter_test.go b/tag_filter_test.go new file mode 100644 index 0000000..0c83caa --- /dev/null +++ b/tag_filter_test.go @@ -0,0 +1,41 @@ +package godog + +import ( + "testing" + + "github.com/DATA-DOG/godog/gherkin" +) + +func assertNotMatchesTagFilter(tags []string, filter string, t *testing.T) { + gtags := gherkin.Tags{} + for _, tag := range tags { + gtags = append(gtags, gherkin.Tag(tag)) + } + s := &suite{tags: filter} + if s.matchesTags(gtags) { + t.Errorf(`expected tags: %v not to match tag filter "%s", but it did`, gtags, filter) + } +} + +func assertMatchesTagFilter(tags []string, filter string, t *testing.T) { + gtags := gherkin.Tags{} + for _, tag := range tags { + gtags = append(gtags, gherkin.Tag(tag)) + } + s := &suite{tags: filter} + if !s.matchesTags(gtags) { + t.Errorf(`expected tags: %v to match tag filter "%s", but it did not`, gtags, filter) + } +} + +func Test_tag_filter(t *testing.T) { + assertMatchesTagFilter([]string{"wip"}, "@wip", t) + assertMatchesTagFilter([]string{}, "~@wip", t) + assertMatchesTagFilter([]string{"one", "two"}, "@two,@three", t) + assertMatchesTagFilter([]string{"one", "two"}, "@one&&@two", t) + assertMatchesTagFilter([]string{"one", "two"}, "one && two", t) + + assertNotMatchesTagFilter([]string{}, "@wip", t) + assertNotMatchesTagFilter([]string{"one", "two"}, "@one&&~@two", t) + assertNotMatchesTagFilter([]string{"one", "two"}, "@one && ~@two", t) +}