func (c *Runner) runScenario(title string, f *gherkin.Feature, s *gherkin.Scenario, isExample bool) { if !s.FilterMatched(f, c.Filters...) { return } for k, fn := range c.BeforeFilters { if s.FilterMatched(f, strings.Split(k, "|")...) { fn() } } if s.Examples != "" { // run scenario outline data exrows := strings.Split(string(s.Examples), "\n") c.line(clrCyan, " "+strings.Join([]string(s.Tags), " ")) c.fileLine("0;1", " %s Outline: %s", s.Filename, s.Line, s.LongestLine()+1, title, s.Title) for _, step := range s.Steps { c.fileLine("0;0", " %s %s", step.Filename, step.Line, s.LongestLine()+1, step.Type, step.Text) } c.line(clrWhite, "") c.line("0;1", " Examples:") c.line(clrCyan, " %s", exrows[0]) tab := s.Examples.ToTable() tabmap := tab.ToMap() for i, rows := 1, len(tab); i < rows; i++ { other := gherkin.Scenario{ Filename: s.Filename, Line: s.Line, Title: s.Title, Examples: gherkin.StringData(""), Steps: []gherkin.Step{}, } for _, step := range s.Steps { step.Text = reOutlineVal.ReplaceAllStringFunc(step.Text, func(t string) string { return tabmap[t[1:len(t)-1]][i-1] }) other.Steps = append(other.Steps, step) } fc := c.FailCount clr := clrGreen c.runScenario(title, f, &other, true) if fc != c.FailCount { clr = clrRed } c.line(clr, " %s", exrows[i]) } c.line(clrWhite, "") for k, fn := range c.AfterFilters { if s.FilterMatched(f, strings.Split(k, "|")...) { fn() } } return } t := &TestingT{} skipping := false clr := clrGreen if !isExample { if len(s.Tags) > 0 { c.line(clrCyan, " "+strings.Join([]string(s.Tags), " ")) } c.fileLine("0;1", " %s: %s", s.Filename, s.Line, s.LongestLine(), title, s.Title) } for _, step := range s.Steps { errCount := len(t.errors) found := false if !skipping && !t.Failed() { done := make(chan bool) go func() { defer func() { c.Results = append(c.Results, RunnerResult{t, f, s}) if t.Skipped() { c.SkipCount++ skipping = true clr = clrYellow } else if t.Failed() { c.FailCount++ clr = clrRed } done <- true }() f, err := c.Execute(t, step.Text, string(step.Argument)) if err != nil { t.Error(err) } found = f if !f { t.Skip("no match function for step") } }() <-done } if skipping && !found { cstep := step c.Unmatched = append(c.Unmatched, &cstep) } if !isExample { c.fileLine(clr, " %s %s", step.Filename, step.Line, s.LongestLine(), step.Type, step.Text) if len(step.Argument) > 0 { if !step.Argument.IsTabular() { c.line(clrWhite, ` """`) } for _, l := range strings.Split(string(step.Argument), "\n") { c.line(clrWhite, " %s", l) } if !step.Argument.IsTabular() { c.line(clrWhite, ` """`) } } } if len(t.errors) > errCount { c.line(clrRed, "\n"+t.errors[len(t.errors)-1].message) } } if !isExample { c.line(clrWhite, "") } for k, fn := range c.AfterFilters { if s.FilterMatched(f, strings.Split(k, "|")...) { fn() } } }
func (s *StepDefinition) CallIfMatch(c *Context, test Tester, line string, arg string) (bool, error) { if match := s.Matcher.FindStringSubmatch(line); match != nil { match = match[1:] // discard full line match // adjust arity if there is step arg data numArgs := len(match) if arg != "" { numArgs++ } t := s.Function.Type() if t.NumIn() > 0 && t.In(0).Kind() == reflect.Ptr { e := t.In(0).Elem() if e.String() == "testing.T" { numArgs++ // first param is *testing.T } } if numArgs != t.NumIn() { // function has different arity return true, fmt.Errorf("matcher function has different arity %d != %d", numArgs, t.NumIn()) } values := make([]reflect.Value, numArgs) for m, i := 0, 0; i < t.NumIn(); i++ { param := t.In(i) var v interface{} switch param.Kind() { case reflect.Slice: param = param.Elem() if param.String() == "gherkin.TabularData" { v = gherkin.StringData(arg).ToTable() } else if param.Kind() == reflect.Slice && param.Elem().Kind() == reflect.String { // just a raw [][]string slice v = gherkin.StringData(arg).ToTable() } case reflect.Ptr: if param.Elem().String() == "testing.T" { v = test } case reflect.Int: i, _ := strconv.ParseInt(match[m], 10, 32) v = int(i) m++ case reflect.Int64: v, _ = strconv.ParseInt(match[m], 10, 64) m++ case reflect.String: // this could be from `arg`, check match index if m >= len(match) { v = arg } else { v = match[m] m++ } case reflect.Float64: v, _ = strconv.ParseFloat(match[m], 64) m++ } if v == nil { panic("type " + t.String() + "is not supported.") } values[i] = reflect.ValueOf(v) } s.Function.Call(values) return true, nil } return false, nil }