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
}
Esempio n. 2
0
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()
		}
	}
}