support gherkin scenario outline and examples
read scenario outline related tokens in gherkin lexer * e8b71e6 test lexing of scenario outline with examples * 13f73f2 update gherkin parser to parse scenario outline with examples * cfad4fe test scenario outline persing
Этот коммит содержится в:
родитель
1cc5fde508
коммит
4afb53d310
6 изменённых файлов: 247 добавлений и 51 удалений
|
@ -87,11 +87,21 @@ func (t Tags) Has(tag Tag) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario describes the scenario details
|
// Scenario describes the scenario details
|
||||||
|
//
|
||||||
|
// if Examples table is not nil, then it
|
||||||
|
// means that this is an outline scenario
|
||||||
|
// with a table of examples to be run for
|
||||||
|
// each and every row
|
||||||
|
//
|
||||||
|
// Scenario may have tags which later may
|
||||||
|
// be used to filter out or run specific
|
||||||
|
// initialization tasks
|
||||||
type Scenario struct {
|
type Scenario struct {
|
||||||
Title string
|
Title string
|
||||||
Steps []*Step
|
Steps []*Step
|
||||||
Tags Tags
|
Tags Tags
|
||||||
Comment string
|
Examples *Table
|
||||||
|
Comment string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background steps are run before every scenario
|
// Background steps are run before every scenario
|
||||||
|
@ -243,34 +253,56 @@ func (p *parser) parseFeature() (ft *Feature, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// there may be tags before scenario
|
// there may be tags before scenario
|
||||||
sc := &Scenario{}
|
var tags Tags
|
||||||
sc.Tags = append(sc.Tags, ft.Tags...)
|
tags = append(tags, ft.Tags...)
|
||||||
if tok.Type == TAGS {
|
if tok.Type == TAGS {
|
||||||
for _, t := range p.parseTags() {
|
for _, t := range p.parseTags() {
|
||||||
if !sc.Tags.Has(t) {
|
if !tags.Has(t) {
|
||||||
sc.Tags = append(sc.Tags, t)
|
tags = append(tags, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tok = p.peek()
|
tok = p.peek()
|
||||||
}
|
}
|
||||||
|
|
||||||
// there must be a scenario otherwise
|
// there must be a scenario or scenario outline otherwise
|
||||||
if tok.Type != SCENARIO {
|
if !tok.OfType(SCENARIO, SCENARIO_OUTLINE) {
|
||||||
return ft, p.err("expected a scenario, but got '"+tok.Type.String()+"' instead", tok.Line)
|
return ft, p.err("expected a scenario or scenario outline, but got '"+tok.Type.String()+"' instead", tok.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
sc.Title = tok.Value
|
scenario, err := p.parseScenario()
|
||||||
sc.Comment = tok.Comment
|
if err != nil {
|
||||||
p.next() // jump to scenario steps
|
|
||||||
if sc.Steps, err = p.parseSteps(); err != nil {
|
|
||||||
return ft, err
|
return ft, err
|
||||||
}
|
}
|
||||||
ft.Scenarios = append(ft.Scenarios, sc)
|
|
||||||
|
scenario.Tags = tags
|
||||||
|
ft.Scenarios = append(ft.Scenarios, scenario)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ft, nil
|
return ft, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseScenario() (s *Scenario, err error) {
|
||||||
|
tok := p.next()
|
||||||
|
s = &Scenario{Title: tok.Value, Comment: tok.Comment}
|
||||||
|
if s.Steps, err = p.parseSteps(); err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
if examples := p.peek(); examples.Type == EXAMPLES {
|
||||||
|
p.next() // jump over the peeked token
|
||||||
|
peek := p.peek()
|
||||||
|
if peek.Type != TABLE_ROW {
|
||||||
|
return s, p.err(strings.Join([]string{
|
||||||
|
"expected a table row,",
|
||||||
|
"but got '" + peek.Type.String() + "' instead, for scenario outline examples",
|
||||||
|
}, " "), examples.Line)
|
||||||
|
}
|
||||||
|
if s.Examples, err = p.parseTable(); err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *parser) parseSteps() (steps []*Step, err error) {
|
func (p *parser) parseSteps() (steps []*Step, err error) {
|
||||||
for tok := p.peek(); tok.OfType(allSteps...); tok = p.peek() {
|
for tok := p.peek(); tok.OfType(allSteps...); tok = p.peek() {
|
||||||
step := &Step{Text: tok.Value, Comment: tok.Comment}
|
step := &Step{Text: tok.Value, Comment: tok.Comment}
|
||||||
|
@ -294,11 +326,11 @@ func (p *parser) parseSteps() (steps []*Step, err error) {
|
||||||
tok = p.peek()
|
tok = p.peek()
|
||||||
switch tok.Type {
|
switch tok.Type {
|
||||||
case PYSTRING:
|
case PYSTRING:
|
||||||
if err := p.parsePystring(step); err != nil {
|
if step.PyString, err = p.parsePystring(); err != nil {
|
||||||
return steps, err
|
return steps, err
|
||||||
}
|
}
|
||||||
case TABLE_ROW:
|
case TABLE_ROW:
|
||||||
if err := p.parseTable(step); err != nil {
|
if step.Table, err = p.parseTable(); err != nil {
|
||||||
return steps, err
|
return steps, err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -312,7 +344,7 @@ func (p *parser) parseSteps() (steps []*Step, err error) {
|
||||||
return steps, nil
|
return steps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parsePystring(s *Step) error {
|
func (p *parser) parsePystring() (*PyString, error) {
|
||||||
var tok *Token
|
var tok *Token
|
||||||
started := p.next() // skip the start of pystring
|
started := p.next() // skip the start of pystring
|
||||||
var lines []string
|
var lines []string
|
||||||
|
@ -320,27 +352,28 @@ func (p *parser) parsePystring(s *Step) error {
|
||||||
lines = append(lines, tok.Text)
|
lines = append(lines, tok.Text)
|
||||||
}
|
}
|
||||||
if tok.Type == EOF {
|
if tok.Type == EOF {
|
||||||
return fmt.Errorf("pystring which was opened on %s:%d was not closed", p.path, started.Line)
|
return nil, fmt.Errorf("pystring which was opened on %s:%d was not closed", p.path, started.Line)
|
||||||
}
|
}
|
||||||
s.PyString = &PyString{Body: strings.Join(lines, "\n")}
|
return &PyString{
|
||||||
return nil
|
Body: strings.Join(lines, "\n"),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseTable(s *Step) error {
|
func (p *parser) parseTable() (*Table, error) {
|
||||||
s.Table = &Table{}
|
tbl := &Table{}
|
||||||
for row := p.peek(); row.Type == TABLE_ROW; row = p.peek() {
|
for row := p.peek(); row.Type == TABLE_ROW; row = p.peek() {
|
||||||
var cols []string
|
var cols []string
|
||||||
for _, r := range strings.Split(strings.Trim(row.Value, "|"), "|") {
|
for _, r := range strings.Split(strings.Trim(row.Value, "|"), "|") {
|
||||||
cols = append(cols, strings.TrimFunc(r, unicode.IsSpace))
|
cols = append(cols, strings.TrimFunc(r, unicode.IsSpace))
|
||||||
}
|
}
|
||||||
// ensure the same colum number for each row
|
// ensure the same colum number for each row
|
||||||
if len(s.Table.rows) > 0 && len(s.Table.rows[0]) != len(cols) {
|
if len(tbl.rows) > 0 && len(tbl.rows[0]) != len(cols) {
|
||||||
return p.err("table row has not the same number of columns compared to previous row", row.Line)
|
return tbl, p.err("table row has not the same number of columns compared to previous row", row.Line)
|
||||||
}
|
}
|
||||||
s.Table.rows = append(s.Table.rows, cols)
|
tbl.rows = append(tbl.rows, cols)
|
||||||
p.next() // jump over the peeked token
|
p.next() // jump over the peeked token
|
||||||
}
|
}
|
||||||
return nil
|
return tbl, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseTags() (tags Tags) {
|
func (p *parser) parseTags() (tags Tags) {
|
||||||
|
|
|
@ -9,14 +9,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var matchers = map[string]*regexp.Regexp{
|
var matchers = map[string]*regexp.Regexp{
|
||||||
"feature": regexp.MustCompile("^(\\s*)Feature:\\s*([^#]*)(#.*)?"),
|
"feature": regexp.MustCompile("^(\\s*)Feature:\\s*([^#]*)(#.*)?"),
|
||||||
"scenario": regexp.MustCompile("^(\\s*)Scenario:\\s*([^#]*)(#.*)?"),
|
"scenario": regexp.MustCompile("^(\\s*)Scenario:\\s*([^#]*)(#.*)?"),
|
||||||
"background": regexp.MustCompile("^(\\s*)Background:(\\s*#.*)?"),
|
"scenario_outline": regexp.MustCompile("^(\\s*)Scenario Outline:\\s*([^#]*)(#.*)?"),
|
||||||
"step": regexp.MustCompile("^(\\s*)(Given|When|Then|And|But)\\s+([^#]*)(#.*)?"),
|
"examples": regexp.MustCompile("^(\\s*)Examples:(\\s*#.*)?"),
|
||||||
"comment": regexp.MustCompile("^(\\s*)#(.+)"),
|
"background": regexp.MustCompile("^(\\s*)Background:(\\s*#.*)?"),
|
||||||
"pystring": regexp.MustCompile("^(\\s*)\\\"\\\"\\\""),
|
"step": regexp.MustCompile("^(\\s*)(Given|When|Then|And|But)\\s+([^#]*)(#.*)?"),
|
||||||
"tags": regexp.MustCompile("^(\\s*)@([^#]*)(#.*)?"),
|
"comment": regexp.MustCompile("^(\\s*)#(.+)"),
|
||||||
"table_row": regexp.MustCompile("^(\\s*)\\|([^#]*)(#.*)?"),
|
"pystring": regexp.MustCompile("^(\\s*)\\\"\\\"\\\""),
|
||||||
|
"tags": regexp.MustCompile("^(\\s*)@([^#]*)(#.*)?"),
|
||||||
|
"table_row": regexp.MustCompile("^(\\s*)\\|([^#]*)(#.*)?"),
|
||||||
}
|
}
|
||||||
|
|
||||||
type lexer struct {
|
type lexer struct {
|
||||||
|
@ -145,6 +147,27 @@ func (l *lexer) read() *Token {
|
||||||
Comment: strings.Trim(m[3], " #"),
|
Comment: strings.Trim(m[3], " #"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// scenario outline
|
||||||
|
if m := matchers["scenario_outline"].FindStringSubmatch(line); len(m) > 0 {
|
||||||
|
return &Token{
|
||||||
|
Type: SCENARIO_OUTLINE,
|
||||||
|
Indent: len(m[1]),
|
||||||
|
Line: l.lines - 1,
|
||||||
|
Value: strings.TrimSpace(m[2]),
|
||||||
|
Text: line,
|
||||||
|
Comment: strings.Trim(m[3], " #"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// examples
|
||||||
|
if m := matchers["examples"].FindStringSubmatch(line); len(m) > 0 {
|
||||||
|
return &Token{
|
||||||
|
Type: EXAMPLES,
|
||||||
|
Indent: len(m[1]),
|
||||||
|
Line: l.lines - 1,
|
||||||
|
Text: line,
|
||||||
|
Comment: strings.Trim(m[2], " #"),
|
||||||
|
}
|
||||||
|
}
|
||||||
// text
|
// text
|
||||||
text := strings.TrimLeftFunc(line, unicode.IsSpace)
|
text := strings.TrimLeftFunc(line, unicode.IsSpace)
|
||||||
return &Token{
|
return &Token{
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var lexerSamples = map[string]string{
|
var testLexerSamples = map[string]string{
|
||||||
"feature": `Feature: gherkin lexer
|
"feature": `Feature: gherkin lexer
|
||||||
in order to run features
|
in order to run features
|
||||||
as gherkin lexer
|
as gherkin lexer
|
||||||
|
@ -27,10 +27,22 @@ var lexerSamples = map[string]string{
|
||||||
| name | lastname | num |
|
| name | lastname | num |
|
||||||
| Jack | Sparrow | 4 |
|
| Jack | Sparrow | 4 |
|
||||||
| John | Doe | 79 |`,
|
| John | Doe | 79 |`,
|
||||||
|
|
||||||
|
"scenario_outline_with_examples": `Scenario Outline: ls supports kinds of options
|
||||||
|
Given I am in a directory "test"
|
||||||
|
And I have a file named "foo"
|
||||||
|
And I have a file named "bar"
|
||||||
|
When I run "ls" with options "<options>"
|
||||||
|
Then I should see "<result>"
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| options | result |
|
||||||
|
| -t | bar foo |
|
||||||
|
| -tr | foo bar |`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_feature_read(t *testing.T) {
|
func Test_feature_read(t *testing.T) {
|
||||||
l := newLexer(strings.NewReader(lexerSamples["feature"]))
|
l := newLexer(strings.NewReader(testLexerSamples["feature"]))
|
||||||
tok := l.read()
|
tok := l.read()
|
||||||
if tok.Type != FEATURE {
|
if tok.Type != FEATURE {
|
||||||
t.Fatalf("Expected a 'feature' type, but got: '%s'", tok.Type)
|
t.Fatalf("Expected a 'feature' type, but got: '%s'", tok.Type)
|
||||||
|
@ -99,16 +111,16 @@ func Test_feature_read(t *testing.T) {
|
||||||
|
|
||||||
func Test_minimal_feature(t *testing.T) {
|
func Test_minimal_feature(t *testing.T) {
|
||||||
file := strings.Join([]string{
|
file := strings.Join([]string{
|
||||||
lexerSamples["feature"] + "\n",
|
testLexerSamples["feature"] + "\n",
|
||||||
|
|
||||||
indent(2, lexerSamples["background"]),
|
indent(2, testLexerSamples["background"]),
|
||||||
indent(4, lexerSamples["step_given"]) + "\n",
|
indent(4, testLexerSamples["step_given"]) + "\n",
|
||||||
|
|
||||||
indent(2, lexerSamples["comment"]),
|
indent(2, testLexerSamples["comment"]),
|
||||||
indent(2, lexerSamples["scenario"]),
|
indent(2, testLexerSamples["scenario"]),
|
||||||
indent(4, lexerSamples["step_given"]),
|
indent(4, testLexerSamples["step_given"]),
|
||||||
indent(4, lexerSamples["step_when"]),
|
indent(4, testLexerSamples["step_when"]),
|
||||||
indent(4, lexerSamples["step_then"]),
|
indent(4, testLexerSamples["step_then"]),
|
||||||
}, "\n")
|
}, "\n")
|
||||||
l := newLexer(strings.NewReader(file))
|
l := newLexer(strings.NewReader(file))
|
||||||
|
|
||||||
|
@ -142,9 +154,9 @@ func Test_minimal_feature(t *testing.T) {
|
||||||
|
|
||||||
func Test_table_row_reading(t *testing.T) {
|
func Test_table_row_reading(t *testing.T) {
|
||||||
file := strings.Join([]string{
|
file := strings.Join([]string{
|
||||||
indent(2, lexerSamples["background"]),
|
indent(2, testLexerSamples["background"]),
|
||||||
indent(4, lexerSamples["step_given_table"]),
|
indent(4, testLexerSamples["step_given_table"]),
|
||||||
indent(4, lexerSamples["step_given"]),
|
indent(4, testLexerSamples["step_given"]),
|
||||||
}, "\n")
|
}, "\n")
|
||||||
l := newLexer(strings.NewReader(file))
|
l := newLexer(strings.NewReader(file))
|
||||||
|
|
||||||
|
@ -179,3 +191,31 @@ func Test_table_row_reading(t *testing.T) {
|
||||||
t.Fatalf("table row value '%s' was not expected", values[2])
|
t.Fatalf("table row value '%s' was not expected", values[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_lexing_of_scenario_outline(t *testing.T) {
|
||||||
|
l := newLexer(strings.NewReader(testLexerSamples["scenario_outline_with_examples"]))
|
||||||
|
|
||||||
|
var tokens []TokenType
|
||||||
|
for tok := l.read(); tok.Type != EOF; tok = l.read() {
|
||||||
|
tokens = append(tokens, tok.Type)
|
||||||
|
}
|
||||||
|
expected := []TokenType{
|
||||||
|
SCENARIO_OUTLINE,
|
||||||
|
GIVEN,
|
||||||
|
AND,
|
||||||
|
AND,
|
||||||
|
WHEN,
|
||||||
|
THEN,
|
||||||
|
NEW_LINE,
|
||||||
|
|
||||||
|
EXAMPLES,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
|
}
|
||||||
|
for i := 0; i < len(expected); i++ {
|
||||||
|
if expected[i] != tokens[i] {
|
||||||
|
t.Fatalf("expected token '%s' at position: %d, is not the same as actual token: '%s'", expected[i], i, tokens[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,9 @@ func Test_parse_feature_file(t *testing.T) {
|
||||||
testStepSamples["step_group_another"] + "\n",
|
testStepSamples["step_group_another"] + "\n",
|
||||||
// scenario - no steps yet
|
// scenario - no steps yet
|
||||||
indent(2, "@todo"), // cust - tag is repeated
|
indent(2, "@todo"), // cust - tag is repeated
|
||||||
indent(2, "Scenario: user is able to reset his password"),
|
indent(2, "Scenario: user is able to reset his password") + "\n",
|
||||||
|
// scenario outline
|
||||||
|
testLexerSamples["scenario_outline_with_examples"],
|
||||||
}, "\n")
|
}, "\n")
|
||||||
|
|
||||||
p := &parser{
|
p := &parser{
|
||||||
|
@ -79,9 +81,22 @@ func Test_parse_feature_file(t *testing.T) {
|
||||||
|
|
||||||
TAGS,
|
TAGS,
|
||||||
SCENARIO,
|
SCENARIO,
|
||||||
|
NEW_LINE,
|
||||||
|
|
||||||
|
SCENARIO_OUTLINE,
|
||||||
|
GIVEN,
|
||||||
|
AND,
|
||||||
|
AND,
|
||||||
|
WHEN,
|
||||||
|
THEN,
|
||||||
|
NEW_LINE,
|
||||||
|
EXAMPLES,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
}, t)
|
}, t)
|
||||||
|
|
||||||
ft.assertHasNumScenarios(3, t)
|
ft.assertHasNumScenarios(4, t)
|
||||||
|
|
||||||
ft.Scenarios[0].assertHasNumTags(2, t)
|
ft.Scenarios[0].assertHasNumTags(2, t)
|
||||||
ft.Scenarios[0].assertHasTag("global-one", t)
|
ft.Scenarios[0].assertHasTag("global-one", t)
|
||||||
|
@ -96,4 +111,6 @@ func Test_parse_feature_file(t *testing.T) {
|
||||||
ft.Scenarios[2].assertHasTag("global-one", t)
|
ft.Scenarios[2].assertHasTag("global-one", t)
|
||||||
ft.Scenarios[2].assertHasTag("cust", t)
|
ft.Scenarios[2].assertHasTag("cust", t)
|
||||||
ft.Scenarios[2].assertHasTag("todo", t)
|
ft.Scenarios[2].assertHasTag("todo", t)
|
||||||
|
|
||||||
|
ft.Scenarios[3].assertHasNumTags(2, t)
|
||||||
}
|
}
|
||||||
|
|
77
gherkin/scenario_test.go
Обычный файл
77
gherkin/scenario_test.go
Обычный файл
|
@ -0,0 +1,77 @@
|
||||||
|
package gherkin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Scenario) assertTitle(title string, t *testing.T) {
|
||||||
|
if s.Title != title {
|
||||||
|
t.Fatalf("expected scenario title to be '%s', but got '%s'", title, s.Title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scenario) assertStep(text string, t *testing.T) *Step {
|
||||||
|
for _, stp := range s.Steps {
|
||||||
|
if stp.Text == text {
|
||||||
|
return stp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatal("expected scenario '%s' to have step: '%s', but it did not", s.Title, text)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scenario) assertExampleRow(t *testing.T, num int, cols ...string) {
|
||||||
|
if s.Examples == nil {
|
||||||
|
t.Fatalf("outline scenario '%s' has no examples", s.Title)
|
||||||
|
}
|
||||||
|
if len(s.Examples.rows) <= num {
|
||||||
|
t.Fatalf("outline scenario '%s' table has no row: %d", s.Title, num)
|
||||||
|
}
|
||||||
|
if len(s.Examples.rows[num]) != len(cols) {
|
||||||
|
t.Fatalf("outline scenario '%s' table row length, does not match expected: %d", s.Title, len(cols))
|
||||||
|
}
|
||||||
|
for i, col := range s.Examples.rows[num] {
|
||||||
|
if col != cols[i] {
|
||||||
|
t.Fatalf("outline scenario '%s' table row %d, column %d - value '%s', does not match expected: %s", s.Title, num, i, col, cols[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_parse_scenario_outline(t *testing.T) {
|
||||||
|
|
||||||
|
p := &parser{
|
||||||
|
lx: newLexer(strings.NewReader(testLexerSamples["scenario_outline_with_examples"])),
|
||||||
|
path: "usual.feature",
|
||||||
|
ast: newAST(),
|
||||||
|
}
|
||||||
|
s, err := p.parseScenario()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
s.assertTitle("ls supports kinds of options", t)
|
||||||
|
|
||||||
|
p.ast.assertMatchesTypes([]TokenType{
|
||||||
|
SCENARIO_OUTLINE,
|
||||||
|
GIVEN,
|
||||||
|
AND,
|
||||||
|
AND,
|
||||||
|
WHEN,
|
||||||
|
THEN,
|
||||||
|
NEW_LINE,
|
||||||
|
EXAMPLES,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
|
TABLE_ROW,
|
||||||
|
}, t)
|
||||||
|
|
||||||
|
s.assertStep(`I am in a directory "test"`, t)
|
||||||
|
s.assertStep(`I have a file named "foo"`, t)
|
||||||
|
s.assertStep(`I have a file named "bar"`, t)
|
||||||
|
s.assertStep(`I run "ls" with options "<options>"`, t)
|
||||||
|
s.assertStep(`I should see "<result>"`, t)
|
||||||
|
|
||||||
|
s.assertExampleRow(t, 0, "options", "result")
|
||||||
|
s.assertExampleRow(t, 1, "-t", "bar foo")
|
||||||
|
s.assertExampleRow(t, 2, "-tr", "foo bar")
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ const (
|
||||||
FEATURE
|
FEATURE
|
||||||
BACKGROUND
|
BACKGROUND
|
||||||
SCENARIO
|
SCENARIO
|
||||||
|
SCENARIO_OUTLINE
|
||||||
|
EXAMPLES
|
||||||
|
|
||||||
steps
|
steps
|
||||||
GIVEN
|
GIVEN
|
||||||
|
@ -51,6 +53,10 @@ func (t TokenType) String() string {
|
||||||
return "background"
|
return "background"
|
||||||
case SCENARIO:
|
case SCENARIO:
|
||||||
return "scenario"
|
return "scenario"
|
||||||
|
case SCENARIO_OUTLINE:
|
||||||
|
return "scenario outline"
|
||||||
|
case EXAMPLES:
|
||||||
|
return "examples"
|
||||||
case GIVEN:
|
case GIVEN:
|
||||||
return "given step"
|
return "given step"
|
||||||
case WHEN:
|
case WHEN:
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче