diff --git a/.circleci/config.yml b/.circleci/config.yml index c78c0e4..fa800ff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,6 +23,7 @@ commands: description: "Run go vet" steps: - run: go vet github.com/cucumber/godog + - run: go vet github.com/cucumber/godog/gherkin - run: go vet github.com/cucumber/godog/colors fmt: description: "Run go fmt" diff --git a/_examples/api/README.md b/_examples/api/README.md index b9e0222..0669e33 100644 --- a/_examples/api/README.md +++ b/_examples/api/README.md @@ -56,8 +56,8 @@ need to store state within steps (a response), we should introduce a structure w package main import ( - "github.com/cucumber/gherkin-go/v9" "github.com/cucumber/godog" + "github.com/cucumber/godog/gherkin" ) type apiFeature struct { @@ -71,7 +71,7 @@ func (a *apiFeature) theResponseCodeShouldBe(code int) error { return godog.ErrPending } -func (a *apiFeature) theResponseShouldMatchJSON(body *messages.PickleStepArgument_PickleDocString) error { +func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) error { return godog.ErrPending } @@ -98,8 +98,8 @@ import ( "net/http" "net/http/httptest" - "github.com/cucumber/gherkin-go/v9" "github.com/cucumber/godog" + "github.com/cucumber/godog/gherkin" ) type apiFeature struct { @@ -142,7 +142,7 @@ func (a *apiFeature) theResponseCodeShouldBe(code int) error { return nil } -func (a *apiFeature) theResponseShouldMatchJSON(body *messages.PickleStepArgument_PickleDocString) error { +func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) (err error) { var expected, actual []byte var data interface{} if err = json.Unmarshal([]byte(body.Content), &data); err != nil { diff --git a/_examples/api/api_test.go b/_examples/api/api_test.go index d6af045..4a24813 100644 --- a/_examples/api/api_test.go +++ b/_examples/api/api_test.go @@ -8,14 +8,14 @@ import ( "reflect" "github.com/cucumber/godog" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) type apiFeature struct { resp *httptest.ResponseRecorder } -func (a *apiFeature) resetResponse(*messages.Pickle) { +func (a *apiFeature) resetResponse(interface{}) { a.resp = httptest.NewRecorder() } @@ -51,7 +51,7 @@ func (a *apiFeature) theResponseCodeShouldBe(code int) error { return nil } -func (a *apiFeature) theResponseShouldMatchJSON(body *messages.PickleStepArgument_PickleDocString) (err error) { +func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) (err error) { var expected, actual interface{} // re-encode expected response diff --git a/_examples/db/api_test.go b/_examples/db/api_test.go index 98a03d0..3e980ca 100644 --- a/_examples/db/api_test.go +++ b/_examples/db/api_test.go @@ -11,7 +11,7 @@ import ( txdb "github.com/DATA-DOG/go-txdb" "github.com/cucumber/godog" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) func init() { @@ -24,7 +24,7 @@ type apiFeature struct { resp *httptest.ResponseRecorder } -func (a *apiFeature) resetResponse(*messages.Pickle) { +func (a *apiFeature) resetResponse(interface{}) { a.resp = httptest.NewRecorder() if a.db != nil { a.db.Close() @@ -71,7 +71,7 @@ func (a *apiFeature) theResponseCodeShouldBe(code int) error { return nil } -func (a *apiFeature) theResponseShouldMatchJSON(body *messages.PickleStepArgument_PickleDocString) (err error) { +func (a *apiFeature) theResponseShouldMatchJSON(body *gherkin.DocString) (err error) { var expected, actual interface{} // re-encode expected response @@ -91,7 +91,7 @@ func (a *apiFeature) theResponseShouldMatchJSON(body *messages.PickleStepArgumen return nil } -func (a *apiFeature) thereAreUsers(users *messages.PickleStepArgument_PickleTable) error { +func (a *apiFeature) thereAreUsers(users *gherkin.DataTable) error { var fields []string var marks []string head := users.Rows[0].Cells diff --git a/_examples/godogs/godogs_test.go b/_examples/godogs/godogs_test.go index d9cd0eb..44d2aa3 100644 --- a/_examples/godogs/godogs_test.go +++ b/_examples/godogs/godogs_test.go @@ -9,7 +9,6 @@ import ( "github.com/cucumber/godog" "github.com/cucumber/godog/colors" - messages "github.com/cucumber/messages-go/v9" ) var opt = godog.Options{Output: colors.Colored(os.Stdout)} @@ -57,7 +56,7 @@ func FeatureContext(s *godog.Suite) { s.Step(`^I eat (\d+)$`, iEat) s.Step(`^there should be (\d+) remaining$`, thereShouldBeRemaining) - s.BeforeScenario(func(*messages.Pickle) { + s.BeforeScenario(func(interface{}) { Godogs = 0 // clean the state before every scenario }) } diff --git a/builder_go112_test.go b/builder_go112_test.go deleted file mode 100644 index 867a2b6..0000000 --- a/builder_go112_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build go1.12 -// +build !go1.13 - -package godog - -import ( - "bytes" - "os" - "path/filepath" - "testing" -) - -func TestGodogBuildWithVendoredGodogAndMod(t *testing.T) { - gopath := filepath.Join(os.TempDir(), "_gpc") - dir := filepath.Join(gopath, "src", "godogs") - err := buildTestPackage(dir, map[string]string{ - "godogs.feature": builderFeatureFile, - "godogs.go": builderMainCodeFile, - "godogs_test.go": builderTestFile, - "go.mod": builderModFile, - }) - if err != nil { - os.RemoveAll(gopath) - t.Fatal(err) - } - defer os.RemoveAll(gopath) - - pkg := filepath.Join(dir, "vendor", "github.com", "cucumber") - if err := os.MkdirAll(pkg, 0755); err != nil { - t.Fatal(err) - } - - prevDir, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - // symlink godog package - if err := os.Symlink(prevDir, filepath.Join(pkg, "godog")); err != nil { - t.Fatal(err) - } - - if err := os.Chdir(dir); err != nil { - t.Fatal(err) - } - defer os.Chdir(prevDir) - - cmd := buildTestCommand(t, "godogs.feature") - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Env = append(envVarsWithoutGopath(), "GOPATH="+gopath) - - if err := cmd.Run(); err != nil { - t.Log(stdout.String()) - t.Log(stderr.String()) - t.Fatal(err) - } -} diff --git a/builder_go113_test.go b/builder_go113_test.go deleted file mode 100644 index 9c881c3..0000000 --- a/builder_go113_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// +build go1.13 - -package godog - -import ( - "bytes" - "os" - "os/exec" - "path/filepath" - "testing" -) - -func TestGodogBuildWithVendoredGodogAndMod(t *testing.T) { - gopath := filepath.Join(os.TempDir(), "_gpc") - dir := filepath.Join(gopath, "src", "godogs") - err := buildTestPackage(dir, map[string]string{ - "godogs.feature": builderFeatureFile, - "godogs.go": builderMainCodeFile, - "godogs_test.go": builderTestFile, - "go.mod": builderModFile, - }) - if err != nil { - os.RemoveAll(gopath) - t.Fatal(err) - } - defer os.RemoveAll(gopath) - - prevDir, err := os.Getwd() - if err != nil { - t.Fatal(err) - } - - if err = exec.Command("go", "mod", "vendor").Run(); err != nil { - t.Fatal(err) - } - - if err := os.Chdir(dir); err != nil { - t.Fatal(err) - } - defer os.Chdir(prevDir) - - cmd := buildTestCommand(t, "godogs.feature") - - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Env = append(envVarsWithoutGopath(), "GOPATH="+gopath) - - if err := cmd.Run(); err != nil { - t.Log(stdout.String()) - t.Log(stderr.String()) - t.Fatal(err) - } -} diff --git a/builder_test.go b/builder_test.go index f623bd8..327b502 100644 --- a/builder_test.go +++ b/builder_test.go @@ -310,6 +310,55 @@ func TestGodogBuildWithinGopath(t *testing.T) { } } +func TestGodogBuildWithVendoredGodogAndMod(t *testing.T) { + gopath := filepath.Join(os.TempDir(), "_gpc") + dir := filepath.Join(gopath, "src", "godogs") + err := buildTestPackage(dir, map[string]string{ + "godogs.feature": builderFeatureFile, + "godogs.go": builderMainCodeFile, + "godogs_test.go": builderTestFile, + "go.mod": builderModFile, + }) + if err != nil { + os.RemoveAll(gopath) + t.Fatal(err) + } + defer os.RemoveAll(gopath) + + pkg := filepath.Join(dir, "vendor", "github.com", "cucumber") + if err := os.MkdirAll(pkg, 0755); err != nil { + t.Fatal(err) + } + + prevDir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + + // symlink godog package + if err := os.Symlink(prevDir, filepath.Join(pkg, "godog")); err != nil { + t.Fatal(err) + } + + if err := os.Chdir(dir); err != nil { + t.Fatal(err) + } + defer os.Chdir(prevDir) + + cmd := buildTestCommand(t, "godogs.feature") + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + cmd.Env = append(envVarsWithoutGopath(), "GOPATH="+gopath) + + if err := cmd.Run(); err != nil { + t.Log(stdout.String()) + t.Log(stderr.String()) + t.Fatal(err) + } +} + func TestGodogBuildWithVendoredGodogWithoutModule(t *testing.T) { gopath := filepath.Join(os.TempDir(), "_gp") dir := filepath.Join(gopath, "src", "godogs") diff --git a/color_tag_test.go b/color_tag_test.go index 5841c36..18d6e71 100644 --- a/color_tag_test.go +++ b/color_tag_test.go @@ -181,7 +181,7 @@ func (cw *tagColorWriter) Write(p []byte) (int, error) { } if cw.state == outsideCsiCode { - nw, err = cw.w.Write(p[first:]) + nw, err = cw.w.Write(p[first:len(p)]) r += nw } diff --git a/colors/ansi_windows.go b/colors/ansi_windows.go index d80f846..66401f9 100644 --- a/colors/ansi_windows.go +++ b/colors/ansi_windows.go @@ -409,7 +409,7 @@ func (cw *ansiColorWriter) Write(p []byte) (int, error) { } if cw.mode != discardNonColorEscSeq || cw.state == outsideCsiCode { - nw, err = cw.w.Write(p[first:]) + nw, err = cw.w.Write(p[first:len(p)]) r += nw } diff --git a/features/load.feature b/features/load.feature index 3bc2865..9425f61 100644 --- a/features/load.feature +++ b/features/load.feature @@ -40,7 +40,7 @@ Feature: load features | feature | number | | features/load.feature:3 | 0 | | features/load.feature:6 | 1 | - | features/load.feature | 6 | + | features/load.feature | 4 | Scenario: load a number of feature files Given a feature path "features/load.feature" diff --git a/features/snippets.feature b/features/snippets.feature index f2bc607..af77def 100644 --- a/features/snippets.feature +++ b/features/snippets.feature @@ -48,7 +48,7 @@ Feature: undefined step snippets When I run feature suite Then the undefined step snippets should be: """ - func iSendRequestToWith(arg1, arg2 string, arg3 *messages.PickleStepArgument_PickleTable) error { + func iSendRequestToWith(arg1, arg2 string, arg3 *gherkin.DataTable) error { return godog.ErrPending } diff --git a/fixtures/cucumber_output.json b/fixtures/cucumber_output.json new file mode 100644 index 0000000..54f419d --- /dev/null +++ b/fixtures/cucumber_output.json @@ -0,0 +1,5640 @@ +[ + { + "uri": "features/background.feature", + "id": "run-background", + "keyword": "Feature", + "name": "run background", + "description": " In order to test application behavior\n As a test suite\n I need to be able to run background correctly", + "line": 1, + "elements": [ + { + "id": "run-background;should-run-background-steps", + "keyword": "Scenario", + "name": "should run background steps", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: with background\n\n Background:\n Given a feature path \"features/load.feature:6\"\n\n Scenario: parse a scenario\n When I parse features\n Then I should have 1 scenario registered", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 18, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 19, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 20, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"\nI parse features\nI should have 1 scenario registered", + "content_type": "", + "line": 21 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-background;should-skip-all-consequent-steps-on-failure", + "keyword": "Scenario", + "name": "should skip all consequent steps on failure", + "description": "", + "line": 27, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 28, + "doc_string": { + "value": "Feature: with background\n\n Background:\n Given a failing step\n And a feature path \"features/load.feature:6\"\n\n Scenario: parse a scenario\n When I parse features\n Then I should have 1 scenario registered", + "content_type": "", + "line": 29 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 40, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 41, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be failed:", + "line": 42, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 43 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 46, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"\nI parse features\nI should have 1 scenario registered", + "content_type": "", + "line": 47 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-background;should-continue-undefined-steps", + "keyword": "Scenario", + "name": "should continue undefined steps", + "description": "", + "line": 53, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 54, + "doc_string": { + "value": "Feature: with background\n\n Background:\n Given an undefined step\n\n Scenario: parse a scenario\n When I do undefined action\n Then I should have 1 scenario registered", + "content_type": "", + "line": 55 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 65, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 66, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 67, + "doc_string": { + "value": "an undefined step\nI do undefined action", + "content_type": "", + "line": 68 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 72, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 73 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/events.feature", + "id": "suite-events", + "keyword": "Feature", + "name": "suite events", + "description": " In order to run tasks before and after important events\n As a test suite\n I need to provide a way to hook into these events", + "line": 1, + "elements": [ + { + "id": "suite-events;triggers-before-scenario-event", + "keyword": "Scenario", + "name": "triggers before scenario event", + "description": "", + "line": 9, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:6\"", + "line": 10, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 11, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "there was event triggered before scenario \"load features within path\"", + "line": 12, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "suite-events;triggers-appropriate-events-for-a-single-scenario", + "keyword": "Scenario", + "name": "triggers appropriate events for a single scenario", + "description": "", + "line": 14, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:6\"", + "line": 15, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 16, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "these events had to be fired for a number of times:", + "line": 17, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + }, + "rows": [ + { + "cells": [ + "BeforeSuite", + "1" + ] + }, + { + "cells": [ + "BeforeFeature", + "1" + ] + }, + { + "cells": [ + "BeforeScenario", + "1" + ] + }, + { + "cells": [ + "BeforeStep", + "3" + ] + }, + { + "cells": [ + "AfterStep", + "3" + ] + }, + { + "cells": [ + "AfterScenario", + "1" + ] + }, + { + "cells": [ + "AfterFeature", + "1" + ] + }, + { + "cells": [ + "AfterSuite", + "1" + ] + } + ] + } + ] + }, + { + "id": "suite-events;triggers-appropriate-events-whole-feature", + "keyword": "Scenario", + "name": "triggers appropriate events whole feature", + "description": "", + "line": 27, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature\"", + "line": 28, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 29, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "these events had to be fired for a number of times:", + "line": 30, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + }, + "rows": [ + { + "cells": [ + "BeforeSuite", + "1" + ] + }, + { + "cells": [ + "BeforeFeature", + "1" + ] + }, + { + "cells": [ + "BeforeScenario", + "6" + ] + }, + { + "cells": [ + "BeforeStep", + "19" + ] + }, + { + "cells": [ + "AfterStep", + "19" + ] + }, + { + "cells": [ + "AfterScenario", + "6" + ] + }, + { + "cells": [ + "AfterFeature", + "1" + ] + }, + { + "cells": [ + "AfterSuite", + "1" + ] + } + ] + } + ] + }, + { + "id": "suite-events;triggers-appropriate-events-for-two-feature-files", + "keyword": "Scenario", + "name": "triggers appropriate events for two feature files", + "description": "", + "line": 40, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:6\"", + "line": 41, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "a feature path \"features/multistep.feature:6\"", + "line": 42, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 43, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "these events had to be fired for a number of times:", + "line": 44, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + }, + "rows": [ + { + "cells": [ + "BeforeSuite", + "1" + ] + }, + { + "cells": [ + "BeforeFeature", + "2" + ] + }, + { + "cells": [ + "BeforeScenario", + "2" + ] + }, + { + "cells": [ + "BeforeStep", + "7" + ] + }, + { + "cells": [ + "AfterStep", + "7" + ] + }, + { + "cells": [ + "AfterScenario", + "2" + ] + }, + { + "cells": [ + "AfterFeature", + "2" + ] + }, + { + "cells": [ + "AfterSuite", + "1" + ] + } + ] + } + ] + }, + { + "id": "suite-events;should-not-trigger-events-on-empty-feature", + "keyword": "Scenario", + "name": "should not trigger events on empty feature", + "description": "", + "line": 54, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 55, + "doc_string": { + "value": "Feature: empty\n\n Scenario: one\n\n Scenario: two", + "content_type": "", + "line": 56 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 63, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "these events had to be fired for a number of times:", + "line": 64, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + }, + "rows": [ + { + "cells": [ + "BeforeSuite", + "1" + ] + }, + { + "cells": [ + "BeforeFeature", + "0" + ] + }, + { + "cells": [ + "BeforeScenario", + "0" + ] + }, + { + "cells": [ + "BeforeStep", + "0" + ] + }, + { + "cells": [ + "AfterStep", + "0" + ] + }, + { + "cells": [ + "AfterScenario", + "0" + ] + }, + { + "cells": [ + "AfterFeature", + "0" + ] + }, + { + "cells": [ + "AfterSuite", + "1" + ] + } + ] + } + ] + }, + { + "id": "suite-events;should-not-trigger-events-on-empty-scenarios", + "keyword": "Scenario", + "name": "should not trigger events on empty scenarios", + "description": "", + "line": 74, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "I'm listening to suite events", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 75, + "doc_string": { + "value": "Feature: half empty\n\n Scenario: one\n\n Scenario: two\n Then passing step\n\n Scenario Outline: three\n Then passing step\n\n Examples:\n | a |\n | 1 |", + "content_type": "", + "line": 76 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 91, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "these events had to be fired for a number of times:", + "line": 92, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + }, + "rows": [ + { + "cells": [ + "BeforeSuite", + "1" + ] + }, + { + "cells": [ + "BeforeFeature", + "1" + ] + }, + { + "cells": [ + "BeforeScenario", + "2" + ] + }, + { + "cells": [ + "BeforeStep", + "2" + ] + }, + { + "cells": [ + "AfterStep", + "2" + ] + }, + { + "cells": [ + "AfterScenario", + "2" + ] + }, + { + "cells": [ + "AfterFeature", + "1" + ] + }, + { + "cells": [ + "AfterSuite", + "1" + ] + } + ] + } + ] + } + ] + }, + { + "uri": "features/formatter/cucumber.feature", + "id": "cucumber-json-formatter", + "keyword": "Feature", + "name": "cucumber json formatter", + "description": " In order to support tools that import cucumber json output\n I need to be able to support cucumber json formatted output", + "line": 1, + "comments": [ + { + "value": "# Currently godog only supports comments on Feature and not", + "line": 365 + }, + { + "value": "# scenario and steps.", + "line": 366 + } + ], + "elements": [ + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-node", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node", + "description": "", + "line": 5, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 6, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 7 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 13, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 14, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 3,\n \"type\": \"scenario\"\n }\n ]\n }\n ]", + "content_type": "application/json", + "line": 15 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-node-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node With Tags", + "description": "", + "line": 38, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 39, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n @TAG2 @TAG3\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 40 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 48, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 49, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 2,\n \"tags\": [\n {\n \"name\": \"@TAG1\",\n \"line\": 1\n }\n ],\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 5,\n \"type\": \"scenario\",\n \"tags\": [\n {\n \"name\": \"@TAG1\",\n \"line\": 1\n },\n {\n \"name\": \"@TAG2\",\n \"line\": 4\n },\n {\n \"name\": \"@TAG3\",\n \"line\": 4\n }\n ]\n }\n ]\n }\n]", + "content_type": "application/json", + "line": 50 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-outline", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline", + "description": "", + "line": 92, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 93, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 94 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 106, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 107, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;2\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 9,\n \"type\": \"scenario\"\n },\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;3\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 10,\n \"type\": \"scenario\"\n }\n ]\n }\n ]", + "content_type": "", + "line": 108 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-outline-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Tags", + "description": "", + "line": 139, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 140, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n\n @TAG2\n Scenario Outline: simple scenario\n simple scenario description\n\n @TAG3\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 141 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 156, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 157, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 2,\n \"tags\": [\n {\n \"name\": \"@TAG1\",\n \"line\": 1\n }\n ],\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;2\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 12,\n \"type\": \"scenario\",\n \"tags\": [\n {\n \"name\": \"@TAG1\",\n \"line\": 1\n },\n {\n \"name\": \"@TAG2\",\n \"line\": 5\n },\n {\n \"name\": \"@TAG3\",\n \"line\": 9\n }\n ]\n },\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;3\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 13,\n \"type\": \"scenario\",\n \"tags\": [\n {\n \"name\": \"@TAG1\",\n \"line\": 1\n },\n {\n \"name\": \"@TAG2\",\n \"line\": 5\n },\n {\n \"name\": \"@TAG3\",\n \"line\": 9\n }\n ]\n }\n ]\n }\n ]", + "content_type": "", + "line": 158 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario With Steps", + "description": "", + "line": 222, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 223, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n Then a failing step\n", + "content_type": "", + "line": 224 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 235, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 236, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 4,\n \"type\": \"scenario\",\n \"steps\": [\n {\n \"keyword\": \"Given \",\n \"name\": \"passing step\",\n \"line\": 7,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"passed\",\n \"duration\": 0\n }\n },\n {\n \"keyword\": \"Then \",\n \"name\": \"a failing step\",\n \"line\": 8,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"failed\",\n \"error_message\": \"intentional failure\",\n \"duration\": 0\n }\n }\n ]\n }\n ]\n }\n ]", + "content_type": "", + "line": 237 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-feature-plus-scenario-outline-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Steps", + "description": "", + "line": 286, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 287, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Given \u003cstatus\u003e step\n\n Examples: simple examples\n | status |\n | passing |\n | failing |\n", + "content_type": "", + "line": 288 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 303, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 304, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;2\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 11,\n \"type\": \"scenario\",\n \"steps\": [\n {\n \"keyword\": \"Given \",\n \"name\": \"passing step\",\n \"line\": 11,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"passed\",\n \"duration\": 0\n }\n }\n ]\n },\n {\n \"id\": \"simple-feature;simple-scenario;simple-examples;3\",\n \"keyword\": \"Scenario Outline\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 12,\n \"type\": \"scenario\",\n \"steps\": [\n {\n \"keyword\": \"Given \",\n \"name\": \"failing step\",\n \"line\": 12,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"failed\",\n \"error_message\": \"intentional failure\",\n \"duration\": 0\n }\n }\n ]\n }\n ]\n }\n ]", + "content_type": "", + "line": 305 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-comments", + "keyword": "Scenario", + "name": "Support of Comments", + "description": "", + "line": 367, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 368, + "doc_string": { + "value": " #Feature comment\n Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description", + "content_type": "", + "line": 369 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 377, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 378, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple description\",\n \"line\": 2,\n \"comments\": [\n {\n \"value\": \"#Feature comment\",\n \"line\": 1\n }\n ],\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple feature description\",\n \"line\": 5,\n \"type\": \"scenario\"\n }\n ]\n }\n ]", + "content_type": "", + "line": 379 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-docstrings", + "keyword": "Scenario", + "name": "Support of Docstrings", + "description": "", + "line": 407, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 408, + "doc_string": { + "value": " Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description\n\n Given passing step\n \"\"\" content type\n step doc string\n \"\"\"", + "content_type": "", + "line": 409 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 421, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 422, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple feature description\",\n \"line\": 4,\n \"type\": \"scenario\",\n \"steps\": [\n {\n \"keyword\": \"Given \",\n \"name\": \"passing step\",\n \"line\": 7,\n \"doc_string\": {\n \"value\": \"step doc string\",\n \"content_type\": \"content type\",\n \"line\": 8\n },\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"passed\",\n \"duration\": 0\n }\n }\n ]\n }\n ]\n }\n]", + "content_type": "", + "line": 423 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "cucumber-json-formatter;support-of-undefined,-pending-and-skipped-status", + "keyword": "Scenario", + "name": "Support of Undefined, Pending and Skipped status", + "description": "", + "line": 464, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 465, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n And pending step\n And undefined\n And passing step\n", + "content_type": "", + "line": 466 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"cucumber\"", + "line": 479, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered json will be as follows:", + "line": 480, + "doc_string": { + "value": " [\n {\n \"uri\": \"features/simple.feature\",\n \"id\": \"simple-feature\",\n \"keyword\": \"Feature\",\n \"name\": \"simple feature\",\n \"description\": \" simple feature description\",\n \"line\": 1,\n \"elements\": [\n {\n \"id\": \"simple-feature;simple-scenario\",\n \"keyword\": \"Scenario\",\n \"name\": \"simple scenario\",\n \"description\": \" simple scenario description\",\n \"line\": 4,\n \"type\": \"scenario\",\n \"steps\": [\n {\n \"keyword\": \"Given \",\n \"name\": \"passing step\",\n \"line\": 7,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"passed\",\n \"duration\": 0\n }\n },\n {\n \"keyword\": \"And \",\n \"name\": \"pending step\",\n \"line\": 8,\n \"match\": {\n \"location\": \"features/simple.feature:8\"\n },\n \"result\": {\n \"status\": \"pending\"\n }\n },\n {\n \"keyword\": \"And \",\n \"name\": \"undefined\",\n \"line\": 9,\n \"match\": {\n \"location\": \"features/simple.feature:9\"\n },\n \"result\": {\n \"status\": \"undefined\"\n }\n },\n {\n \"keyword\": \"And \",\n \"name\": \"passing step\",\n \"line\": 10,\n \"match\": {\n \"location\": \"suite_context.go:0\"\n },\n \"result\": {\n \"status\": \"skipped\"\n }\n }\n ]\n }\n ]\n }\n ]", + "content_type": "", + "line": 481 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/formatter/events.feature", + "id": "event-stream-formatter", + "keyword": "Feature", + "name": "event stream formatter", + "description": " In order to have universal cucumber formatter\n As a test suite\n I need to be able to support event stream formatter", + "line": 1, + "elements": [ + { + "id": "event-stream-formatter;should-fire-only-suite-events-without-any-scenario", + "keyword": "Scenario", + "name": "should fire only suite events without any scenario", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:4\"", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"events\"", + "line": 8, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following events should be fired:", + "line": 9, + "doc_string": { + "value": " TestRunStarted\n TestSource\n TestRunFinished", + "content_type": "", + "line": 10 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "event-stream-formatter;should-process-simple-scenario", + "keyword": "Scenario", + "name": "should process simple scenario", + "description": "", + "line": 16, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:26\"", + "line": 17, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"events\"", + "line": 18, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following events should be fired:", + "line": 19, + "doc_string": { + "value": " TestRunStarted\n TestSource\n TestCaseStarted\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n TestCaseFinished\n TestRunFinished", + "content_type": "", + "line": 20 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "event-stream-formatter;should-process-outline-scenario", + "keyword": "Scenario", + "name": "should process outline scenario", + "description": "", + "line": 37, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:34\"", + "line": 38, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"events\"", + "line": 39, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following events should be fired:", + "line": 40, + "doc_string": { + "value": " TestRunStarted\n TestSource\n TestCaseStarted\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n TestCaseFinished\n TestCaseStarted\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n TestCaseFinished\n TestCaseStarted\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n StepDefinitionFound\n TestStepStarted\n TestStepFinished\n TestCaseFinished\n TestRunFinished", + "content_type": "", + "line": 41 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/formatter/junit.feature", + "id": "junit-xml-formatter", + "keyword": "Feature", + "name": "JUnit XML formatter", + "description": " In order to support tools that import JUnit XML output\n I need to be able to support junit formatted output", + "line": 1, + "comments": [ + { + "value": "# Currently godog only supports comments on Feature and not", + "line": 154 + }, + { + "value": "# scenario and steps.", + "line": 155 + } + ], + "elements": [ + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-node", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node", + "description": "", + "line": 5, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 6, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 7 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 13, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 14, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 15 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-node-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node With Tags", + "description": "", + "line": 24, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 25, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n @TAG2 @TAG3\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 26 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 34, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 35, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 36 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-outline", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline", + "description": "", + "line": 44, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 45, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 46 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 58, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 59, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"2\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"2\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario #1\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003ctestcase name=\"simple scenario #2\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 60 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-outline-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Tags", + "description": "", + "line": 70, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 71, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n\n @TAG2\n Scenario Outline: simple scenario\n simple scenario description\n\n @TAG3\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 72 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 87, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 88, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"2\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"2\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario #1\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003ctestcase name=\"simple scenario #2\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 89 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario With Steps", + "description": "", + "line": 98, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 99, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n Then a failing step\n", + "content_type": "", + "line": 100 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 111, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 112, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"1\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"1\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"failed\" time=\"0\"\u003e\n \u003cfailure message=\"Step a failing step: intentional failure\"\u003e\u003c/failure\u003e\n \u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 113 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-feature-plus-scenario-outline-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Steps", + "description": "", + "line": 123, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 124, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Given \u003cstatus\u003e step\n\n Examples: simple examples\n | status |\n | passing |\n | failing |\n", + "content_type": "", + "line": 125 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 140, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 141, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"2\" skipped=\"0\" failures=\"1\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"2\" skipped=\"0\" failures=\"1\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario #1\" status=\"passed\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003ctestcase name=\"simple scenario #2\" status=\"failed\" time=\"0\"\u003e\n \u003cfailure message=\"Step failing step: intentional failure\"\u003e\u003c/failure\u003e\n \u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 142 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-comments", + "keyword": "Scenario", + "name": "Support of Comments", + "description": "", + "line": 156, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 157, + "doc_string": { + "value": " #Feature comment\n Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description", + "content_type": "", + "line": 158 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 166, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 167, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 168 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-docstrings", + "keyword": "Scenario", + "name": "Support of Docstrings", + "description": "", + "line": 176, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 177, + "doc_string": { + "value": " Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description\n\n Given passing step\n \"\"\" content type\n step doc string\n \"\"\"", + "content_type": "", + "line": 178 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 190, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 191, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"0\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"passed\" time=\"0\"\u003e\u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 192 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "junit-xml-formatter;support-of-undefined,-pending-and-skipped-status", + "keyword": "Scenario", + "name": "Support of Undefined, Pending and Skipped status", + "description": "", + "line": 200, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 201, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n And pending step\n And undefined\n And passing step\n", + "content_type": "", + "line": 202 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"junit\"", + "line": 215, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered xml will be as follows:", + "line": 216, + "doc_string": { + "value": " \u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n \u003ctestsuites name=\"godog\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"1\" time=\"0\"\u003e\n \u003ctestsuite name=\"simple feature\" tests=\"1\" skipped=\"0\" failures=\"0\" errors=\"1\" time=\"0\"\u003e\n \u003ctestcase name=\"simple scenario\" status=\"undefined\" time=\"0\"\u003e\n \u003cerror message=\"Step pending step: TODO: write pending definition\" type=\"pending\"\u003e\u003c/error\u003e\n \u003cerror message=\"Step undefined\" type=\"undefined\"\u003e\u003c/error\u003e\n \u003cerror message=\"Step passing step\" type=\"skipped\"\u003e\u003c/error\u003e\n \u003c/testcase\u003e\n \u003c/testsuite\u003e\n \u003c/testsuites\u003e", + "content_type": "application/xml", + "line": 217 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/formatter/pretty.feature", + "id": "pretty-formatter", + "keyword": "Feature", + "name": "pretty formatter", + "description": " In order to support tools that import pretty output\n I need to be able to support pretty formatted output", + "line": 1, + "comments": [ + { + "value": "# Currently godog only supports comments on Feature and not", + "line": 193 + }, + { + "value": "# scenario and steps.", + "line": 194 + } + ], + "elements": [ + { + "id": "pretty-formatter;support-of-feature-plus-scenario-node", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node", + "description": "", + "line": 5, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 6, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 7 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 13, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 14, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario # features/simple.feature:3\n\n 1 scenarios (1 undefined)\n No steps\n 0s", + "content_type": "", + "line": 15 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-feature-plus-scenario-node-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Node With Tags", + "description": "", + "line": 26, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 27, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n @TAG2 @TAG3\n Scenario: simple scenario\n simple scenario description", + "content_type": "", + "line": 28 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 36, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 37, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario # features/simple.feature:5\n\n 1 scenarios (1 undefined)\n No steps\n 0s", + "content_type": "", + "line": 38 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-feature-plus-scenario-outline", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline", + "description": "", + "line": 48, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 49, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 50 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 62, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 63, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario # features/simple.feature:4\n\n Examples: simple examples\n | status |\n | pass |\n | fail |\n\n 2 scenarios (2 undefined)\n No steps\n 0s", + "content_type": "", + "line": 64 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-feature-plus-scenario-outline-with-tags", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Tags", + "description": "", + "line": 80, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 81, + "doc_string": { + "value": " @TAG1\n Feature: simple feature\n simple feature description\n\n @TAG2\n Scenario Outline: simple scenario\n simple scenario description\n\n @TAG3\n Examples: simple examples\n | status |\n | pass |\n | fail |", + "content_type": "", + "line": 82 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 97, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 98, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario # features/simple.feature:6\n\n Examples: simple examples\n | status |\n | pass |\n | fail |\n\n 2 scenarios (2 undefined)\n No steps\n 0s", + "content_type": "", + "line": 99 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-feature-plus-scenario-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario With Steps", + "description": "", + "line": 114, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 115, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n Then a failing step\n", + "content_type": "", + "line": 116 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 127, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 128, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario # features/simple.feature:4\n Given passing step # suite_context.go:0 -\u003e SuiteContext.func2\n Then a failing step # suite_context.go:0 -\u003e *suiteContext\n intentional failure\n\n --- Failed steps:\n\n Scenario: simple scenario # features/simple.feature:4\n Then a failing step # features/simple.feature:8\n Error: intentional failure\n\n\n 1 scenarios (1 failed)\n 2 steps (1 passed, 1 failed)\n 0s", + "content_type": "", + "line": 129 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-feature-plus-scenario-outline-with-steps", + "keyword": "Scenario", + "name": "Support of Feature Plus Scenario Outline With Steps", + "description": "", + "line": 149, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 150, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario\n simple scenario description\n\n Given \u003cstatus\u003e step\n\n Examples: simple examples\n | status |\n | passing |\n | failing |\n", + "content_type": "", + "line": 151 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 166, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 167, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario Outline: simple scenario # features/simple.feature:4\n Given \u003cstatus\u003e step # suite_context.go:0 -\u003e SuiteContext.func2\n\n Examples: simple examples\n | status |\n | passing |\n | failing |\n intentional failure\n\n --- Failed steps:\n\n Scenario Outline: simple scenario # features/simple.feature:4\n Given failing step # features/simple.feature:7\n Error: intentional failure\n\n\n 2 scenarios (1 passed, 1 failed)\n 2 steps (1 passed, 1 failed)\n 0s", + "content_type": "", + "line": 168 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-comments", + "keyword": "Scenario", + "name": "Support of Comments", + "description": "", + "line": 195, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 196, + "doc_string": { + "value": " #Feature comment\n Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description", + "content_type": "", + "line": 197 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 205, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 206, + "doc_string": { + "value": " Feature: simple feature\n simple description\n\n Scenario: simple scenario # features/simple.feature:5\n\n 1 scenarios (1 undefined)\n No steps\n 0s", + "content_type": "", + "line": 207 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-docstrings", + "keyword": "Scenario", + "name": "Support of Docstrings", + "description": "", + "line": 217, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 218, + "doc_string": { + "value": " Feature: simple feature\n simple description\n\n Scenario: simple scenario\n simple feature description\n\n Given passing step\n \"\"\" content type\n step doc string\n \"\"\"", + "content_type": "", + "line": 219 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 231, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 232, + "doc_string": { + "value": " Feature: simple feature\n simple description\n\n Scenario: simple scenario # features/simple.feature:4\n Given passing step # suite_context.go:0 -\u003e SuiteContext.func2\n \"\"\" content type\n step doc string\n \"\"\"\n\n 1 scenarios (1 passed)\n 1 steps (1 passed)\n 0s", + "content_type": "", + "line": 233 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "pretty-formatter;support-of-undefined,-pending-and-skipped-status", + "keyword": "Scenario", + "name": "Support of Undefined, Pending and Skipped status", + "description": "", + "line": 247, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"features/simple.feature\" file:", + "line": 248, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario\n simple scenario description\n\n Given passing step\n And pending step\n And undefined\n And passing step\n", + "content_type": "", + "line": 249 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with formatter \"pretty\"", + "line": 262, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the rendered output will be as follows:", + "line": 263, + "doc_string": { + "value": " Feature: simple feature\n simple feature description\n\n Scenario: simple scenario # features/simple.feature:4\n Given passing step # suite_context.go:0 -\u003e SuiteContext.func2\n And pending step # suite_context.go:0 -\u003e SuiteContext.func1\n TODO: write pending definition\n And undefined\n And passing step # suite_context.go:0 -\u003e SuiteContext.func2\n\n 1 scenarios (1 pending, 1 undefined)\n 4 steps (1 passed, 1 pending, 1 undefined, 1 skipped)\n 0s\n\n You can implement step definitions for undefined steps with these snippets:\n\n func undefined() error {\n return godog.ErrPending\n }\n\n func FeatureContext(s *godog.Suite) {\n s.Step(`^undefined$`, undefined)\n }", + "content_type": "", + "line": 264 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/lang.feature", + "id": "užkrauti-savybes", + "keyword": "Savybė", + "name": "užkrauti savybes", + "description": " Kad būtų galima paleisti savybių testus\n Kaip testavimo įrankis\n Aš turiu galėti užregistruoti savybes", + "line": 3, + "tags": [ + { + "name": "@lang", + "line": 2 + } + ], + "elements": [ + { + "id": "užkrauti-savybes;savybių-užkrovimas-iš-aplanko", + "keyword": "Scenarijus", + "name": "savybių užkrovimas iš aplanko", + "description": "", + "line": 8, + "type": "scenario", + "tags": [ + { + "name": "@lang", + "line": 2 + } + ], + "steps": [ + { + "keyword": "Duota ", + "name": "savybių aplankas \"features\"", + "line": 9, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Kai ", + "name": "aš išskaitau savybes", + "line": 10, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Tada ", + "name": "aš turėčiau turėti 13 savybių failus:", + "line": 11, + "doc_string": { + "value": "features/background.feature\nfeatures/events.feature\nfeatures/formatter/cucumber.feature\nfeatures/formatter/events.feature\nfeatures/formatter/junit.feature\nfeatures/formatter/pretty.feature\nfeatures/lang.feature\nfeatures/load.feature\nfeatures/multistep.feature\nfeatures/outline.feature\nfeatures/run.feature\nfeatures/snippets.feature\nfeatures/tags.feature", + "content_type": "", + "line": 12 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/load.feature", + "id": "load-features", + "keyword": "Feature", + "name": "load features", + "description": " In order to run features\n As a test suite\n I need to be able to load features", + "line": 1, + "elements": [ + { + "id": "load-features;load-features-within-path", + "keyword": "Scenario", + "name": "load features within path", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features\"", + "line": 7, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 8, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 13 feature files:", + "line": 9, + "doc_string": { + "value": "features/background.feature\nfeatures/events.feature\nfeatures/formatter/cucumber.feature\nfeatures/formatter/events.feature\nfeatures/formatter/junit.feature\nfeatures/formatter/pretty.feature\nfeatures/lang.feature\nfeatures/load.feature\nfeatures/multistep.feature\nfeatures/outline.feature\nfeatures/run.feature\nfeatures/snippets.feature\nfeatures/tags.feature", + "content_type": "", + "line": 10 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "load-features;load-a-specific-feature-file", + "keyword": "Scenario", + "name": "load a specific feature file", + "description": "", + "line": 26, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature\"", + "line": 27, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 28, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 1 feature file:", + "line": 29, + "doc_string": { + "value": "features/load.feature", + "content_type": "", + "line": 30 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "load-features;loaded-feature-should-have-a-number-of-scenarios;;2", + "keyword": "Scenario Outline", + "name": "loaded feature should have a number of scenarios", + "description": "", + "line": 41, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:3\"", + "line": 41, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 41, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 0 scenario registered", + "line": 41, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "load-features;loaded-feature-should-have-a-number-of-scenarios;;3", + "keyword": "Scenario Outline", + "name": "loaded feature should have a number of scenarios", + "description": "", + "line": 42, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature:6\"", + "line": 42, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 42, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 1 scenario registered", + "line": 42, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "load-features;loaded-feature-should-have-a-number-of-scenarios;;4", + "keyword": "Scenario Outline", + "name": "loaded feature should have a number of scenarios", + "description": "", + "line": 43, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature\"", + "line": 43, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 43, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 4 scenario registered", + "line": 43, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "load-features;load-a-number-of-feature-files", + "keyword": "Scenario", + "name": "load a number of feature files", + "description": "", + "line": 45, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature path \"features/load.feature\"", + "line": 46, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "a feature path \"features/events.feature\"", + "line": 47, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I parse features", + "line": 48, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "I should have 2 feature files:", + "line": 49, + "doc_string": { + "value": "features/events.feature\nfeatures/load.feature", + "content_type": "", + "line": 50 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/multistep.feature", + "id": "run-features-with-nested-steps", + "keyword": "Feature", + "name": "run features with nested steps", + "description": " In order to test multisteps\n As a test suite\n I need to be able to execute multisteps", + "line": 1, + "elements": [ + { + "id": "run-features-with-nested-steps;should-run-passing-multistep-successfully", + "keyword": "Scenario", + "name": "should run passing multistep successfully", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: normal feature\n\n Scenario: run passing multistep\n Given passing step\n Then passing multistep", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 15, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 16, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 17, + "doc_string": { + "value": "passing step\npassing multistep", + "content_type": "", + "line": 18 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features-with-nested-steps;should-fail-multistep", + "keyword": "Scenario", + "name": "should fail multistep", + "description": "", + "line": 23, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 24, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: run failing multistep\n Given passing step\n When failing multistep\n Then I should have 1 scenario registered", + "content_type": "", + "line": 25 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 33, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 34, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be failed:", + "line": 35, + "doc_string": { + "value": "failing multistep", + "content_type": "", + "line": 36 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 39, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 40 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 43, + "doc_string": { + "value": "passing step", + "content_type": "", + "line": 44 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features-with-nested-steps;should-fail-nested-multistep", + "keyword": "Scenario", + "name": "should fail nested multistep", + "description": "", + "line": 48, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 49, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: run failing nested multistep\n Given failing nested multistep\n When passing step", + "content_type": "", + "line": 50 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 57, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 58, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be failed:", + "line": 59, + "doc_string": { + "value": "failing nested multistep", + "content_type": "", + "line": 60 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 63, + "doc_string": { + "value": "passing step", + "content_type": "", + "line": 64 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features-with-nested-steps;should-skip-steps-after-undefined-multistep", + "keyword": "Scenario", + "name": "should skip steps after undefined multistep", + "description": "", + "line": 68, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 69, + "doc_string": { + "value": "Feature: run undefined multistep\n\n Scenario: run undefined multistep\n Given passing step\n When undefined multistep\n Then passing multistep", + "content_type": "", + "line": 70 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 78, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 79, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be passed:", + "line": 80, + "doc_string": { + "value": "passing step", + "content_type": "", + "line": 81 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be undefined:", + "line": 84, + "doc_string": { + "value": "undefined multistep", + "content_type": "", + "line": 85 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 88, + "doc_string": { + "value": "passing multistep", + "content_type": "", + "line": 89 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features-with-nested-steps;should-match-undefined-steps-in-a-row", + "keyword": "Scenario", + "name": "should match undefined steps in a row", + "description": "", + "line": 93, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 94, + "doc_string": { + "value": "Feature: undefined feature\n\n Scenario: parse a scenario\n Given undefined step\n When undefined multistep\n Then I should have 1 scenario registered", + "content_type": "", + "line": 95 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 103, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 104, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 105, + "doc_string": { + "value": "undefined step\nundefined multistep", + "content_type": "", + "line": 106 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 110, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 111 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features-with-nested-steps;should-mark-undefined-steps-after-pending", + "keyword": "Scenario", + "name": "should mark undefined steps after pending", + "description": "", + "line": 115, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"pending.feature\" file:", + "line": 116, + "doc_string": { + "value": "Feature: pending feature\n\n Scenario: parse a scenario\n Given pending step\n When undefined step\n Then undefined multistep\n And I should have 1 scenario registered", + "content_type": "", + "line": 117 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 126, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 127, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 128, + "doc_string": { + "value": "undefined step\nundefined multistep", + "content_type": "", + "line": 129 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be pending:", + "line": 133, + "doc_string": { + "value": "pending step", + "content_type": "", + "line": 134 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 137, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 138 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/outline.feature", + "id": "run-outline", + "keyword": "Feature", + "name": "run outline", + "description": " In order to test application behavior\n As a test suite\n I need to be able to run outline scenarios", + "line": 1, + "elements": [ + { + "id": "run-outline;should-run-a-normal-outline", + "keyword": "Scenario", + "name": "should run a normal outline", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: outline\n\n Background:\n Given passing step\n\n Scenario Outline: parse a scenario\n Given a feature path \"\u003cpath\u003e\"\n When I parse features\n Then I should have \u003cnum\u003e scenario registered\n\n Examples:\n | path | num |\n | features/load.feature:6 | 1 |\n | features/load.feature:3 | 0 |", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 24, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 25, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 26, + "doc_string": { + "value": "a passing step\nI parse features\na feature path \"features/load.feature:6\"\na feature path \"features/load.feature:3\"\nI should have 1 scenario registered\nI should have 0 scenario registered", + "content_type": "", + "line": 27 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-outline;should-continue-through-examples-on-failure", + "keyword": "Scenario", + "name": "should continue through examples on failure", + "description": "", + "line": 36, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 37, + "doc_string": { + "value": "Feature: outline\n\n Background:\n Given passing step\n\n Scenario Outline: parse a scenario\n Given a feature path \"\u003cpath\u003e\"\n When I parse features\n Then I should have \u003cnum\u003e scenario registered\n\n Examples:\n | path | num |\n | features/load.feature:6 | 5 |\n | features/load.feature:3 | 0 |", + "content_type": "", + "line": 38 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 54, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 55, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 56, + "doc_string": { + "value": "a passing step\nI parse features\na feature path \"features/load.feature:6\"\na feature path \"features/load.feature:3\"\nI should have 0 scenario registered", + "content_type": "", + "line": 57 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be failed:", + "line": 64, + "doc_string": { + "value": "I should have 5 scenario registered", + "content_type": "", + "line": 65 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-outline;should-skip-examples-on-background-failure", + "keyword": "Scenario", + "name": "should skip examples on background failure", + "description": "", + "line": 69, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 70, + "doc_string": { + "value": "Feature: outline\n\n Background:\n Given a failing step\n\n Scenario Outline: parse a scenario\n Given a feature path \"\u003cpath\u003e\"\n When I parse features\n Then I should have \u003cnum\u003e scenario registered\n\n Examples:\n | path | num |\n | features/load.feature:6 | 1 |\n | features/load.feature:3 | 0 |", + "content_type": "", + "line": 71 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 87, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 88, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 89, + "doc_string": { + "value": "I parse features\na feature path \"features/load.feature:6\"\na feature path \"features/load.feature:3\"\nI should have 0 scenario registered\nI should have 1 scenario registered", + "content_type": "", + "line": 90 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be failed:", + "line": 97, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 98 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-outline;should-translate-step-table-body", + "keyword": "Scenario", + "name": "should translate step table body", + "description": "", + "line": 102, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 103, + "doc_string": { + "value": "Feature: outline\n\n Background:\n Given I'm listening to suite events\n\n Scenario Outline: run with events\n Given a feature path \"\u003cpath\u003e\"\n When I run feature suite\n Then these events had to be fired for a number of times:\n | BeforeScenario | \u003cscen\u003e |\n | BeforeStep | \u003cstep\u003e |\n\n Examples:\n | path | scen | step |\n | features/load.feature:6 | 1 | 3 |\n | features/load.feature | 6 | 19 |", + "content_type": "", + "line": 104 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 122, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 123, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 124, + "doc_string": { + "value": "I'm listening to suite events\nI run feature suite\na feature path \"features/load.feature:6\"\na feature path \"features/load.feature\"", + "content_type": "", + "line": 125 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-outline;should-translate-step-doc-string-argument;;2", + "keyword": "Scenario Outline", + "name": "should translate step doc string argument", + "description": "", + "line": 151, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 151, + "doc_string": { + "value": "Feature: scenario events\n\n Background:\n Given I'm listening to suite events\n\n Scenario: run with events\n Given a feature path \"features/load.feature:6\"\n When I run feature suite\n Then these events had to be fired for a number of times:\n | BeforeScenario | 1 |", + "content_type": "", + "line": 134 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 151, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 151, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-outline;should-translate-step-doc-string-argument;;3", + "keyword": "Scenario Outline", + "name": "should translate step doc string argument", + "description": "", + "line": 152, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 152, + "doc_string": { + "value": "Feature: scenario events\n\n Background:\n Given I'm listening to suite events\n\n Scenario: run with events\n Given a feature path \"features/load.feature\"\n When I run feature suite\n Then these events had to be fired for a number of times:\n | BeforeScenario | 6 |", + "content_type": "", + "line": 134 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 152, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 152, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/run.feature", + "id": "run-features", + "keyword": "Feature", + "name": "run features", + "description": " In order to test application behavior\n As a test suite\n I need to be able to run features", + "line": 1, + "elements": [ + { + "id": "run-features;should-run-a-normal-feature", + "keyword": "Scenario", + "name": "should run a normal feature", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: normal feature\n\n Scenario: parse a scenario\n Given a feature path \"features/load.feature:6\"\n When I parse features\n Then I should have 1 scenario registered", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 16, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 17, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 18, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"\nI parse features\nI should have 1 scenario registered", + "content_type": "", + "line": 19 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-skip-steps-after-failure", + "keyword": "Scenario", + "name": "should skip steps after failure", + "description": "", + "line": 25, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 26, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: parse a scenario\n Given a failing step\n When I parse features\n Then I should have 1 scenario registered", + "content_type": "", + "line": 27 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 35, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 36, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be failed:", + "line": 37, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 38 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 41, + "doc_string": { + "value": "I parse features\nI should have 1 scenario registered", + "content_type": "", + "line": 42 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-skip-all-scenarios-if-background-fails", + "keyword": "Scenario", + "name": "should skip all scenarios if background fails", + "description": "", + "line": 47, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 48, + "doc_string": { + "value": "Feature: failed feature\n\n Background:\n Given a failing step\n\n Scenario: parse a scenario\n Given a feature path \"features/load.feature:6\"\n When I parse features\n Then I should have 1 scenario registered", + "content_type": "", + "line": 49 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 60, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have failed", + "line": 61, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be failed:", + "line": 62, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 63 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 66, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"\nI parse features\nI should have 1 scenario registered", + "content_type": "", + "line": 67 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-skip-steps-after-undefined", + "keyword": "Scenario", + "name": "should skip steps after undefined", + "description": "", + "line": 73, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 74, + "doc_string": { + "value": "Feature: undefined feature\n\n Scenario: parse a scenario\n Given a feature path \"features/load.feature:6\"\n When undefined action\n Then I should have 1 scenario registered", + "content_type": "", + "line": 75 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 83, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 84, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be passed:", + "line": 85, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"", + "content_type": "", + "line": 86 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be undefined:", + "line": 89, + "doc_string": { + "value": "undefined action", + "content_type": "", + "line": 90 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 93, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 94 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-match-undefined-steps-in-a-row", + "keyword": "Scenario", + "name": "should match undefined steps in a row", + "description": "", + "line": 98, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 99, + "doc_string": { + "value": "Feature: undefined feature\n\n Scenario: parse a scenario\n Given undefined step\n When undefined action\n Then I should have 1 scenario registered", + "content_type": "", + "line": 100 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 108, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 109, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 110, + "doc_string": { + "value": "undefined step\nundefined action", + "content_type": "", + "line": 111 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 115, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 116 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-skip-steps-on-pending", + "keyword": "Scenario", + "name": "should skip steps on pending", + "description": "", + "line": 120, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"pending.feature\" file:", + "line": 121, + "doc_string": { + "value": "Feature: pending feature\n\n Scenario: parse a scenario\n Given undefined step\n When pending step\n Then I should have 1 scenario registered", + "content_type": "", + "line": 122 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 130, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 131, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be undefined:", + "line": 132, + "doc_string": { + "value": "undefined step", + "content_type": "", + "line": 133 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 136, + "doc_string": { + "value": "pending step\nI should have 1 scenario registered", + "content_type": "", + "line": 137 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-handle-pending-step", + "keyword": "Scenario", + "name": "should handle pending step", + "description": "", + "line": 142, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"pending.feature\" file:", + "line": 143, + "doc_string": { + "value": "Feature: pending feature\n\n Scenario: parse a scenario\n Given a feature path \"features/load.feature:6\"\n When pending step\n Then I should have 1 scenario registered", + "content_type": "", + "line": 144 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 152, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 153, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be passed:", + "line": 154, + "doc_string": { + "value": "a feature path \"features/load.feature:6\"", + "content_type": "", + "line": 155 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be pending:", + "line": 158, + "doc_string": { + "value": "pending step", + "content_type": "", + "line": 159 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 162, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 163 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-mark-undefined-steps-after-pending", + "keyword": "Scenario", + "name": "should mark undefined steps after pending", + "description": "", + "line": 167, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"pending.feature\" file:", + "line": 168, + "doc_string": { + "value": "Feature: pending feature\n\n Scenario: parse a scenario\n Given pending step\n When undefined\n Then undefined 2\n And I should have 1 scenario registered", + "content_type": "", + "line": 169 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 178, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 179, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 180, + "doc_string": { + "value": "undefined\nundefined 2", + "content_type": "", + "line": 181 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be pending:", + "line": 185, + "doc_string": { + "value": "pending step", + "content_type": "", + "line": 186 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following step should be skipped:", + "line": 189, + "doc_string": { + "value": "I should have 1 scenario registered", + "content_type": "", + "line": 190 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-fail-suite-if-undefined-steps-follow-after-the-failure", + "keyword": "Scenario", + "name": "should fail suite if undefined steps follow after the failure", + "description": "", + "line": 194, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 195, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: parse a scenario\n Given a failing step\n When an undefined step\n Then another undefined step", + "content_type": "", + "line": 196 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 204, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following step should be failed:", + "line": 205, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 206 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 209, + "doc_string": { + "value": "an undefined step\nanother undefined step", + "content_type": "", + "line": 210 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the suite should have failed", + "line": 214, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-fail-suite-and-skip-pending-step-after-failed-step", + "keyword": "Scenario", + "name": "should fail suite and skip pending step after failed step", + "description": "", + "line": 216, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 217, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: parse a scenario\n Given a failing step\n When pending step\n Then another undefined step", + "content_type": "", + "line": 218 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 226, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following step should be failed:", + "line": 227, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 228 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 231, + "doc_string": { + "value": "pending step", + "content_type": "", + "line": 232 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 235, + "doc_string": { + "value": "another undefined step", + "content_type": "", + "line": 236 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the suite should have failed", + "line": 239, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "run-features;should-fail-suite-and-skip-next-step-after-failed-step", + "keyword": "Scenario", + "name": "should fail suite and skip next step after failed step", + "description": "", + "line": 241, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"failed.feature\" file:", + "line": 242, + "doc_string": { + "value": "Feature: failed feature\n\n Scenario: parse a scenario\n Given a failing step\n When a failing step\n Then another undefined step", + "content_type": "", + "line": 243 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 251, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following step should be failed:", + "line": 252, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 253 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be skipped:", + "line": 256, + "doc_string": { + "value": "a failing step", + "content_type": "", + "line": 257 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be undefined:", + "line": 260, + "doc_string": { + "value": "another undefined step", + "content_type": "", + "line": 261 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the suite should have failed", + "line": 264, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/snippets.feature", + "id": "undefined-step-snippets", + "keyword": "Feature", + "name": "undefined step snippets", + "description": " In order to implement step definitions faster\n As a test suite user\n I need to be able to get undefined step snippets", + "line": 1, + "elements": [ + { + "id": "undefined-step-snippets;should-generate-snippets", + "keyword": "Scenario", + "name": "should generate snippets", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: undefined steps\n\n Scenario: get version number from api\n When I send \"GET\" request to \"/version\"\n Then the response code should be 200", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 15, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following steps should be undefined:", + "line": 16, + "doc_string": { + "value": "I send \"GET\" request to \"/version\"\nthe response code should be 200", + "content_type": "", + "line": 17 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the undefined step snippets should be:", + "line": 21, + "doc_string": { + "value": "func iSendRequestTo(arg1, arg2 string) error {\n return godog.ErrPending\n}\n\nfunc theResponseCodeShouldBe(arg1 int) error {\n return godog.ErrPending\n}\n\nfunc FeatureContext(s *godog.Suite) {\n s.Step(`^I send \"([^\"]*)\" request to \"([^\"]*)\"$`, iSendRequestTo)\n s.Step(`^the response code should be (\\d+)$`, theResponseCodeShouldBe)\n}", + "content_type": "", + "line": 22 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "undefined-step-snippets;should-generate-snippets-with-more-arguments", + "keyword": "Scenario", + "name": "should generate snippets with more arguments", + "description": "", + "line": 37, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 38, + "doc_string": { + "value": "Feature: undefined steps\n\n Scenario: get version number from api\n When I send \"GET\" request to \"/version\" with:\n | col1 | val1 |\n | col2 | val2 |\n Then the response code should be 200 and header \"X-Powered-By\" should be \"godog\"", + "content_type": "", + "line": 39 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 48, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the undefined step snippets should be:", + "line": 49, + "doc_string": { + "value": "func iSendRequestToWith(arg1, arg2 string, arg3 *gherkin.DataTable) error {\n return godog.ErrPending\n}\n\nfunc theResponseCodeShouldBeAndHeaderShouldBe(arg1 int, arg2, arg3 string) error {\n return godog.ErrPending\n}\n\nfunc FeatureContext(s *godog.Suite) {\n s.Step(`^I send \"([^\"]*)\" request to \"([^\"]*)\" with:$`, iSendRequestToWith)\n s.Step(`^the response code should be (\\d+) and header \"([^\"]*)\" should be \"([^\"]*)\"$`, theResponseCodeShouldBeAndHeaderShouldBe)\n}", + "content_type": "", + "line": 50 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "undefined-step-snippets;should-handle-escaped-symbols", + "keyword": "Scenario", + "name": "should handle escaped symbols", + "description": "", + "line": 65, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 66, + "doc_string": { + "value": "Feature: undefined steps\n\n Scenario: get version number from api\n When I pull from github.com\n Then the project should be there", + "content_type": "", + "line": 67 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 74, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the following steps should be undefined:", + "line": 75, + "doc_string": { + "value": "I pull from github.com\nthe project should be there", + "content_type": "", + "line": 76 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the undefined step snippets should be:", + "line": 80, + "doc_string": { + "value": "func iPullFromGithubcom() error {\n return godog.ErrPending\n}\n\nfunc theProjectShouldBeThere() error {\n return godog.ErrPending\n}\n\nfunc FeatureContext(s *godog.Suite) {\n s.Step(`^I pull from github\\.com$`, iPullFromGithubcom)\n s.Step(`^the project should be there$`, theProjectShouldBeThere)\n}", + "content_type": "", + "line": 81 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "undefined-step-snippets;should-handle-string-argument-followed-by-comma", + "keyword": "Scenario", + "name": "should handle string argument followed by comma", + "description": "", + "line": 96, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 97, + "doc_string": { + "value": "Feature: undefined\n\n Scenario: add item to basket\n Given there is a \"Sith Lord Lightsaber\", which costs £5\n When I add the \"Sith Lord Lightsaber\" to the basket", + "content_type": "", + "line": 98 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 105, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the undefined step snippets should be:", + "line": 106, + "doc_string": { + "value": "func thereIsAWhichCosts(arg1 string, arg2 int) error {\n return godog.ErrPending\n}\n\nfunc iAddTheToTheBasket(arg1 string) error {\n return godog.ErrPending\n}\n\nfunc FeatureContext(s *godog.Suite) {\n s.Step(`^there is a \"([^\"]*)\", which costs £(\\d+)$`, thereIsAWhichCosts)\n s.Step(`^I add the \"([^\"]*)\" to the basket$`, iAddTheToTheBasket)\n}", + "content_type": "", + "line": 107 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "undefined-step-snippets;should-handle-arguments-in-the-beggining-or-end-of-the-step", + "keyword": "Scenario", + "name": "should handle arguments in the beggining or end of the step", + "description": "", + "line": 122, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"undefined.feature\" file:", + "line": 123, + "doc_string": { + "value": "Feature: undefined\n\n Scenario: add item to basket\n Given \"Sith Lord Lightsaber\", which costs £5\n And 12 godogs", + "content_type": "", + "line": 124 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite", + "line": 131, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the undefined step snippets should be:", + "line": 132, + "doc_string": { + "value": "func whichCosts(arg1 string, arg2 int) error {\n return godog.ErrPending\n}\n\nfunc godogs(arg1 int) error {\n return godog.ErrPending\n}\n\nfunc FeatureContext(s *godog.Suite) {\n s.Step(`^\"([^\"]*)\", which costs £(\\d+)$`, whichCosts)\n s.Step(`^(\\d+) godogs$`, godogs)\n}", + "content_type": "", + "line": 133 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + }, + { + "uri": "features/tags.feature", + "id": "tag-filters", + "keyword": "Feature", + "name": "tag filters", + "description": " In order to test application behavior\n As a test suite\n I need to be able to filter features and scenarios by tags", + "line": 1, + "elements": [ + { + "id": "tag-filters;should-filter-outline-examples-by-tags", + "keyword": "Scenario", + "name": "should filter outline examples by tags", + "description": "", + "line": 6, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 7, + "doc_string": { + "value": "Feature: outline\n\n Background:\n Given passing step\n\n Scenario Outline: parse a scenario\n Given a feature path \"\u003cpath\u003e\"\n When I parse features\n Then I should have \u003cnum\u003e scenario registered\n\n Examples:\n | path | num |\n | features/load.feature:3 | 0 |\n\n @used\n Examples:\n | path | num |\n | features/load.feature:6 | 1 |", + "content_type": "", + "line": 8 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with tags \"@used\"", + "line": 28, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 29, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 30, + "doc_string": { + "value": "I parse features\na feature path \"features/load.feature:6\"\nI should have 1 scenario registered", + "content_type": "", + "line": 31 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "I should have 1 scenario registered", + "line": 36, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "tag-filters;should-filter-scenarios-by-x-tag", + "keyword": "Scenario", + "name": "should filter scenarios by X tag", + "description": "", + "line": 38, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 39, + "doc_string": { + "value": "Feature: tagged\n\n @x\n Scenario: one\n Given a feature path \"one\"\n\n @x\n Scenario: two\n Given a feature path \"two\"\n\n @x @y\n Scenario: three\n Given a feature path \"three\"\n\n @y\n Scenario: four\n Given a feature path \"four\"", + "content_type": "", + "line": 40 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with tags \"@x\"", + "line": 59, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 60, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "I should have 3 scenario registered", + "line": 61, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 62, + "doc_string": { + "value": "a feature path \"one\"\na feature path \"two\"\na feature path \"three\"", + "content_type": "", + "line": 63 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "tag-filters;should-filter-scenarios-by-x-tag-not-having-y", + "keyword": "Scenario", + "name": "should filter scenarios by X tag not having Y", + "description": "", + "line": 69, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 70, + "doc_string": { + "value": "Feature: tagged\n\n @x\n Scenario: one\n Given a feature path \"one\"\n\n @x\n Scenario: two\n Given a feature path \"two\"\n\n @x @y\n Scenario: three\n Given a feature path \"three\"\n\n @y @z\n Scenario: four\n Given a feature path \"four\"", + "content_type": "", + "line": 71 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with tags \"@x \u0026\u0026 ~@y\"", + "line": 90, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 91, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "I should have 2 scenario registered", + "line": 92, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 93, + "doc_string": { + "value": "a feature path \"one\"\na feature path \"two\"", + "content_type": "", + "line": 94 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + }, + { + "id": "tag-filters;should-filter-scenarios-having-y-and-z-tags", + "keyword": "Scenario", + "name": "should filter scenarios having Y and Z tags", + "description": "", + "line": 99, + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a feature \"normal.feature\" file:", + "line": 100, + "doc_string": { + "value": "Feature: tagged\n\n @x\n Scenario: one\n Given a feature path \"one\"\n\n @x\n Scenario: two\n Given a feature path \"two\"\n\n @x @y\n Scenario: three\n Given a feature path \"three\"\n\n @y @z\n Scenario: four\n Given a feature path \"four\"", + "content_type": "", + "line": 101 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "When ", + "name": "I run feature suite with tags \"@y \u0026\u0026 @z\"", + "line": 120, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "Then ", + "name": "the suite should have passed", + "line": 121, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "I should have 1 scenario registered", + "line": 122, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + }, + { + "keyword": "And ", + "name": "the following steps should be passed:", + "line": 123, + "doc_string": { + "value": "a feature path \"four\"", + "content_type": "", + "line": 124 + }, + "match": { + "location": "suite_context.go:0" + }, + "result": { + "status": "passed", + "duration": 0 + } + } + ] + } + ] + } +] \ No newline at end of file diff --git a/fmt.go b/fmt.go index d667ff3..93be2d9 100644 --- a/fmt.go +++ b/fmt.go @@ -15,8 +15,7 @@ import ( "unicode" "github.com/cucumber/godog/colors" - - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) // some snippet formatting regexps @@ -44,7 +43,7 @@ var undefinedSnippetsTpl = template.Must(template.New("snippets").Funcs(snippetH type undefinedSnippet struct { Method string Expr string - argument *messages.PickleStepArgument + argument interface{} // gherkin step argument } type registeredFormatter struct { @@ -98,14 +97,14 @@ func AvailableFormatters() map[string]string { // formatters needs to be registered with a // godog.Format function call type Formatter interface { - Feature(*messages.GherkinDocument, string, []byte) - Pickle(*messages.Pickle) - Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) - Failed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition, error) - Passed(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) - Skipped(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) - Undefined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) - Pending(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) + Feature(*gherkin.Feature, string, []byte) + Node(interface{}) + Defined(*gherkin.Step, *StepDef) + Failed(*gherkin.Step, *StepDef, error) + Passed(*gherkin.Step, *StepDef) + Skipped(*gherkin.Step, *StepDef) + Undefined(*gherkin.Step, *StepDef) + Pending(*gherkin.Step, *StepDef) Summary() } @@ -121,17 +120,17 @@ type ConcurrentFormatter interface { // suite name and io.Writer to record output type FormatterFunc func(string, io.Writer) Formatter -type stepResultStatus int +type stepType int const ( - passed stepResultStatus = iota + passed stepType = iota failed skipped undefined pending ) -func (st stepResultStatus) clr() colors.ColorFunc { +func (st stepType) clr() colors.ColorFunc { switch st { case passed: return green @@ -144,7 +143,7 @@ func (st stepResultStatus) clr() colors.ColorFunc { } } -func (st stepResultStatus) String() string { +func (st stepType) String() string { switch st { case passed: return "passed" @@ -162,17 +161,65 @@ func (st stepResultStatus) String() string { } type stepResult struct { - status stepResultStatus - time time.Time - err error - - owner *messages.Pickle - step *messages.Pickle_PickleStep - def *StepDefinition + typ stepType + feature *feature + owner interface{} + step *gherkin.Step + time time.Time + def *StepDef + err error } -func newStepResult(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) *stepResult { - return &stepResult{time: timeNowFunc(), owner: pickle, step: step, def: match} +func (f stepResult) line() string { + return fmt.Sprintf("%s:%d", f.feature.Path, f.step.Location.Line) +} + +func (f stepResult) scenarioDesc() string { + if sc, ok := f.owner.(*gherkin.Scenario); ok { + return fmt.Sprintf("%s: %s", sc.Keyword, sc.Name) + } + + if row, ok := f.owner.(*gherkin.TableRow); ok { + for _, def := range f.feature.Feature.ScenarioDefinitions { + out, ok := def.(*gherkin.ScenarioOutline) + if !ok { + continue + } + + for _, ex := range out.Examples { + for _, rw := range ex.TableBody { + if rw.Location.Line == row.Location.Line { + return fmt.Sprintf("%s: %s", out.Keyword, out.Name) + } + } + } + } + } + return f.line() // was not expecting different owner +} + +func (f stepResult) scenarioLine() string { + if sc, ok := f.owner.(*gherkin.Scenario); ok { + return fmt.Sprintf("%s:%d", f.feature.Path, sc.Location.Line) + } + + if row, ok := f.owner.(*gherkin.TableRow); ok { + for _, def := range f.feature.Feature.ScenarioDefinitions { + out, ok := def.(*gherkin.ScenarioOutline) + if !ok { + continue + } + + for _, ex := range out.Examples { + for _, rw := range ex.TableBody { + if rw.Location.Line == row.Location.Line { + return fmt.Sprintf("%s:%d", f.feature.Path, out.Location.Line) + } + } + } + } + } + return f.line() // was not expecting different owner } func newBaseFmt(suite string, out io.Writer) *basefmt { @@ -192,205 +239,217 @@ type basefmt struct { owner interface{} indent int - started time.Time - features []*feature + started time.Time + features []*feature + failed []*stepResult + passed []*stepResult + skipped []*stepResult + undefined []*stepResult + pending []*stepResult lock *sync.Mutex } -func (f *basefmt) lastFeature() *feature { - return f.features[len(f.features)-1] -} +func (f *basefmt) Node(n interface{}) { + f.lock.Lock() + defer f.lock.Unlock() -func (f *basefmt) lastStepResult() *stepResult { - return f.lastFeature().lastStepResult() -} + switch t := n.(type) { + case *gherkin.Scenario: + f.owner = t + feature := f.features[len(f.features)-1] + feature.Scenarios = append(feature.Scenarios, &scenario{Name: t.Name, time: timeNowFunc()}) + case *gherkin.ScenarioOutline: + feature := f.features[len(f.features)-1] + feature.Scenarios = append(feature.Scenarios, &scenario{OutlineName: t.Name}) + case *gherkin.TableRow: + f.owner = t -func (f *basefmt) findScenario(scenarioAstID string) *messages.GherkinDocument_Feature_Scenario { - for _, ft := range f.features { - if sc := ft.findScenario(scenarioAstID); sc != nil { - return sc + feature := f.features[len(f.features)-1] + lastExample := feature.Scenarios[len(feature.Scenarios)-1] + + newExample := scenario{OutlineName: lastExample.OutlineName, ExampleNo: lastExample.ExampleNo + 1, time: timeNowFunc()} + newExample.Name = fmt.Sprintf("%s #%d", newExample.OutlineName, newExample.ExampleNo) + + const firstExample = 1 + if newExample.ExampleNo == firstExample { + feature.Scenarios[len(feature.Scenarios)-1] = &newExample + } else { + feature.Scenarios = append(feature.Scenarios, &newExample) } } - - panic("Couldn't find scenario for AST ID: " + scenarioAstID) } -func (f *basefmt) findBackground(scenarioAstID string) *messages.GherkinDocument_Feature_Background { - for _, ft := range f.features { - if bg := ft.findBackground(scenarioAstID); bg != nil { - return bg - } +func (f *basefmt) Defined(*gherkin.Step, *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() +} + +func (f *basefmt) Feature(ft *gherkin.Feature, p string, c []byte) { + f.lock.Lock() + defer f.lock.Unlock() + + f.features = append(f.features, &feature{Path: p, Feature: ft, time: timeNowFunc()}) +} + +func (f *basefmt) Passed(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + + s := &stepResult{ + owner: f.owner, + feature: f.features[len(f.features)-1], + step: step, + def: match, + typ: passed, + time: timeNowFunc(), } + f.passed = append(f.passed, s) - return nil + f.features[len(f.features)-1].appendStepResult(s) } -func (f *basefmt) findExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) { - for _, ft := range f.features { - if es, rs := ft.findExample(exampleAstID); es != nil && rs != nil { - return es, rs - } +func (f *basefmt) Skipped(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + + s := &stepResult{ + owner: f.owner, + feature: f.features[len(f.features)-1], + step: step, + def: match, + typ: skipped, + time: timeNowFunc(), } + f.skipped = append(f.skipped, s) - return nil, nil + f.features[len(f.features)-1].appendStepResult(s) } -func (f *basefmt) findStep(stepAstID string) *messages.GherkinDocument_Feature_Step { - for _, ft := range f.features { - if st := ft.findStep(stepAstID); st != nil { - return st - } +func (f *basefmt) Undefined(step *gherkin.Step, match *StepDef) { + f.lock.Lock() + defer f.lock.Unlock() + + s := &stepResult{ + owner: f.owner, + feature: f.features[len(f.features)-1], + step: step, + def: match, + typ: undefined, + time: timeNowFunc(), } + f.undefined = append(f.undefined, s) - panic("Couldn't find step for AST ID: " + stepAstID) + f.features[len(f.features)-1].appendStepResult(s) } -func (f *basefmt) Pickle(p *messages.Pickle) { +func (f *basefmt) Failed(step *gherkin.Step, match *StepDef, err error) { f.lock.Lock() defer f.lock.Unlock() - feature := f.features[len(f.features)-1] - feature.pickleResults = append(feature.pickleResults, &pickleResult{Name: p.Name, time: timeNowFunc()}) + s := &stepResult{ + owner: f.owner, + feature: f.features[len(f.features)-1], + step: step, + def: match, + err: err, + typ: failed, + time: timeNowFunc(), + } + f.failed = append(f.failed, s) + + f.features[len(f.features)-1].appendStepResult(s) } -func (f *basefmt) Defined(*messages.Pickle, *messages.Pickle_PickleStep, *StepDefinition) {} - -func (f *basefmt) Feature(ft *messages.GherkinDocument, p string, c []byte) { +func (f *basefmt) Pending(step *gherkin.Step, match *StepDef) { f.lock.Lock() defer f.lock.Unlock() - f.features = append(f.features, &feature{Path: p, GherkinDocument: ft, time: timeNowFunc()}) -} + s := &stepResult{ + owner: f.owner, + feature: f.features[len(f.features)-1], + step: step, + def: match, + typ: pending, + time: timeNowFunc(), + } + f.pending = append(f.pending, s) -func (f *basefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.lock.Lock() - defer f.lock.Unlock() - - s := newStepResult(pickle, step, match) - s.status = passed - f.lastFeature().appendStepResult(s) -} - -func (f *basefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.lock.Lock() - defer f.lock.Unlock() - - s := newStepResult(pickle, step, match) - s.status = skipped - f.lastFeature().appendStepResult(s) -} - -func (f *basefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.lock.Lock() - defer f.lock.Unlock() - - s := newStepResult(pickle, step, match) - s.status = undefined - f.lastFeature().appendStepResult(s) -} - -func (f *basefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { - f.lock.Lock() - defer f.lock.Unlock() - - s := newStepResult(pickle, step, match) - s.status = failed - s.err = err - f.lastFeature().appendStepResult(s) -} - -func (f *basefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.lock.Lock() - defer f.lock.Unlock() - - s := newStepResult(pickle, step, match) - s.status = pending - f.lastFeature().appendStepResult(s) + f.features[len(f.features)-1].appendStepResult(s) } func (f *basefmt) Summary() { - var totalSc, passedSc, undefinedSc int - var totalSt, passedSt, failedSt, skippedSt, pendingSt, undefinedSt int - - for _, feat := range f.features { - for _, pr := range feat.pickleResults { - var prStatus stepResultStatus - totalSc++ - - if len(pr.stepResults) == 0 { - prStatus = undefined - } - - for _, sr := range pr.stepResults { - totalSt++ - - switch sr.status { - case passed: - prStatus = passed - passedSt++ - case failed: - prStatus = failed - failedSt++ - case skipped: - skippedSt++ - case undefined: - prStatus = undefined - undefinedSt++ - case pending: - prStatus = pending - pendingSt++ + var total, passed, undefined int + for _, ft := range f.features { + for _, def := range ft.ScenarioDefinitions { + switch t := def.(type) { + case *gherkin.Scenario: + total++ + if len(t.Steps) == 0 { + undefined++ + } + case *gherkin.ScenarioOutline: + for _, ex := range t.Examples { + total += len(ex.TableBody) + if len(t.Steps) == 0 { + undefined += len(ex.TableBody) + } } } - - if prStatus == passed { - passedSc++ - } else if prStatus == undefined { - undefinedSc++ - } + } + } + passed = total - undefined + var owner interface{} + for _, undef := range f.undefined { + if owner != undef.owner { + undefined++ + owner = undef.owner } } var steps, parts, scenarios []string - if passedSt > 0 { - steps = append(steps, green(fmt.Sprintf("%d passed", passedSt))) + nsteps := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending) + if len(f.passed) > 0 { + steps = append(steps, green(fmt.Sprintf("%d passed", len(f.passed)))) } - if failedSt > 0 { - parts = append(parts, red(fmt.Sprintf("%d failed", failedSt))) - steps = append(steps, red(fmt.Sprintf("%d failed", failedSt))) + if len(f.failed) > 0 { + passed -= len(f.failed) + parts = append(parts, red(fmt.Sprintf("%d failed", len(f.failed)))) + steps = append(steps, parts[len(parts)-1]) } - if pendingSt > 0 { - parts = append(parts, yellow(fmt.Sprintf("%d pending", pendingSt))) - steps = append(steps, yellow(fmt.Sprintf("%d pending", pendingSt))) + if len(f.pending) > 0 { + passed -= len(f.pending) + parts = append(parts, yellow(fmt.Sprintf("%d pending", len(f.pending)))) + steps = append(steps, yellow(fmt.Sprintf("%d pending", len(f.pending)))) } - if undefinedSt > 0 { - parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefinedSc))) - steps = append(steps, yellow(fmt.Sprintf("%d undefined", undefinedSt))) - } else if undefinedSc > 0 { + if len(f.undefined) > 0 { + passed -= undefined + parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefined))) + steps = append(steps, yellow(fmt.Sprintf("%d undefined", len(f.undefined)))) + } else if undefined > 0 { // there may be some scenarios without steps - parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefinedSc))) + parts = append(parts, yellow(fmt.Sprintf("%d undefined", undefined))) } - if skippedSt > 0 { - steps = append(steps, cyan(fmt.Sprintf("%d skipped", skippedSt))) + if len(f.skipped) > 0 { + steps = append(steps, cyan(fmt.Sprintf("%d skipped", len(f.skipped)))) } - if passedSc > 0 { - scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passedSc))) + if passed > 0 { + scenarios = append(scenarios, green(fmt.Sprintf("%d passed", passed))) } scenarios = append(scenarios, parts...) elapsed := timeNowFunc().Sub(f.started) fmt.Fprintln(f.out, "") - - if totalSc == 0 { + if total == 0 { fmt.Fprintln(f.out, "No scenarios") } else { - fmt.Fprintln(f.out, fmt.Sprintf("%d scenarios (%s)", totalSc, strings.Join(scenarios, ", "))) + fmt.Fprintln(f.out, fmt.Sprintf("%d scenarios (%s)", total, strings.Join(scenarios, ", "))) } - if totalSt == 0 { + if nsteps == 0 { fmt.Fprintln(f.out, "No steps") } else { - fmt.Fprintln(f.out, fmt.Sprintf("%d steps (%s)", totalSt, strings.Join(steps, ", "))) + fmt.Fprintln(f.out, fmt.Sprintf("%d steps (%s)", nsteps, strings.Join(steps, ", "))) } elapsedString := elapsed.String() @@ -425,6 +484,21 @@ func (f *basefmt) Copy(cf ConcurrentFormatter) { for _, v := range source.features { f.features = append(f.features, v) } + for _, v := range source.failed { + f.failed = append(f.failed, v) + } + for _, v := range source.passed { + f.passed = append(f.passed, v) + } + for _, v := range source.skipped { + f.skipped = append(f.skipped, v) + } + for _, v := range source.undefined { + f.undefined = append(f.undefined, v) + } + for _, v := range source.pending { + f.pending = append(f.pending, v) + } } } @@ -455,13 +529,12 @@ func (s *undefinedSnippet) Args() (ret string) { 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") + switch s.argument.(type) { + case *gherkin.DocString: + args = append(args, "*gherkin.DocString") + case *gherkin.DataTable: + args = append(args, "*gherkin.DataTable") } } @@ -477,30 +550,15 @@ func (s *undefinedSnippet) Args() (ret string) { 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 { - for _, sr := range pr.stepResults { - if sr.status == status { - res = append(res, sr) - } - } - } - } - - return -} - func (f *basefmt) snippets() string { - undefinedStepResults := f.findStepResults(undefined) - if len(undefinedStepResults) == 0 { + if len(f.undefined) == 0 { return "" } var index int var snips []*undefinedSnippet // build snippets - for _, u := range undefinedStepResults { + for _, u := range f.undefined { steps := []string{u.step.Text} arg := u.step.Argument if u.def != nil { @@ -529,7 +587,7 @@ func (f *basefmt) snippets() string { name = strings.Join(words, "") if len(name) == 0 { index++ - name = fmt.Sprintf("StepDefinitioninition%d", index) + name = fmt.Sprintf("stepDefinition%d", index) } var found bool @@ -553,6 +611,25 @@ func (f *basefmt) snippets() string { return strings.Replace(buf.String(), " \n", "\n", -1) } -func isLastStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep) bool { - return pickle.Steps[len(pickle.Steps)-1].Id == step.Id +func (f *basefmt) isLastStep(s *gherkin.Step) bool { + ft := f.features[len(f.features)-1] + + for _, def := range ft.ScenarioDefinitions { + if outline, ok := def.(*gherkin.ScenarioOutline); ok { + for n, step := range outline.Steps { + if step.Location.Line == s.Location.Line { + return n == len(outline.Steps)-1 + } + } + } + + if scenario, ok := def.(*gherkin.Scenario); ok { + for n, step := range scenario.Steps { + if step.Location.Line == s.Location.Line { + return n == len(scenario.Steps)-1 + } + } + } + } + return false } diff --git a/fmt_cucumber.go b/fmt_cucumber.go index 3315fec..5f46376 100644 --- a/fmt_cucumber.go +++ b/fmt_cucumber.go @@ -15,10 +15,11 @@ import ( "encoding/json" "fmt" "io" + "strconv" "strings" "time" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) func init() { @@ -106,7 +107,7 @@ type cukefmt struct { // it restricts this formatter to run only in synchronous single // threaded execution. Unless running a copy of formatter for each feature path string - status stepResultStatus // last step status, before skipped + stat stepType // last step status, before skipped ID string // current test id. results []cukeFeatureJSON // structure that represent cuke results curStep *cukeStep // track the current step @@ -121,86 +122,115 @@ type cukefmt struct { // of the example name inorder to build id fields. } -func (f *cukefmt) Pickle(pickle *messages.Pickle) { - f.basefmt.Pickle(pickle) +func (f *cukefmt) Node(n interface{}) { + f.basefmt.Node(n) - scenario := f.findScenario(pickle.AstNodeIds[0]) + switch t := n.(type) { - f.curFeature.Elements = append(f.curFeature.Elements, cukeElement{}) - f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1] + // When the example definition is seen we just need track the id and + // append the name associated with the example as part of the id. + case *gherkin.Examples: - f.curElement.Name = pickle.Name - f.curElement.Line = int(scenario.Location.Line) - f.curElement.Description = scenario.Description - f.curElement.Keyword = scenario.Keyword - f.curElement.ID = f.curFeature.ID + ";" + makeID(pickle.Name) - f.curElement.Type = "scenario" + f.curExampleName = makeID(t.Name) + f.curRow = 2 // there can be more than one example set per outline so reset row count. + // cucumber counts the header row as an example when creating the id. - f.curElement.Tags = make([]cukeTag, len(scenario.Tags)+len(f.curFeature.Tags)) - - if len(f.curElement.Tags) > 0 { - // apply feature level tags - copy(f.curElement.Tags, f.curFeature.Tags) - - // apply scenario level tags. - for idx, element := range scenario.Tags { - f.curElement.Tags[idx+len(f.curFeature.Tags)].Line = int(element.Location.Line) - f.curElement.Tags[idx+len(f.curFeature.Tags)].Name = element.Name + // store any example level tags in a temp location. + f.curExampleTags = make([]cukeTag, len(t.Tags)) + for idx, element := range t.Tags { + f.curExampleTags[idx].Line = element.Location.Line + f.curExampleTags[idx].Name = element.Name } - } - if len(pickle.AstNodeIds) == 1 { - return - } + // The outline node creates a placeholder and the actual element is added as each TableRow is processed. + case *gherkin.ScenarioOutline: - example, _ := f.findExample(pickle.AstNodeIds[1]) - // apply example level tags. - for _, tag := range example.Tags { - tag := cukeTag{Line: int(tag.Location.Line), Name: tag.Name} - f.curElement.Tags = append(f.curElement.Tags, tag) - } + f.curOutline = cukeElement{} + f.curOutline.Name = t.Name + f.curOutline.Line = t.Location.Line + f.curOutline.Description = t.Description + f.curOutline.Keyword = t.Keyword + f.curOutline.ID = f.curFeature.ID + ";" + makeID(t.Name) + f.curOutline.Type = "scenario" + f.curOutline.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags)) - examples := scenario.GetExamples() - if len(examples) > 0 { - rowID := pickle.AstNodeIds[1] + // apply feature level tags + if len(f.curOutline.Tags) > 0 { + copy(f.curOutline.Tags, f.curFeature.Tags) - for _, example := range examples { - for idx, row := range example.TableBody { - if rowID == row.Id { - f.curElement.ID += fmt.Sprintf(";%s;%d", makeID(example.Name), idx+2) - f.curElement.Line = int(row.Location.Line) - } + // apply outline level tags. + for idx, element := range t.Tags { + f.curOutline.Tags[idx+len(f.curFeature.Tags)].Line = element.Location.Line + f.curOutline.Tags[idx+len(f.curFeature.Tags)].Name = element.Name } } + + // This scenario adds the element to the output immediately. + case *gherkin.Scenario: + f.curFeature.Elements = append(f.curFeature.Elements, cukeElement{}) + f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1] + + f.curElement.Name = t.Name + f.curElement.Line = t.Location.Line + f.curElement.Description = t.Description + f.curElement.Keyword = t.Keyword + f.curElement.ID = f.curFeature.ID + ";" + makeID(t.Name) + f.curElement.Type = "scenario" + f.curElement.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags)) + + if len(f.curElement.Tags) > 0 { + // apply feature level tags + copy(f.curElement.Tags, f.curFeature.Tags) + + // apply scenario level tags. + for idx, element := range t.Tags { + f.curElement.Tags[idx+len(f.curFeature.Tags)].Line = element.Location.Line + f.curElement.Tags[idx+len(f.curFeature.Tags)].Name = element.Name + } + } + + // This is an outline scenario and the element is added to the output as + // the TableRows are encountered. + case *gherkin.TableRow: + tmpElem := f.curOutline + tmpElem.Line = t.Location.Line + tmpElem.ID = tmpElem.ID + ";" + f.curExampleName + ";" + strconv.Itoa(f.curRow) + f.curRow++ + f.curFeature.Elements = append(f.curFeature.Elements, tmpElem) + f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1] + + // copy in example level tags. + f.curElement.Tags = append(f.curElement.Tags, f.curExampleTags...) + } } -func (f *cukefmt) Feature(gd *messages.GherkinDocument, p string, c []byte) { - f.basefmt.Feature(gd, p, c) +func (f *cukefmt) Feature(ft *gherkin.Feature, p string, c []byte) { + f.basefmt.Feature(ft, p, c) f.path = p - f.ID = makeID(gd.Feature.Name) + f.ID = makeID(ft.Name) f.results = append(f.results, cukeFeatureJSON{}) f.curFeature = &f.results[len(f.results)-1] f.curFeature.URI = p - f.curFeature.Name = gd.Feature.Name - f.curFeature.Keyword = gd.Feature.Keyword - f.curFeature.Line = int(gd.Feature.Location.Line) - f.curFeature.Description = gd.Feature.Description + f.curFeature.Name = ft.Name + f.curFeature.Keyword = ft.Keyword + f.curFeature.Line = ft.Location.Line + f.curFeature.Description = ft.Description f.curFeature.ID = f.ID - f.curFeature.Tags = make([]cukeTag, len(gd.Feature.Tags)) + f.curFeature.Tags = make([]cukeTag, len(ft.Tags)) - for idx, element := range gd.Feature.Tags { - f.curFeature.Tags[idx].Line = int(element.Location.Line) + for idx, element := range ft.Tags { + f.curFeature.Tags[idx].Line = element.Location.Line f.curFeature.Tags[idx].Name = element.Name } - f.curFeature.Comments = make([]cukeComment, len(gd.Comments)) - for idx, comment := range gd.Comments { + f.curFeature.Comments = make([]cukeComment, len(ft.Comments)) + for idx, comment := range ft.Comments { f.curFeature.Comments[idx].Value = strings.TrimSpace(comment.Text) - f.curFeature.Comments[idx].Line = int(comment.Location.Line) + f.curFeature.Comments[idx].Line = comment.Location.Line } } @@ -214,43 +244,49 @@ func (f *cukefmt) Summary() { } func (f *cukefmt) step(res *stepResult) { - d := int(timeNowFunc().Sub(f.startTime).Nanoseconds()) - f.curStep.Result.Duration = &d - f.curStep.Result.Status = res.status.String() - if res.err != nil { - f.curStep.Result.Error = res.err.Error() + + // determine if test case has finished + switch t := f.owner.(type) { + case *gherkin.TableRow: + d := int(timeNowFunc().Sub(f.startTime).Nanoseconds()) + f.curStep.Result.Duration = &d + f.curStep.Line = t.Location.Line + f.curStep.Result.Status = res.typ.String() + if res.err != nil { + f.curStep.Result.Error = res.err.Error() + } + case *gherkin.Scenario: + d := int(timeNowFunc().Sub(f.startTime).Nanoseconds()) + f.curStep.Result.Duration = &d + f.curStep.Result.Status = res.typ.String() + if res.err != nil { + f.curStep.Result.Error = res.err.Error() + } } } -func (f *cukefmt) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) { +func (f *cukefmt) Defined(step *gherkin.Step, def *StepDef) { + f.startTime = timeNowFunc() // start timing the step f.curElement.Steps = append(f.curElement.Steps, cukeStep{}) f.curStep = &f.curElement.Steps[len(f.curElement.Steps)-1] - step := f.findStep(pickleStep.AstNodeIds[0]) - - line := step.Location.Line - if len(pickle.AstNodeIds) == 2 { - _, row := f.findExample(pickle.AstNodeIds[1]) - line = row.Location.Line - } - - f.curStep.Name = pickleStep.Text - f.curStep.Line = int(line) + f.curStep.Name = step.Text + f.curStep.Line = step.Location.Line f.curStep.Keyword = step.Keyword - arg := pickleStep.Argument - - if arg.GetDocString() != nil && step.GetDocString() != nil { + if _, ok := step.Argument.(*gherkin.DocString); ok { f.curStep.Docstring = &cukeDocstring{} - f.curStep.Docstring.ContentType = strings.TrimSpace(arg.GetDocString().MediaType) - f.curStep.Docstring.Line = int(step.GetDocString().Location.Line) - f.curStep.Docstring.Value = arg.GetDocString().Content + f.curStep.Docstring.ContentType = strings.TrimSpace(step.Argument.(*gherkin.DocString).ContentType) + f.curStep.Docstring.Line = step.Argument.(*gherkin.DocString).Location.Line + f.curStep.Docstring.Value = step.Argument.(*gherkin.DocString).Content } - if arg.GetDataTable() != nil { - f.curStep.DataTable = make([]*cukeDataTableRow, len(arg.GetDataTable().Rows)) - for i, row := range arg.GetDataTable().Rows { + if _, ok := step.Argument.(*gherkin.DataTable); ok { + dataTable := step.Argument.(*gherkin.DataTable) + + f.curStep.DataTable = make([]*cukeDataTableRow, len(dataTable.Rows)) + for i, row := range dataTable.Rows { cells := make([]string, len(row.Cells)) for j, cell := range row.Cells { cells[j] = cell.Value @@ -264,47 +300,42 @@ func (f *cukefmt) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_P } } -func (f *cukefmt) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Passed(pickle, step, match) - - f.status = passed - f.step(f.lastStepResult()) +func (f *cukefmt) Passed(step *gherkin.Step, match *StepDef) { + f.basefmt.Passed(step, match) + f.stat = passed + f.step(f.passed[len(f.passed)-1]) } -func (f *cukefmt) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Skipped(pickle, step, match) - - f.step(f.lastStepResult()) +func (f *cukefmt) Skipped(step *gherkin.Step, match *StepDef) { + f.basefmt.Skipped(step, match) + f.step(f.skipped[len(f.skipped)-1]) // no duration reported for skipped. f.curStep.Result.Duration = nil } -func (f *cukefmt) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Undefined(pickle, step, match) - - f.status = undefined - f.step(f.lastStepResult()) +func (f *cukefmt) Undefined(step *gherkin.Step, match *StepDef) { + f.basefmt.Undefined(step, match) + f.stat = undefined + f.step(f.undefined[len(f.undefined)-1]) // the location for undefined is the feature file location not the step file. - f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, f.findStep(step.AstNodeIds[0]).Location.Line) + f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line) f.curStep.Result.Duration = nil } -func (f *cukefmt) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { - f.basefmt.Failed(pickle, step, match, err) - - f.status = failed - f.step(f.lastStepResult()) +func (f *cukefmt) Failed(step *gherkin.Step, match *StepDef, err error) { + f.basefmt.Failed(step, match, err) + f.stat = failed + f.step(f.failed[len(f.failed)-1]) } -func (f *cukefmt) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Pending(pickle, step, match) - - f.status = pending - f.step(f.lastStepResult()) +func (f *cukefmt) Pending(step *gherkin.Step, match *StepDef) { + f.stat = pending + f.basefmt.Pending(step, match) + f.step(f.pending[len(f.pending)-1]) // the location for pending is the feature file location not the step file. - f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, f.findStep(step.AstNodeIds[0]).Location.Line) + f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line) f.curStep.Result.Duration = nil } diff --git a/fmt_events.go b/fmt_events.go index 6f31086..19ffa92 100644 --- a/fmt_events.go +++ b/fmt_events.go @@ -5,7 +5,7 @@ import ( "fmt" "io" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) const nanoSec = 1000000 @@ -41,8 +41,8 @@ type events struct { // it restricts this formatter to run only in synchronous single // threaded execution. Unless running a copy of formatter for each feature path string - status stepResultStatus // last step status, before skipped - outlineSteps int // number of current outline scenario steps + stat stepType // last step status, before skipped + outlineSteps int // number of current outline scenario steps } func (f *events) event(ev interface{}) { @@ -53,8 +53,25 @@ func (f *events) event(ev interface{}) { fmt.Fprintln(f.out, string(data)) } -func (f *events) Pickle(pickle *messages.Pickle) { - f.basefmt.Pickle(pickle) +func (f *events) Node(n interface{}) { + f.basefmt.Node(n) + + var id string + var undefined bool + switch t := n.(type) { + case *gherkin.Scenario: + id = fmt.Sprintf("%s:%d", f.path, t.Location.Line) + undefined = len(t.Steps) == 0 + case *gherkin.TableRow: + id = fmt.Sprintf("%s:%d", f.path, t.Location.Line) + undefined = f.outlineSteps == 0 + case *gherkin.ScenarioOutline: + f.outlineSteps = len(t.Steps) + } + + if len(id) == 0 { + return + } f.event(&struct { Event string `json:"event"` @@ -62,11 +79,11 @@ func (f *events) Pickle(pickle *messages.Pickle) { Timestamp int64 `json:"timestamp"` }{ "TestCaseStarted", - f.scenarioLocation(pickle.AstNodeIds), + id, timeNowFunc().UnixNano() / nanoSec, }) - if len(pickle.Steps) == 0 { + if undefined { // @TODO: is status undefined or passed? when there are no steps // for this scenario f.event(&struct { @@ -76,14 +93,14 @@ func (f *events) Pickle(pickle *messages.Pickle) { Status string `json:"status"` }{ "TestCaseFinished", - f.scenarioLocation(pickle.AstNodeIds), + id, timeNowFunc().UnixNano() / nanoSec, "undefined", }) } } -func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) { +func (f *events) Feature(ft *gherkin.Feature, p string, c []byte) { f.basefmt.Feature(ft, p, c) f.path = p f.event(&struct { @@ -92,7 +109,7 @@ func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) { Source string `json:"source"` }{ "TestSource", - fmt.Sprintf("%s:%d", p, ft.Feature.Location.Line), + fmt.Sprintf("%s:%d", p, ft.Location.Line), string(c), }) } @@ -100,10 +117,10 @@ func (f *events) Feature(ft *messages.GherkinDocument, p string, c []byte) { func (f *events) Summary() { // @TODO: determine status status := passed - if len(f.findStepResults(failed)) > 0 { + if len(f.failed) > 0 { status = failed - } else if len(f.findStepResults(passed)) == 0 { - if len(f.findStepResults(undefined)) > len(f.findStepResults(pending)) { + } else if len(f.passed) == 0 { + if len(f.undefined) > len(f.pending) { status = undefined } else { status = pending @@ -131,8 +148,6 @@ func (f *events) Summary() { } func (f *events) step(res *stepResult) { - step := f.findStep(res.step.AstNodeIds[0]) - var errMsg string if res.err != nil { errMsg = res.err.Error() @@ -145,13 +160,25 @@ func (f *events) step(res *stepResult) { Summary string `json:"summary,omitempty"` }{ "TestStepFinished", - fmt.Sprintf("%s:%d", f.path, step.Location.Line), + fmt.Sprintf("%s:%d", f.path, res.step.Location.Line), timeNowFunc().UnixNano() / nanoSec, - res.status.String(), + res.typ.String(), errMsg, }) - if isLastStep(res.owner, res.step) { + // determine if test case has finished + var finished bool + var line int + switch t := f.owner.(type) { + case *gherkin.TableRow: + line = t.Location.Line + finished = f.isLastStep(res.step) + case *gherkin.Scenario: + line = t.Location.Line + finished = f.isLastStep(res.step) + } + + if finished { f.event(&struct { Event string `json:"event"` Location string `json:"location"` @@ -159,18 +186,16 @@ func (f *events) step(res *stepResult) { Status string `json:"status"` }{ "TestCaseFinished", - f.scenarioLocation(res.owner.AstNodeIds), + fmt.Sprintf("%s:%d", f.path, line), timeNowFunc().UnixNano() / nanoSec, - f.status.String(), + f.stat.String(), }) } } -func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_PickleStep, def *StepDefinition) { - step := f.findStep(pickleStep.AstNodeIds[0]) - +func (f *events) Defined(step *gherkin.Step, def *StepDef) { if def != nil { - m := def.Expr.FindStringSubmatchIndex(pickleStep.Text)[2:] + m := def.Expr.FindStringSubmatchIndex(step.Text)[2:] var args [][2]int for i := 0; i < len(m)/2; i++ { pair := m[i : i*2+2] @@ -208,47 +233,31 @@ func (f *events) Defined(pickle *messages.Pickle, pickleStep *messages.Pickle_Pi }) } -func (f *events) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Passed(pickle, step, match) - - f.status = passed - f.step(f.lastStepResult()) +func (f *events) Passed(step *gherkin.Step, match *StepDef) { + f.basefmt.Passed(step, match) + f.stat = passed + f.step(f.passed[len(f.passed)-1]) } -func (f *events) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Skipped(pickle, step, match) - - f.step(f.lastStepResult()) +func (f *events) Skipped(step *gherkin.Step, match *StepDef) { + f.basefmt.Skipped(step, match) + f.step(f.skipped[len(f.skipped)-1]) } -func (f *events) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Undefined(pickle, step, match) - - f.status = undefined - f.step(f.lastStepResult()) +func (f *events) Undefined(step *gherkin.Step, match *StepDef) { + f.basefmt.Undefined(step, match) + f.stat = undefined + f.step(f.undefined[len(f.undefined)-1]) } -func (f *events) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { - f.basefmt.Failed(pickle, step, match, err) - - f.status = failed - f.step(f.lastStepResult()) +func (f *events) Failed(step *gherkin.Step, match *StepDef, err error) { + f.basefmt.Failed(step, match, err) + f.stat = failed + f.step(f.failed[len(f.failed)-1]) } -func (f *events) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Pending(pickle, step, match) - - f.status = pending - f.step(f.lastStepResult()) -} - -func (f *events) scenarioLocation(astNodeIds []string) string { - scenario := f.findScenario(astNodeIds[0]) - line := scenario.Location.Line - if len(astNodeIds) == 2 { - _, row := f.findExample(astNodeIds[1]) - line = row.Location.Line - } - - return fmt.Sprintf("%s:%d", f.path, line) +func (f *events) Pending(step *gherkin.Step, match *StepDef) { + f.stat = pending + f.basefmt.Pending(step, match) + f.step(f.pending[len(f.pending)-1]) } diff --git a/fmt_junit.go b/fmt_junit.go index 8d901c0..b5b827a 100644 --- a/fmt_junit.go +++ b/fmt_junit.go @@ -64,55 +64,45 @@ func buildJUNITPackageSuite(suiteName string, startedAt time.Time, features []*f for idx, feat := range features { ts := junitTestSuite{ - Name: feat.GherkinDocument.Feature.Name, + Name: feat.Name, Time: junitTimeDuration(feat.startedAt(), feat.finishedAt()), - TestCases: make([]*junitTestCase, len(feat.pickleResults)), + TestCases: make([]*junitTestCase, len(feat.Scenarios)), } - var testcaseNames = make(map[string]int) - for _, pickleResult := range feat.pickleResults { - testcaseNames[pickleResult.Name] = testcaseNames[pickleResult.Name] + 1 - } - - var outlineNo = make(map[string]int) - for idx, pickleResult := range feat.pickleResults { - tc := junitTestCase{} - tc.Time = junitTimeDuration(pickleResult.startedAt(), pickleResult.finishedAt()) - - tc.Name = pickleResult.Name - if testcaseNames[tc.Name] > 1 { - outlineNo[tc.Name] = outlineNo[tc.Name] + 1 - tc.Name += fmt.Sprintf(" #%d", outlineNo[tc.Name]) + for idx, scenario := range feat.Scenarios { + tc := junitTestCase{ + Name: scenario.Name, + Time: junitTimeDuration(scenario.startedAt(), scenario.finishedAt()), } ts.Tests++ suite.Tests++ - for _, stepResult := range pickleResult.stepResults { - switch stepResult.status { + for _, step := range scenario.Steps { + switch step.typ { case passed: tc.Status = passed.String() case failed: tc.Status = failed.String() tc.Failure = &junitFailure{ - Message: fmt.Sprintf("Step %s: %s", stepResult.step.Text, stepResult.err), + Message: fmt.Sprintf("%s %s: %s", step.step.Type, step.step.Text, step.err), } case skipped: tc.Error = append(tc.Error, &junitError{ Type: "skipped", - Message: fmt.Sprintf("Step %s", stepResult.step.Text), + Message: fmt.Sprintf("%s %s", step.step.Type, step.step.Text), }) case undefined: tc.Status = undefined.String() tc.Error = append(tc.Error, &junitError{ Type: "undefined", - Message: fmt.Sprintf("Step %s", stepResult.step.Text), + Message: fmt.Sprintf("%s %s", step.step.Type, step.step.Text), }) case pending: tc.Status = pending.String() tc.Error = append(tc.Error, &junitError{ Type: "pending", - Message: fmt.Sprintf("Step %s: TODO: write pending definition", stepResult.step.Text), + Message: fmt.Sprintf("%s %s: TODO: write pending definition", step.step.Type, step.step.Text), }) } } diff --git a/fmt_junit_test.go b/fmt_junit_test.go index ac50648..137923c 100644 --- a/fmt_junit_test.go +++ b/fmt_junit_test.go @@ -8,12 +8,8 @@ import ( "strings" "testing" - "github.com/cucumber/gherkin-go/v9" - "github.com/cucumber/messages-go/v9" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/gherkin" ) var sampleGherkinFeature = ` @@ -53,22 +49,19 @@ Feature: junit formatter ` func TestJUnitFormatterOutput(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(sampleGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(sampleGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) s := &Suite{ fmt: junitFunc("junit", w), - features: []*feature{{ - GherkinDocument: gd, - pickles: pickles, - Path: path, - Content: []byte(sampleGherkinFeature), + features: []*feature{&feature{ + Path: "any.feature", + Feature: feat, + Content: []byte(sampleGherkinFeature), }}, } @@ -158,18 +151,19 @@ func TestJUnitFormatterOutput(t *testing.T) { }, }}, } - s.run() s.fmt.Summary() var exp bytes.Buffer - _, err = io.WriteString(&exp, xml.Header) - require.NoError(t, err) - + if _, err = io.WriteString(&exp, xml.Header); err != nil { + t.Fatalf("unexpected error: %v", err) + } enc := xml.NewEncoder(&exp) enc.Indent("", " ") - err = enc.Encode(expected) - require.NoError(t, err) - - assert.Equal(t, exp.String(), buf.String()) + if err = enc.Encode(expected); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if buf.String() != exp.String() { + t.Fatalf("expected output does not match: %s", buf.String()) + } } diff --git a/fmt_pretty.go b/fmt_pretty.go index 6037d5b..bef7db0 100644 --- a/fmt_pretty.go +++ b/fmt_pretty.go @@ -3,13 +3,13 @@ package godog import ( "fmt" "io" + "math" "regexp" "strings" "unicode/utf8" - "github.com/cucumber/messages-go/v9" - "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/gherkin" ) func init() { @@ -25,59 +25,79 @@ var outlinePlaceholderRegexp = regexp.MustCompile("<[^>]+>") // a built in default pretty formatter type pretty struct { *basefmt + + // currently processed + feature *gherkin.Feature + scenario *gherkin.Scenario + outline *gherkin.ScenarioOutline + + // state + bgSteps int + totalBgSteps int + steps int + commentPos int + + // whether scenario or scenario outline keyword was printed + scenarioKeyword bool + + // outline + outlineSteps []*stepResult + outlineNumExample int + outlineNumExamples int } -func (f *pretty) Feature(gd *messages.GherkinDocument, p string, c []byte) { - f.basefmt.Feature(gd, p, c) - f.printFeature(gd.Feature) -} - -// Pickle takes a gherkin node for formatting -func (f *pretty) Pickle(pickle *messages.Pickle) { - f.basefmt.Pickle(pickle) - - if len(pickle.Steps) == 0 { - f.printUndefinedPickle(pickle) - return +func (f *pretty) Feature(ft *gherkin.Feature, p string, c []byte) { + if len(f.features) != 0 { + // not a first feature, add a newline + fmt.Fprintln(f.out, "") } -} - -func (f *pretty) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Passed(pickle, step, match) - f.printStep(f.lastStepResult()) -} - -func (f *pretty) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Skipped(pickle, step, match) - f.printStep(f.lastStepResult()) -} - -func (f *pretty) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Undefined(pickle, step, match) - f.printStep(f.lastStepResult()) -} - -func (f *pretty) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { - f.basefmt.Failed(pickle, step, match, err) - f.printStep(f.lastStepResult()) -} - -func (f *pretty) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Pending(pickle, step, match) - f.printStep(f.lastStepResult()) -} - -func (f *pretty) printFeature(feature *messages.GherkinDocument_Feature) { - if len(f.features) > 1 { - fmt.Fprintln(f.out, "") // not a first feature, add a newline - } - - fmt.Fprintln(f.out, keywordAndName(feature.Keyword, feature.Name)) - if strings.TrimSpace(feature.Description) != "" { - for _, line := range strings.Split(feature.Description, "\n") { + f.features = append(f.features, &feature{Path: p, Feature: ft}) + fmt.Fprintln(f.out, keywordAndName(ft.Keyword, ft.Name)) + if strings.TrimSpace(ft.Description) != "" { + for _, line := range strings.Split(ft.Description, "\n") { fmt.Fprintln(f.out, s(f.indent)+strings.TrimSpace(line)) } } + + f.feature = ft + f.scenario = nil + f.outline = nil + f.bgSteps = 0 + f.totalBgSteps = 0 + if ft.Background != nil { + f.bgSteps = len(ft.Background.Steps) + f.totalBgSteps = len(ft.Background.Steps) + } +} + +// Node takes a gherkin node for formatting +func (f *pretty) Node(node interface{}) { + f.basefmt.Node(node) + + switch t := node.(type) { + case *gherkin.Examples: + f.outlineNumExamples = len(t.TableBody) + f.outlineNumExample++ + case *gherkin.Scenario: + f.scenario = t + f.outline = nil + f.steps = len(t.Steps) + f.totalBgSteps + f.scenarioKeyword = false + if isEmptyScenario(t) { + f.printUndefinedScenario(t) + } + case *gherkin.ScenarioOutline: + f.outline = t + f.scenario = nil + f.outlineNumExample = -1 + f.scenarioKeyword = false + if isEmptyScenario(t) { + f.printUndefinedScenario(t) + } + case *gherkin.TableRow: + f.steps = len(f.outline.Steps) + f.totalBgSteps + f.outlineSteps = []*stepResult{} + } } func keywordAndName(keyword, name string) string { @@ -88,302 +108,342 @@ func keywordAndName(keyword, name string) string { return title } -func (f *pretty) scenarioLengths(scenarioAstID string) (scenarioHeaderLength int, maxLength int) { - astScenario := f.findScenario(scenarioAstID) - astBackground := f.findBackground(scenarioAstID) +func (f *pretty) printUndefinedScenario(sc interface{}) { + if f.bgSteps > 0 { + bg := f.feature.Background + f.commentPos = f.longestStep(bg.Steps, f.length(bg)) + fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(bg.Keyword, bg.Name)) - scenarioHeaderLength = f.lengthPickle(astScenario.Keyword, astScenario.Name) - maxLength = f.longestStep(astScenario.Steps, scenarioHeaderLength) - - if astBackground != nil { - maxLength = f.longestStep(astBackground.Steps, maxLength) - } - - return scenarioHeaderLength, maxLength -} - -func (f *pretty) printScenarioHeader(astScenario *messages.GherkinDocument_Feature_Scenario, spaceFilling int) { - text := s(f.indent) + keywordAndName(astScenario.Keyword, astScenario.Name) - text += s(spaceFilling) + f.line(astScenario.Location) - fmt.Fprintln(f.out, "\n"+text) -} - -func (f *pretty) printUndefinedPickle(pickle *messages.Pickle) { - astScenario := f.findScenario(pickle.AstNodeIds[0]) - astBackground := f.findBackground(pickle.AstNodeIds[0]) - - scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) - - if astBackground != nil { - fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name)) - for _, step := range astBackground.Steps { - text := s(f.indent*2) + cyan(strings.TrimSpace(step.Keyword)) + " " + cyan(step.Text) - fmt.Fprintln(f.out, text) + for _, step := range bg.Steps { + f.bgSteps-- + f.printStep(step, nil, colors.Cyan) } } - // do not print scenario headers and examples multiple times - if len(astScenario.Examples) > 0 { - exampleTable, exampleRow := f.findExample(pickle.AstNodeIds[1]) - firstExampleRow := exampleTable.TableBody[0].Id == exampleRow.Id - firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line + switch t := sc.(type) { + case *gherkin.Scenario: + f.commentPos = f.longestStep(t.Steps, f.length(sc)) + text := s(f.indent) + keywordAndName(t.Keyword, t.Name) + text += s(f.commentPos-f.length(t)+1) + f.line(t.Location) + fmt.Fprintln(f.out, "\n"+text) + case *gherkin.ScenarioOutline: + f.commentPos = f.longestStep(t.Steps, f.length(sc)) + text := s(f.indent) + keywordAndName(t.Keyword, t.Name) + text += s(f.commentPos-f.length(t)+1) + f.line(t.Location) + fmt.Fprintln(f.out, "\n"+text) - if !(firstExamplesTable && firstExampleRow) { - return - } - } - - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) - - for _, examples := range astScenario.Examples { - max := longestExampleRow(examples, cyan, cyan) - - fmt.Fprintln(f.out, "") - fmt.Fprintln(f.out, s(f.indent*2)+keywordAndName(examples.Keyword, examples.Name)) - - f.printTableHeader(examples.TableHeader, max) - - for _, row := range examples.TableBody { - f.printTableRow(row, max, cyan) + for _, example := range t.Examples { + max := longest(example, cyan) + f.printExampleHeader(example, max) + for _, row := range example.TableBody { + f.printExampleRow(row, max, cyan) + } } } } // Summary sumarize the feature formatter output func (f *pretty) Summary() { - failedStepResults := f.findStepResults(failed) - if len(failedStepResults) > 0 { + if len(f.failed) > 0 { fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n") - for _, fail := range failedStepResults { - astScenario := f.findScenario(fail.owner.AstNodeIds[0]) - scenarioDesc := fmt.Sprintf("%s: %s", astScenario.Keyword, fail.owner.Name) - - astStep := f.findStep(fail.step.AstNodeIds[0]) - stepDesc := strings.TrimSpace(astStep.Keyword) + " " + fail.step.Text - - fmt.Fprintln(f.out, s(f.indent)+red(scenarioDesc)+f.line(astScenario.Location)) - fmt.Fprintln(f.out, s(f.indent*2)+red(stepDesc)+f.line(astStep.Location)) - fmt.Fprintln(f.out, s(f.indent*3)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n") + for _, fail := range f.failed { + fmt.Fprintln(f.out, s(2)+red(fail.scenarioDesc())+blackb(" # "+fail.scenarioLine())) + fmt.Fprintln(f.out, s(4)+red(strings.TrimSpace(fail.step.Keyword)+" "+fail.step.Text)+blackb(" # "+fail.line())) + fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n") } } - f.basefmt.Summary() } -func (f *pretty) printOutlineExample(pickle *messages.Pickle, backgroundSteps int) { - var errorMsg string - var clr = green +func (f *pretty) printOutlineExample(outline *gherkin.ScenarioOutline) { + var msg string + var clr colors.ColorFunc - astScenario := f.findScenario(pickle.AstNodeIds[0]) - scenarioHeaderLength, maxLength := f.scenarioLengths(pickle.AstNodeIds[0]) - - exampleTable, exampleRow := f.findExample(pickle.AstNodeIds[1]) - printExampleHeader := exampleTable.TableBody[0].Id == exampleRow.Id - firstExamplesTable := astScenario.Examples[0].Location.Line == exampleTable.Location.Line - - firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == backgroundSteps+1 - if firstExamplesTable && printExampleHeader && firstExecutedScenarioStep { - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) - } - - if len(exampleTable.TableBody) == 0 { + ex := outline.Examples[f.outlineNumExample] + example, hasExamples := examples(ex) + if !hasExamples { // do not print empty examples return } - lastStep := len(f.lastFeature().lastPickleResult().stepResults) == len(pickle.Steps) - if !lastStep { - // do not print examples unless all steps has finished - return - } + firstExample := f.outlineNumExamples == len(example.TableBody) + printSteps := firstExample && f.outlineNumExample == 0 - for _, result := range f.lastFeature().lastPickleResult().stepResults { + for i, res := range f.outlineSteps { // determine example row status switch { - case result.status == failed: - errorMsg = result.err.Error() - clr = result.status.clr() - case result.status == undefined || result.status == pending: - clr = result.status.clr() - case result.status == skipped && clr == nil: + case res.typ == failed: + msg = res.err.Error() + clr = res.typ.clr() + case res.typ == undefined || res.typ == pending: + clr = res.typ.clr() + case res.typ == skipped && clr == nil: clr = cyan } - - if firstExamplesTable && printExampleHeader { + if printSteps && i >= f.totalBgSteps { // in first example, we need to print steps var text string - - astStep := f.findStep(result.step.AstNodeIds[0]) - - if result.def != nil { - if m := outlinePlaceholderRegexp.FindAllStringIndex(astStep.Text, -1); len(m) > 0 { + ostep := outline.Steps[i-f.totalBgSteps] + if res.def != nil { + if m := outlinePlaceholderRegexp.FindAllStringIndex(ostep.Text, -1); len(m) > 0 { var pos int for i := 0; i < len(m); i++ { pair := m[i] - text += cyan(astStep.Text[pos:pair[0]]) - text += cyanb(astStep.Text[pair[0]:pair[1]]) + text += cyan(ostep.Text[pos:pair[0]]) + text += cyanb(ostep.Text[pair[0]:pair[1]]) pos = pair[1] } - text += cyan(astStep.Text[pos:len(astStep.Text)]) + text += cyan(ostep.Text[pos:len(ostep.Text)]) } else { - text = cyan(astStep.Text) + text = cyan(ostep.Text) } - - _, maxLength := f.scenarioLengths(result.owner.AstNodeIds[0]) - stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text) - - text += s(maxLength - stepLength) - text += " " + blackb("# "+result.def.definitionID()) + text += s(f.commentPos-f.length(ostep)+1) + blackb(fmt.Sprintf("# %s", res.def.definitionID())) } else { - text = cyan(astStep.Text) + text = cyan(ostep.Text) } // print the step outline - fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(astStep.Keyword))+" "+text) + fmt.Fprintln(f.out, s(f.indent*2)+cyan(strings.TrimSpace(ostep.Keyword))+" "+text) - if table := result.step.Argument.GetDataTable(); table != nil { - f.printTable(table, cyan) - } - - if docString := astStep.GetDocString(); docString != nil { - f.printDocString(docString) + // print step argument + // @TODO: need to make example header cells bold + switch t := ostep.Argument.(type) { + case *gherkin.DataTable: + f.printTable(t, cyan) + case *gherkin.DocString: + var ct string + if len(t.ContentType) > 0 { + ct = " " + cyan(t.ContentType) + } + fmt.Fprintln(f.out, s(f.indent*3)+cyan(t.Delimitter)+ct) + for _, ln := range strings.Split(t.Content, "\n") { + fmt.Fprintln(f.out, s(f.indent*3)+cyan(ln)) + } + fmt.Fprintln(f.out, s(f.indent*3)+cyan(t.Delimitter)) } } } - max := longestExampleRow(exampleTable, clr, cyan) - - // an example table header - if printExampleHeader { - fmt.Fprintln(f.out, "") - fmt.Fprintln(f.out, s(f.indent*2)+keywordAndName(exampleTable.Keyword, exampleTable.Name)) - - f.printTableHeader(exampleTable.TableHeader, max) + if clr == nil { + clr = green } - f.printTableRow(exampleRow, max, clr) + max := longest(example, clr, cyan) + // an example table header + if firstExample { + f.printExampleHeader(example, max) + } - if errorMsg != "" { - fmt.Fprintln(f.out, s(f.indent*4)+redb(errorMsg)) + // an example table row + row := example.TableBody[len(example.TableBody)-f.outlineNumExamples] + f.printExampleRow(row, max, clr) + + // if there is an error + if msg != "" { + fmt.Fprintln(f.out, s(f.indent*4)+redb(msg)) } } -func (f *pretty) printTableRow(row *messages.GherkinDocument_Feature_TableRow, max []int, clr colors.ColorFunc) { +func (f *pretty) printExampleRow(row *gherkin.TableRow, max []int, clr colors.ColorFunc) { cells := make([]string, len(row.Cells)) - for i, cell := range row.Cells { val := clr(cell.Value) ln := utf8.RuneCountInString(val) cells[i] = val + s(max[i]-ln) } - fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |") } -func (f *pretty) printTableHeader(row *messages.GherkinDocument_Feature_TableRow, max []int) { - f.printTableRow(row, max, cyan) +func (f *pretty) printExampleHeader(example *gherkin.Examples, max []int) { + cells := make([]string, len(example.TableHeader.Cells)) + // an example table header + fmt.Fprintln(f.out, "") + fmt.Fprintln(f.out, s(f.indent*2)+keywordAndName(example.Keyword, example.Name)) + + for i, cell := range example.TableHeader.Cells { + val := cyan(cell.Value) + ln := utf8.RuneCountInString(val) + cells[i] = val + s(max[i]-ln) + } + fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cells, " | ")+" |") } -func (f *pretty) printStep(result *stepResult) { - astBackground := f.findBackground(result.owner.AstNodeIds[0]) - astScenario := f.findScenario(result.owner.AstNodeIds[0]) - astStep := f.findStep(result.step.AstNodeIds[0]) - - var backgroundSteps int - if astBackground != nil { - backgroundSteps = len(astBackground.Steps) +func (f *pretty) printStep(step *gherkin.Step, def *StepDef, c colors.ColorFunc) { + text := s(f.indent*2) + c(strings.TrimSpace(step.Keyword)) + " " + switch { + case def != nil: + if m := def.Expr.FindStringSubmatchIndex(step.Text)[2:]; len(m) > 0 { + var pos, i int + for pos, i = 0, 0; i < len(m); i++ { + if m[i] == -1 { + continue // no index for this match + } + if math.Mod(float64(i), 2) == 0 { + text += c(step.Text[pos:m[i]]) + } else { + text += colors.Bold(c)(step.Text[pos:m[i]]) + } + pos = m[i] + } + text += c(step.Text[pos:len(step.Text)]) + } else { + text += c(step.Text) + } + text += s(f.commentPos-f.length(step)+1) + blackb(fmt.Sprintf("# %s", def.definitionID())) + default: + text += c(step.Text) } - astBackgroundStep := backgroundSteps > 0 && backgroundSteps >= len(f.lastFeature().lastPickleResult().stepResults) - - if astBackgroundStep { - if len(f.lastFeature().pickleResults) > 1 { - return + fmt.Fprintln(f.out, text) + switch t := step.Argument.(type) { + case *gherkin.DataTable: + f.printTable(t, c) + case *gherkin.DocString: + var ct string + if len(t.ContentType) > 0 { + ct = " " + c(t.ContentType) } - - firstExecutedBackgroundStep := astBackground != nil && len(f.lastFeature().lastPickleResult().stepResults) == 1 - if firstExecutedBackgroundStep { - fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(astBackground.Keyword, astBackground.Name)) + fmt.Fprintln(f.out, s(f.indent*3)+c(t.Delimitter)+ct) + for _, ln := range strings.Split(t.Content, "\n") { + fmt.Fprintln(f.out, s(f.indent*3)+c(ln)) } + fmt.Fprintln(f.out, s(f.indent*3)+c(t.Delimitter)) } +} - if !astBackgroundStep && len(astScenario.Examples) > 0 { - f.printOutlineExample(result.owner, backgroundSteps) +func (f *pretty) printStepKind(res *stepResult) { + f.steps-- + if f.outline != nil { + f.outlineSteps = append(f.outlineSteps, res) + } + var bgStep bool + bg := f.feature.Background + + // if has not printed background yet + switch { + // first background step + case f.bgSteps > 0 && f.bgSteps == len(bg.Steps): + f.commentPos = f.longestStep(bg.Steps, f.length(bg)) + fmt.Fprintln(f.out, "\n"+s(f.indent)+keywordAndName(bg.Keyword, bg.Name)) + f.bgSteps-- + bgStep = true + // subsequent background steps + case f.bgSteps > 0: + f.bgSteps-- + bgStep = true + // first step of scenario, print header and calculate comment position + case f.scenario != nil: + // print scenario keyword and value if first example + if !f.scenarioKeyword { + f.commentPos = f.longestStep(f.scenario.Steps, f.length(f.scenario)) + if bg != nil { + if bgLen := f.longestStep(bg.Steps, f.length(bg)); bgLen > f.commentPos { + f.commentPos = bgLen + } + } + text := s(f.indent) + keywordAndName(f.scenario.Keyword, f.scenario.Name) + text += s(f.commentPos-f.length(f.scenario)+1) + f.line(f.scenario.Location) + fmt.Fprintln(f.out, "\n"+text) + f.scenarioKeyword = true + } + // first step of outline scenario, print header and calculate comment position + case f.outline != nil: + // print scenario keyword and value if first example + if !f.scenarioKeyword { + f.commentPos = f.longestStep(f.outline.Steps, f.length(f.outline)) + if bg != nil { + if bgLen := f.longestStep(bg.Steps, f.length(bg)); bgLen > f.commentPos { + f.commentPos = bgLen + } + } + text := s(f.indent) + keywordAndName(f.outline.Keyword, f.outline.Name) + text += s(f.commentPos-f.length(f.outline)+1) + f.line(f.outline.Location) + fmt.Fprintln(f.out, "\n"+text) + f.scenarioKeyword = true + } + if len(f.outlineSteps) == len(f.outline.Steps)+f.totalBgSteps { + // an outline example steps has went through + f.printOutlineExample(f.outline) + f.outlineNumExamples-- + } return } - scenarioHeaderLength, maxLength := f.scenarioLengths(result.owner.AstNodeIds[0]) - stepLength := f.lengthPickleStep(astStep.Keyword, astStep.Text) - - firstExecutedScenarioStep := len(f.lastFeature().lastPickleResult().stepResults) == backgroundSteps+1 - if !astBackgroundStep && firstExecutedScenarioStep { - f.printScenarioHeader(astScenario, maxLength-scenarioHeaderLength) + if !f.isBackgroundStep(res.step) || bgStep { + f.printStep(res.step, res.def, res.typ.clr()) } - - text := s(f.indent*2) + result.status.clr()(strings.TrimSpace(astStep.Keyword)) + " " + result.status.clr()(astStep.Text) - if result.def != nil { - text += s(maxLength - stepLength + 1) - text += blackb("# " + result.def.definitionID()) + if res.err != nil { + fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", res.err))) } - fmt.Fprintln(f.out, text) - - if table := result.step.Argument.GetDataTable(); table != nil { - f.printTable(table, cyan) - } - - if docString := astStep.GetDocString(); docString != nil { - f.printDocString(docString) - } - - if result.err != nil { - fmt.Fprintln(f.out, s(f.indent*2)+redb(fmt.Sprintf("%+v", result.err))) - } - - if result.status == pending { + if res.typ == pending { fmt.Fprintln(f.out, s(f.indent*3)+yellow("TODO: write pending definition")) } } -func (f *pretty) printDocString(docString *messages.GherkinDocument_Feature_Step_DocString) { - var ct string - - if len(docString.MediaType) > 0 { - ct = " " + cyan(docString.MediaType) +func (f *pretty) isBackgroundStep(step *gherkin.Step) bool { + if f.feature.Background == nil { + return false } - fmt.Fprintln(f.out, s(f.indent*3)+cyan(docString.Delimiter)+ct) - - for _, ln := range strings.Split(docString.Content, "\n") { - fmt.Fprintln(f.out, s(f.indent*3)+cyan(ln)) + for _, bstep := range f.feature.Background.Steps { + if bstep.Location.Line == step.Location.Line { + return true + } } - - fmt.Fprintln(f.out, s(f.indent*3)+cyan(docString.Delimiter)) + return false } // print table with aligned table cells -// @TODO: need to make example header cells bold -func (f *pretty) printTable(t *messages.PickleStepArgument_PickleTable, c colors.ColorFunc) { - maxColLengths := maxColLengths(t, c) +func (f *pretty) printTable(t *gherkin.DataTable, c colors.ColorFunc) { + var l = longest(t, c) var cols = make([]string, len(t.Rows[0].Cells)) - for _, row := range t.Rows { for i, cell := range row.Cells { val := c(cell.Value) - colLength := utf8.RuneCountInString(val) - cols[i] = val + s(maxColLengths[i]-colLength) + ln := utf8.RuneCountInString(val) + cols[i] = val + s(l[i]-ln) } - fmt.Fprintln(f.out, s(f.indent*3)+"| "+strings.Join(cols, " | ")+" |") } } +func (f *pretty) Passed(step *gherkin.Step, match *StepDef) { + f.basefmt.Passed(step, match) + f.printStepKind(f.passed[len(f.passed)-1]) +} + +func (f *pretty) Skipped(step *gherkin.Step, match *StepDef) { + f.basefmt.Skipped(step, match) + f.printStepKind(f.skipped[len(f.skipped)-1]) +} + +func (f *pretty) Undefined(step *gherkin.Step, match *StepDef) { + f.basefmt.Undefined(step, match) + f.printStepKind(f.undefined[len(f.undefined)-1]) +} + +func (f *pretty) Failed(step *gherkin.Step, match *StepDef, err error) { + f.basefmt.Failed(step, match, err) + f.printStepKind(f.failed[len(f.failed)-1]) +} + +func (f *pretty) Pending(step *gherkin.Step, match *StepDef) { + f.basefmt.Pending(step, match) + f.printStepKind(f.pending[len(f.pending)-1]) +} + // longest gives a list of longest columns of all rows in Table -func maxColLengths(t *messages.PickleStepArgument_PickleTable, clrs ...colors.ColorFunc) []int { - if t == nil { - return []int{} +func longest(tbl interface{}, clrs ...colors.ColorFunc) []int { + var rows []*gherkin.TableRow + switch t := tbl.(type) { + case *gherkin.Examples: + rows = append(rows, t.TableHeader) + rows = append(rows, t.TableBody...) + case *gherkin.DataTable: + rows = append(rows, t.Rows...) } - longest := make([]int, len(t.Rows[0].Cells)) - for _, row := range t.Rows { + longest := make([]int, len(rows[0].Cells)) + for _, row := range rows { for i, cell := range row.Cells { for _, c := range clrs { ln := utf8.RuneCountInString(c(cell.Value)) @@ -398,71 +458,35 @@ func maxColLengths(t *messages.PickleStepArgument_PickleTable, clrs ...colors.Co } } } - return longest } -func longestExampleRow(t *messages.GherkinDocument_Feature_Scenario_Examples, clrs ...colors.ColorFunc) []int { - if t == nil { - return []int{} - } - - longest := make([]int, len(t.TableHeader.Cells)) - for i, cell := range t.TableHeader.Cells { - for _, c := range clrs { - ln := utf8.RuneCountInString(c(cell.Value)) - if longest[i] < ln { - longest[i] = ln - } - } - - ln := utf8.RuneCountInString(cell.Value) - if longest[i] < ln { - longest[i] = ln - } - } - - for _, row := range t.TableBody { - for i, cell := range row.Cells { - for _, c := range clrs { - ln := utf8.RuneCountInString(c(cell.Value)) - if longest[i] < ln { - longest[i] = ln - } - } - - ln := utf8.RuneCountInString(cell.Value) - if longest[i] < ln { - longest[i] = ln - } - } - } - - return longest -} - -func (f *pretty) longestStep(steps []*messages.GherkinDocument_Feature_Step, pickleLength int) int { - max := pickleLength - +func (f *pretty) longestStep(steps []*gherkin.Step, base int) int { + ret := base for _, step := range steps { - length := f.lengthPickleStep(step.Keyword, step.Text) - if length > max { - max = length + length := f.length(step) + if length > ret { + ret = length } } - - return max + return ret } // a line number representation in feature file -func (f *pretty) line(loc *messages.Location) string { - return " " + blackb(fmt.Sprintf("# %s:%d", f.lastFeature().Path, loc.Line)) +func (f *pretty) line(loc *gherkin.Location) string { + return blackb(fmt.Sprintf("# %s:%d", f.features[len(f.features)-1].Path, loc.Line)) } -func (f *pretty) lengthPickleStep(keyword, text string) int { - return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(keyword)+" "+text) -} - -func (f *pretty) lengthPickle(keyword, name string) int { - return f.indent + utf8.RuneCountInString(strings.TrimSpace(keyword)+": "+name) +func (f *pretty) length(node interface{}) int { + switch t := node.(type) { + case *gherkin.Background: + return f.indent + utf8.RuneCountInString(strings.TrimSpace(t.Keyword)+": "+t.Name) + case *gherkin.Step: + return f.indent*2 + utf8.RuneCountInString(strings.TrimSpace(t.Keyword)+" "+t.Text) + case *gherkin.Scenario: + return f.indent + utf8.RuneCountInString(strings.TrimSpace(t.Keyword)+": "+t.Name) + case *gherkin.ScenarioOutline: + return f.indent + utf8.RuneCountInString(strings.TrimSpace(t.Keyword)+": "+t.Name) + } + panic(fmt.Sprintf("unexpected node %T to determine length", node)) } diff --git a/fmt_progress.go b/fmt_progress.go index 6d422b2..1fb19db 100644 --- a/fmt_progress.go +++ b/fmt_progress.go @@ -6,7 +6,7 @@ import ( "math" "strings" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) func init() { @@ -37,39 +37,21 @@ func (f *progress) Summary() { fmt.Fprintf(f.out, " %d\n", *f.steps) } } - - var failedStepsOutput []string - for _, sr := range f.findStepResults(failed) { - if sr.status == failed { - sc := f.findScenario(sr.owner.AstNodeIds[0]) - scenarioDesc := fmt.Sprintf("%s: %s", sc.Keyword, sr.owner.Name) - scenarioLine := fmt.Sprintf("%s:%d", sr.owner.Uri, sc.Location.Line) - - step := f.findStep(sr.step.AstNodeIds[0]) - stepDesc := strings.TrimSpace(step.Keyword) + " " + sr.step.Text - stepLine := fmt.Sprintf("%s:%d", sr.owner.Uri, step.Location.Line) - - failedStepsOutput = append( - failedStepsOutput, - s(2)+red(scenarioDesc)+blackb(" # "+scenarioLine), - s(4)+red(stepDesc)+blackb(" # "+stepLine), - s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", sr.err)), - "", - ) - } - } - - if len(failedStepsOutput) > 0 { - fmt.Fprintln(f.out, "\n\n--- "+red("Failed steps:")+"\n") - fmt.Fprint(f.out, strings.Join(failedStepsOutput, "\n")) - } fmt.Fprintln(f.out, "") + if len(f.failed) > 0 { + fmt.Fprintln(f.out, "\n--- "+red("Failed steps:")+"\n") + for _, fail := range f.failed { + fmt.Fprintln(f.out, s(2)+red(fail.scenarioDesc())+blackb(" # "+fail.scenarioLine())) + fmt.Fprintln(f.out, s(4)+red(strings.TrimSpace(fail.step.Keyword)+" "+fail.step.Text)+blackb(" # "+fail.line())) + fmt.Fprintln(f.out, s(6)+red("Error: ")+redb(fmt.Sprintf("%+v", fail.err))+"\n") + } + } f.basefmt.Summary() } func (f *progress) step(res *stepResult) { - switch res.status { + switch res.typ { case passed: fmt.Fprint(f.out, green(".")) case skipped: @@ -89,49 +71,44 @@ func (f *progress) step(res *stepResult) { } } -func (f *progress) Passed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Passed(pickle, step, match) +func (f *progress) Passed(step *gherkin.Step, match *StepDef) { + f.basefmt.Passed(step, match) f.lock.Lock() defer f.lock.Unlock() - - f.step(f.lastStepResult()) + f.step(f.passed[len(f.passed)-1]) } -func (f *progress) Skipped(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Skipped(pickle, step, match) +func (f *progress) Skipped(step *gherkin.Step, match *StepDef) { + f.basefmt.Skipped(step, match) f.lock.Lock() defer f.lock.Unlock() - - f.step(f.lastStepResult()) + f.step(f.skipped[len(f.skipped)-1]) } -func (f *progress) Undefined(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Undefined(pickle, step, match) +func (f *progress) Undefined(step *gherkin.Step, match *StepDef) { + f.basefmt.Undefined(step, match) f.lock.Lock() defer f.lock.Unlock() - - f.step(f.lastStepResult()) + f.step(f.undefined[len(f.undefined)-1]) } -func (f *progress) Failed(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition, err error) { - f.basefmt.Failed(pickle, step, match, err) +func (f *progress) Failed(step *gherkin.Step, match *StepDef, err error) { + f.basefmt.Failed(step, match, err) f.lock.Lock() defer f.lock.Unlock() - - f.step(f.lastStepResult()) + f.step(f.failed[len(f.failed)-1]) } -func (f *progress) Pending(pickle *messages.Pickle, step *messages.Pickle_PickleStep, match *StepDefinition) { - f.basefmt.Pending(pickle, step, match) +func (f *progress) Pending(step *gherkin.Step, match *StepDef) { + f.basefmt.Pending(step, match) f.lock.Lock() defer f.lock.Unlock() - - f.step(f.lastStepResult()) + f.step(f.pending[len(f.pending)-1]) } func (f *progress) Sync(cf ConcurrentFormatter) { diff --git a/fmt_progress_test.go b/fmt_progress_test.go index de29d56..9a04c65 100644 --- a/fmt_progress_test.go +++ b/fmt_progress_test.go @@ -3,42 +3,28 @@ package godog import ( "bytes" "fmt" + "io/ioutil" "strings" "testing" - "github.com/cucumber/gherkin-go/v9" - "github.com/cucumber/messages-go/v9" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/gherkin" ) -var basicGherkinFeature = ` -Feature: basic - - Scenario: passing scenario - When one - Then two -` - func TestProgressFormatterOutput(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(sampleGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(sampleGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{ - GherkinDocument: gd, - pickles: pickles, - Path: path, - Content: []byte(sampleGherkinFeature), + features: []*feature{&feature{ + Path: "any.feature", + Feature: feat, + Content: []byte(sampleGherkinFeature), }}, initializer: func(s *Suite) { s.Step(`^passing$`, func() error { return nil }) @@ -81,52 +67,61 @@ func FeatureContext(s *godog.Suite) { s.Step(` + "`^next undefined$`" + `, nextUndefined) }` - require.True(t, r.run()) - expected = trimAllLines(expected) + + r.run() + actual := trimAllLines(buf.String()) - assert.Equal(t, expected, actual) + shouldMatchOutput(expected, actual, t) } +var basicGherkinFeature = ` +Feature: basic + + Scenario: passing scenario + When one + Then two +` + func TestProgressFormatterWhenStepPanics(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { panic("omg") }) }, } - require.True(t, r.run()) + if !r.run() { + t.Fatal("the suite should have failed") + } - actual := buf.String() - assert.Contains(t, actual, "godog/fmt_progress_test.go:107") + out := buf.String() + if idx := strings.Index(out, "godog/fmt_progress_test.go:100"); idx == -1 { + t.Fatalf("expected to find panic stacktrace, actual:\n%s", out) + } } func TestProgressFormatterWithPassingMultisteps(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return nil }) @@ -136,22 +131,22 @@ func TestProgressFormatterWithPassingMultisteps(t *testing.T) { }, } - assert.False(t, r.run()) + if r.run() { + t.Fatal("the suite should have passed") + } } func TestProgressFormatterWithFailingMultisteps(t *testing.T) { - const path = "some.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles, Path: path}}, + features: []*feature{&feature{Feature: feat, Path: "some.feature"}}, initializer: func(s *Suite) { s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return fmt.Errorf("errored") }) @@ -161,7 +156,9 @@ func TestProgressFormatterWithFailingMultisteps(t *testing.T) { }, } - require.True(t, r.run()) + if !r.run() { + t.Fatal("the suite should have failed") + } expected := ` .F 2 @@ -181,21 +178,48 @@ Error: sub2: sub-sub: errored expected = trimAllLines(expected) actual := trimAllLines(buf.String()) - assert.Equal(t, expected, actual) + + shouldMatchOutput(expected, actual, t) +} + +func shouldMatchOutput(expected, actual string, t *testing.T) { + act := []byte(actual) + exp := []byte(expected) + + if len(act) != len(exp) { + t.Fatalf("content lengths do not match, expected: %d, actual %d, actual output:\n%s", len(exp), len(act), actual) + } + + for i := 0; i < len(exp); i++ { + if act[i] == exp[i] { + continue + } + + cpe := make([]byte, len(exp)) + copy(cpe, exp) + e := append(exp[:i], '^') + e = append(e, cpe[i:]...) + + cpa := make([]byte, len(act)) + copy(cpa, act) + a := append(act[:i], '^') + a = append(a, cpa[i:]...) + + t.Fatalf("expected output does not match:\n%s\n\n%s", string(a), string(e)) + } } func TestProgressFormatterWithPanicInMultistep(t *testing.T) { - const path = "any.feature" + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^sub1$`, func() error { return nil }) s.Step(`^sub-sub$`, func() error { return nil }) @@ -205,22 +229,22 @@ func TestProgressFormatterWithPanicInMultistep(t *testing.T) { }, } - assert.True(t, r.run()) + if !r.run() { + t.Fatal("the suite should have failed") + } } func TestProgressFormatterMultistepTemplates(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var buf bytes.Buffer w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^sub-sub$`, func() error { return nil }) s.Step(`^substep$`, func() Steps { return Steps{"sub-sub", `unavailable "John" cost 5`, "one", "three"} }) @@ -229,7 +253,9 @@ func TestProgressFormatterMultistepTemplates(t *testing.T) { }, } - require.False(t, r.run()) + if r.run() { + t.Fatal("the suite should have passed") + } expected := ` .U 2 @@ -261,13 +287,14 @@ func FeatureContext(s *godog.Suite) { ` expected = trimAllLines(expected) - actual := trimAllLines(buf.String()) - assert.Equal(t, expected, actual) + actual := trimAllLines(buf.String()) + if actual != expected { + t.Fatalf("expected output does not match: %s", actual) + } } func TestProgressFormatterWhenMultiStepHasArgument(t *testing.T) { - const path = "any.feature" var featureSource = ` Feature: basic @@ -279,28 +306,26 @@ Feature: basic text """ ` + feat, err := gherkin.ParseFeature(strings.NewReader(featureSource)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(featureSource), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) - - var buf bytes.Buffer - w := colors.Uncolored(&buf) r := runner{ - fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + fmt: progressFunc("progress", ioutil.Discard), + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) - s.Step(`^two:$`, func(doc *messages.PickleStepArgument_PickleDocString) Steps { return Steps{"one"} }) + s.Step(`^two:$`, func(doc *gherkin.DocString) Steps { return Steps{"one"} }) }, } - assert.False(t, r.run()) + if r.run() { + t.Fatal("the suite should have passed") + } } func TestProgressFormatterWhenMultiStepHasStepWithArgument(t *testing.T) { - const path = "any.feature" var featureSource = ` Feature: basic @@ -309,10 +334,10 @@ Feature: basic When one Then two` - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(featureSource), (&messages.Incrementing{}).NewId) - require.NoError(t, err) - - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(featureSource)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } var subStep = `three: """ @@ -323,15 +348,17 @@ Feature: basic w := colors.Uncolored(&buf) r := runner{ fmt: progressFunc("progress", w), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() Steps { return Steps{subStep} }) - s.Step(`^three:$`, func(doc *messages.PickleStepArgument_PickleDocString) error { return nil }) + s.Step(`^three:$`, func(doc *gherkin.DocString) error { return nil }) }, } - require.True(t, r.run()) + if !r.run() { + t.Fatal("the suite should have failed") + } expected := ` .F 2 @@ -339,8 +366,8 @@ Feature: basic --- Failed steps: - Scenario: passing scenario # any.feature:4 - Then two # any.feature:6 + Scenario: passing scenario # :4 + Then two # :6 Error: nested steps cannot be multiline and have table or content body argument @@ -351,6 +378,7 @@ Feature: basic expected = trimAllLines(expected) actual := trimAllLines(buf.String()) - - assert.Equal(t, expected, actual) + if actual != expected { + t.Fatalf("expected output does not match: %s", actual) + } } diff --git a/formatter-tests/events/scenario_outline b/formatter-tests/events/scenario_outline index adab074..3af6cad 100644 --- a/formatter-tests/events/scenario_outline +++ b/formatter-tests/events/scenario_outline @@ -1,5 +1,5 @@ {"event":"TestRunStarted","version":"0.1.0","timestamp":-6795364578871,"suite":"events"} -{"event":"TestSource","location":"formatter-tests/features/scenario_outline.feature:2","source":"@outline @tag\nFeature: outline\n\n @scenario\n Scenario Outline: outline\n Given passing step\n When passing step\n Then odd \u003codd\u003e and even \u003ceven\u003e number\n\n @tagged\n Examples: tagged\n | odd | even |\n | 1 | 2 |\n | 2 | 0 |\n | 3 | 11 |\n\n @tag2\n Examples:\n | odd | even |\n | 1 | 14 |\n | 3 | 9 |\n"} +{"event":"TestSource","location":"formatter-tests/features/scenario_outline.feature:2","source":"@outline @tag\nFeature: outline\n\n @scenario\n Scenario Outline: outline\n Given passing step\n When passing step\n Then odd \u003codd\u003e and even \u003ceven\u003e number\n\n @tagged\n Examples: tagged\n | odd | even |\n | 1 | 2 |\n | 2 | 0 |\n | 3 | 11 |\n\n @tag2\n Examples:\n | odd | even |\n | 1 | 14 |\n | 3 | 9 |\n\n"} {"event":"TestCaseStarted","location":"formatter-tests/features/scenario_outline.feature:13","timestamp":-6795364578871} {"event":"StepDefinitionFound","location":"formatter-tests/features/scenario_outline.feature:6","definition_id":"formatters_print_test.go:63 -\u003e passingStepDef","arguments":[]} {"event":"TestStepStarted","location":"formatter-tests/features/scenario_outline.feature:6","timestamp":-6795364578871} diff --git a/formatter-tests/features/scenario_outline.feature b/formatter-tests/features/scenario_outline.feature index 1178642..3691ce3 100644 --- a/formatter-tests/features/scenario_outline.feature +++ b/formatter-tests/features/scenario_outline.feature @@ -19,3 +19,4 @@ Feature: outline | odd | even | | 1 | 14 | | 3 | 9 | + diff --git a/formatter-tests/pretty/scenario_outline b/formatter-tests/pretty/scenario_outline index 60d4e97..e8d8a61 100644 --- a/formatter-tests/pretty/scenario_outline +++ b/formatter-tests/pretty/scenario_outline @@ -21,16 +21,16 @@ --- Failed steps: - Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 - Then odd 2 and even 0 number # formatter-tests/features/scenario_outline.feature:8 + Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 + Then odd 2 and even 0 number # formatter-tests/features/scenario_outline.feature:8 Error: 2 is not odd - Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 - Then odd 3 and even 11 number # formatter-tests/features/scenario_outline.feature:8 + Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 + Then odd 3 and even 11 number # formatter-tests/features/scenario_outline.feature:8 Error: 11 is not even - Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 - Then odd 3 and even 9 number # formatter-tests/features/scenario_outline.feature:8 + Scenario Outline: outline # formatter-tests/features/scenario_outline.feature:5 + Then odd 3 and even 9 number # formatter-tests/features/scenario_outline.feature:8 Error: 9 is not even diff --git a/formatter-tests/pretty/some_scenarions_including_failing b/formatter-tests/pretty/some_scenarions_including_failing index 4693204..dbab7e1 100644 --- a/formatter-tests/pretty/some_scenarions_including_failing +++ b/formatter-tests/pretty/some_scenarions_including_failing @@ -17,8 +17,8 @@ --- Failed steps: - Scenario: failing # formatter-tests/features/some_scenarions_including_failing.feature:3 - When failing step # formatter-tests/features/some_scenarions_including_failing.feature:5 + Scenario: failing # formatter-tests/features/some_scenarions_including_failing.feature:3 + When failing step # formatter-tests/features/some_scenarions_including_failing.feature:5 Error: step failed diff --git a/formatter-tests/pretty/two_scenarios_with_background_fail b/formatter-tests/pretty/two_scenarios_with_background_fail index c5b0969..bee7005 100644 --- a/formatter-tests/pretty/two_scenarios_with_background_fail +++ b/formatter-tests/pretty/two_scenarios_with_background_fail @@ -10,16 +10,17 @@ Then passing step # formatters_print_test.go:63 -> passingStepDef Scenario: two # formatter-tests/features/two_scenarios_with_background_fail.feature:11 + step failed Then passing step # formatters_print_test.go:63 -> passingStepDef --- Failed steps: - Scenario: one # formatter-tests/features/two_scenarios_with_background_fail.feature:7 - And failing step # formatter-tests/features/two_scenarios_with_background_fail.feature:5 + Scenario: one # formatter-tests/features/two_scenarios_with_background_fail.feature:7 + And failing step # formatter-tests/features/two_scenarios_with_background_fail.feature:5 Error: step failed - Scenario: two # formatter-tests/features/two_scenarios_with_background_fail.feature:11 - And failing step # formatter-tests/features/two_scenarios_with_background_fail.feature:5 + Scenario: two # formatter-tests/features/two_scenarios_with_background_fail.feature:11 + And failing step # formatter-tests/features/two_scenarios_with_background_fail.feature:5 Error: step failed diff --git a/gherkin.go b/gherkin.go new file mode 100644 index 0000000..c2a8185 --- /dev/null +++ b/gherkin.go @@ -0,0 +1,36 @@ +package godog + +import "github.com/cucumber/godog/gherkin" + +// examples is a helper func to cast gherkin.Examples +// or gherkin.BaseExamples if its empty +// @TODO: this should go away with gherkin update +func examples(ex interface{}) (*gherkin.Examples, bool) { + t, ok := ex.(*gherkin.Examples) + return t, ok +} + +// means there are no scenarios or they do not have steps +func isEmptyFeature(ft *gherkin.Feature) bool { + for _, def := range ft.ScenarioDefinitions { + if !isEmptyScenario(def) { + return false + } + } + return true +} + +// means scenario dooes not have steps +func isEmptyScenario(def interface{}) bool { + switch t := def.(type) { + case *gherkin.Scenario: + if len(t.Steps) > 0 { + return false + } + case *gherkin.ScenarioOutline: + if len(t.Steps) > 0 { + return false + } + } + return true +} diff --git a/gherkin/LICENSE b/gherkin/LICENSE new file mode 100644 index 0000000..ea68549 --- /dev/null +++ b/gherkin/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2016 Cucumber Ltd, Gaspar Nagy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/gherkin/README.md b/gherkin/README.md new file mode 100644 index 0000000..6b90df0 --- /dev/null +++ b/gherkin/README.md @@ -0,0 +1,3 @@ +[![Build Status](https://secure.travis-ci.org/cucumber/gherkin-go.svg)](http://travis-ci.org/cucumber/gherkin-go) + +Gherkin parser/compiler for Go. Please see [Gherkin](https://github.com/cucumber/gherkin) for details. diff --git a/gherkin/ast.go b/gherkin/ast.go new file mode 100644 index 0000000..519e0b3 --- /dev/null +++ b/gherkin/ast.go @@ -0,0 +1,95 @@ +package gherkin + +type Location struct { + Line int `json:"line"` + Column int `json:"column"` +} + +type Node struct { + Location *Location `json:"location,omitempty"` + Type string `json:"type"` +} + +type Feature struct { + Node + Tags []*Tag `json:"tags"` + Language string `json:"language,omitempty"` + Keyword string `json:"keyword"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Background *Background `json:"background,omitempty"` + ScenarioDefinitions []interface{} `json:"scenarioDefinitions"` + Comments []*Comment `json:"comments"` +} + +type Comment struct { + Node + Text string `json:"text"` +} + +type Tag struct { + Node + Name string `json:"name"` +} + +type Background struct { + ScenarioDefinition +} + +type Scenario struct { + ScenarioDefinition + Tags []*Tag `json:"tags"` +} + +type ScenarioOutline struct { + ScenarioDefinition + Tags []*Tag `json:"tags"` + Examples []*Examples `json:"examples,omitempty"` +} + +type Examples struct { + Node + Tags []*Tag `json:"tags"` + Keyword string `json:"keyword"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + TableHeader *TableRow `json:"tableHeader"` + TableBody []*TableRow `json:"tableBody"` +} + +type TableRow struct { + Node + Cells []*TableCell `json:"cells"` +} + +type TableCell struct { + Node + Value string `json:"value"` +} + +type ScenarioDefinition struct { + Node + Keyword string `json:"keyword"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Steps []*Step `json:"steps"` +} + +type Step struct { + Node + Keyword string `json:"keyword"` + Text string `json:"text"` + Argument interface{} `json:"argument,omitempty"` +} + +type DocString struct { + Node + ContentType string `json:"contentType,omitempty"` + Content string `json:"content"` + Delimitter string `json:"-"` +} + +type DataTable struct { + Node + Rows []*TableRow `json:"rows"` +} diff --git a/gherkin/astbuilder.go b/gherkin/astbuilder.go new file mode 100644 index 0000000..e92ea36 --- /dev/null +++ b/gherkin/astbuilder.go @@ -0,0 +1,378 @@ +package gherkin + +import ( + "strings" +) + +type AstBuilder interface { + Builder + GetFeature() *Feature +} + +type astBuilder struct { + stack []*astNode + comments []*Comment +} + +func (t *astBuilder) Reset() { + t.comments = []*Comment{} + t.stack = []*astNode{} + t.push(newAstNode(RuleType_None)) +} + +func (t *astBuilder) GetFeature() *Feature { + res := t.currentNode().getSingle(RuleType_Feature) + if val, ok := res.(*Feature); ok { + return val + } + return nil +} + +type astNode struct { + ruleType RuleType + subNodes map[RuleType][]interface{} +} + +func (a *astNode) add(rt RuleType, obj interface{}) { + a.subNodes[rt] = append(a.subNodes[rt], obj) +} + +func (a *astNode) getSingle(rt RuleType) interface{} { + if val, ok := a.subNodes[rt]; ok { + for i := range val { + return val[i] + } + } + return nil +} + +func (a *astNode) getItems(rt RuleType) []interface{} { + var res []interface{} + if val, ok := a.subNodes[rt]; ok { + for i := range val { + res = append(res, val[i]) + } + } + return res +} + +func (a *astNode) getToken(tt TokenType) *Token { + if val, ok := a.getSingle(tt.RuleType()).(*Token); ok { + return val + } + return nil +} + +func (a *astNode) getTokens(tt TokenType) []*Token { + var items = a.getItems(tt.RuleType()) + var tokens []*Token + for i := range items { + if val, ok := items[i].(*Token); ok { + tokens = append(tokens, val) + } + } + return tokens +} + +func (t *astBuilder) currentNode() *astNode { + if len(t.stack) > 0 { + return t.stack[len(t.stack)-1] + } + return nil +} + +func newAstNode(rt RuleType) *astNode { + return &astNode{ + ruleType: rt, + subNodes: make(map[RuleType][]interface{}), + } +} + +func NewAstBuilder() AstBuilder { + builder := new(astBuilder) + builder.comments = []*Comment{} + builder.push(newAstNode(RuleType_None)) + return builder +} + +func (t *astBuilder) push(n *astNode) { + t.stack = append(t.stack, n) +} + +func (t *astBuilder) pop() *astNode { + x := t.stack[len(t.stack)-1] + t.stack = t.stack[:len(t.stack)-1] + return x +} + +func (t *astBuilder) Build(tok *Token) (bool, error) { + if tok.Type == TokenType_Comment { + comment := new(Comment) + comment.Type = "Comment" + comment.Location = astLocation(tok) + comment.Text = tok.Text + t.comments = append(t.comments, comment) + } else { + t.currentNode().add(tok.Type.RuleType(), tok) + } + return true, nil +} +func (t *astBuilder) StartRule(r RuleType) (bool, error) { + t.push(newAstNode(r)) + return true, nil +} +func (t *astBuilder) EndRule(r RuleType) (bool, error) { + node := t.pop() + transformedNode, err := t.transformNode(node) + t.currentNode().add(node.ruleType, transformedNode) + return true, err +} + +func (t *astBuilder) transformNode(node *astNode) (interface{}, error) { + switch node.ruleType { + + case RuleType_Step: + stepLine := node.getToken(TokenType_StepLine) + step := new(Step) + step.Type = "Step" + step.Location = astLocation(stepLine) + step.Keyword = stepLine.Keyword + step.Text = stepLine.Text + step.Argument = node.getSingle(RuleType_DataTable) + if step.Argument == nil { + step.Argument = node.getSingle(RuleType_DocString) + } + return step, nil + + case RuleType_DocString: + separatorToken := node.getToken(TokenType_DocStringSeparator) + contentType := separatorToken.Text + lineTokens := node.getTokens(TokenType_Other) + var text string + for i := range lineTokens { + if i > 0 { + text += "\n" + } + text += lineTokens[i].Text + } + ds := new(DocString) + ds.Type = "DocString" + ds.Location = astLocation(separatorToken) + ds.ContentType = contentType + ds.Content = text + ds.Delimitter = DOCSTRING_SEPARATOR // TODO: remember separator + return ds, nil + + case RuleType_DataTable: + rows, err := astTableRows(node) + dt := new(DataTable) + dt.Type = "DataTable" + dt.Location = rows[0].Location + dt.Rows = rows + return dt, err + + case RuleType_Background: + backgroundLine := node.getToken(TokenType_BackgroundLine) + description, _ := node.getSingle(RuleType_Description).(string) + bg := new(Background) + bg.Type = "Background" + bg.Location = astLocation(backgroundLine) + bg.Keyword = backgroundLine.Keyword + bg.Name = backgroundLine.Text + bg.Description = description + bg.Steps = astSteps(node) + return bg, nil + + case RuleType_Scenario_Definition: + tags := astTags(node) + scenarioNode, _ := node.getSingle(RuleType_Scenario).(*astNode) + if scenarioNode != nil { + scenarioLine := scenarioNode.getToken(TokenType_ScenarioLine) + description, _ := scenarioNode.getSingle(RuleType_Description).(string) + sc := new(Scenario) + sc.Type = "Scenario" + sc.Tags = tags + sc.Location = astLocation(scenarioLine) + sc.Keyword = scenarioLine.Keyword + sc.Name = scenarioLine.Text + sc.Description = description + sc.Steps = astSteps(scenarioNode) + return sc, nil + } else { + scenarioOutlineNode, ok := node.getSingle(RuleType_ScenarioOutline).(*astNode) + if !ok { + panic("Internal grammar error") + } + scenarioOutlineLine := scenarioOutlineNode.getToken(TokenType_ScenarioOutlineLine) + description, _ := scenarioOutlineNode.getSingle(RuleType_Description).(string) + sc := new(ScenarioOutline) + sc.Type = "ScenarioOutline" + sc.Tags = tags + sc.Location = astLocation(scenarioOutlineLine) + sc.Keyword = scenarioOutlineLine.Keyword + sc.Name = scenarioOutlineLine.Text + sc.Description = description + sc.Steps = astSteps(scenarioOutlineNode) + sc.Examples = astExamples(scenarioOutlineNode) + return sc, nil + } + + case RuleType_Examples_Definition: + tags := astTags(node) + examplesNode, _ := node.getSingle(RuleType_Examples).(*astNode) + examplesLine := examplesNode.getToken(TokenType_ExamplesLine) + description, _ := examplesNode.getSingle(RuleType_Description).(string) + allRows, err := astTableRows(examplesNode) + ex := new(Examples) + ex.Type = "Examples" + ex.Tags = tags + ex.Location = astLocation(examplesLine) + ex.Keyword = examplesLine.Keyword + ex.Name = examplesLine.Text + ex.Description = description + ex.TableHeader = allRows[0] + ex.TableBody = allRows[1:] + return ex, err + + case RuleType_Description: + lineTokens := node.getTokens(TokenType_Other) + // Trim trailing empty lines + end := len(lineTokens) + for end > 0 && strings.TrimSpace(lineTokens[end-1].Text) == "" { + end-- + } + var desc []string + for i := range lineTokens[0:end] { + desc = append(desc, lineTokens[i].Text) + } + return strings.Join(desc, "\n"), nil + + case RuleType_Feature: + header, ok := node.getSingle(RuleType_Feature_Header).(*astNode) + if !ok { + return nil, nil + } + tags := astTags(header) + featureLine := header.getToken(TokenType_FeatureLine) + if featureLine == nil { + return nil, nil + } + background, _ := node.getSingle(RuleType_Background).(*Background) + scenarioDefinitions := node.getItems(RuleType_Scenario_Definition) + if scenarioDefinitions == nil { + scenarioDefinitions = []interface{}{} + } + description, _ := header.getSingle(RuleType_Description).(string) + + feat := new(Feature) + feat.Type = "Feature" + feat.Tags = tags + feat.Location = astLocation(featureLine) + feat.Language = featureLine.GherkinDialect + feat.Keyword = featureLine.Keyword + feat.Name = featureLine.Text + feat.Description = description + feat.Background = background + feat.ScenarioDefinitions = scenarioDefinitions + feat.Comments = t.comments + return feat, nil + } + return node, nil +} + +func astLocation(t *Token) *Location { + return &Location{ + Line: t.Location.Line, + Column: t.Location.Column, + } +} + +func astTableRows(t *astNode) (rows []*TableRow, err error) { + rows = []*TableRow{} + tokens := t.getTokens(TokenType_TableRow) + for i := range tokens { + row := new(TableRow) + row.Type = "TableRow" + row.Location = astLocation(tokens[i]) + row.Cells = astTableCells(tokens[i]) + rows = append(rows, row) + } + err = ensureCellCount(rows) + return +} + +func ensureCellCount(rows []*TableRow) error { + if len(rows) <= 1 { + return nil + } + cellCount := len(rows[0].Cells) + for i := range rows { + if cellCount != len(rows[i].Cells) { + return &parseError{"inconsistent cell count within the table", &Location{ + Line: rows[i].Location.Line, + Column: rows[i].Location.Column, + }} + } + } + return nil +} + +func astTableCells(t *Token) (cells []*TableCell) { + cells = []*TableCell{} + for i := range t.Items { + item := t.Items[i] + cell := new(TableCell) + cell.Type = "TableCell" + cell.Location = &Location{ + Line: t.Location.Line, + Column: item.Column, + } + cell.Value = item.Text + cells = append(cells, cell) + } + return +} + +func astSteps(t *astNode) (steps []*Step) { + steps = []*Step{} + tokens := t.getItems(RuleType_Step) + for i := range tokens { + step, _ := tokens[i].(*Step) + steps = append(steps, step) + } + return +} + +func astExamples(t *astNode) (examples []*Examples) { + examples = []*Examples{} + tokens := t.getItems(RuleType_Examples_Definition) + for i := range tokens { + example, _ := tokens[i].(*Examples) + examples = append(examples, example) + } + return +} + +func astTags(node *astNode) (tags []*Tag) { + tags = []*Tag{} + tagsNode, ok := node.getSingle(RuleType_Tags).(*astNode) + if !ok { + return + } + tokens := tagsNode.getTokens(TokenType_TagLine) + for i := range tokens { + token := tokens[i] + for k := range token.Items { + item := token.Items[k] + tag := new(Tag) + tag.Type = "Tag" + tag.Location = &Location{ + Line: token.Location.Line, + Column: item.Column, + } + tag.Name = item.Text + tags = append(tags, tag) + } + } + return +} diff --git a/gherkin/dialect.go b/gherkin/dialect.go new file mode 100644 index 0000000..e8af1e4 --- /dev/null +++ b/gherkin/dialect.go @@ -0,0 +1,47 @@ +package gherkin + +type GherkinDialect struct { + Language string + Name string + Native string + Keywords map[string][]string +} + +func (g *GherkinDialect) FeatureKeywords() []string { + return g.Keywords["feature"] +} + +func (g *GherkinDialect) ScenarioKeywords() []string { + return g.Keywords["scenario"] +} + +func (g *GherkinDialect) StepKeywords() []string { + result := g.Keywords["given"] + result = append(result, g.Keywords["when"]...) + result = append(result, g.Keywords["then"]...) + result = append(result, g.Keywords["and"]...) + result = append(result, g.Keywords["but"]...) + return result +} + +func (g *GherkinDialect) BackgroundKeywords() []string { + return g.Keywords["background"] +} + +func (g *GherkinDialect) ScenarioOutlineKeywords() []string { + return g.Keywords["scenarioOutline"] +} + +func (g *GherkinDialect) ExamplesKeywords() []string { + return g.Keywords["examples"] +} + +type GherkinDialectProvider interface { + GetDialect(language string) *GherkinDialect +} + +type gherkinDialectMap map[string]*GherkinDialect + +func (g gherkinDialectMap) GetDialect(language string) *GherkinDialect { + return g[language] +} diff --git a/gherkin/dialects_builtin.go b/gherkin/dialects_builtin.go new file mode 100644 index 0000000..1d74625 --- /dev/null +++ b/gherkin/dialects_builtin.go @@ -0,0 +1,2988 @@ +package gherkin + +// Builtin dialects for af (Afrikaans), am (Armenian), ar (Arabic), bg (Bulgarian), bm (Malay), bs (Bosnian), ca (Catalan), cs (Czech), cy-GB (Welsh), da (Danish), de (German), el (Greek), em (Emoji), en (English), en-Scouse (Scouse), en-au (Australian), en-lol (LOLCAT), en-old (Old English), en-pirate (Pirate), eo (Esperanto), es (Spanish), et (Estonian), fa (Persian), fi (Finnish), fr (French), ga (Irish), gj (Gujarati), gl (Galician), he (Hebrew), hi (Hindi), hr (Croatian), ht (Creole), hu (Hungarian), id (Indonesian), is (Icelandic), it (Italian), ja (Japanese), jv (Javanese), kn (Kannada), ko (Korean), lt (Lithuanian), lu (Luxemburgish), lv (Latvian), mn (Mongolian), nl (Dutch), no (Norwegian), pa (Panjabi), pl (Polish), pt (Portuguese), ro (Romanian), ru (Russian), sk (Slovak), sl (Slovenian), sr-Cyrl (Serbian), sr-Latn (Serbian (Latin)), sv (Swedish), ta (Tamil), th (Thai), tl (Telugu), tlh (Klingon), tr (Turkish), tt (Tatar), uk (Ukrainian), ur (Urdu), uz (Uzbek), vi (Vietnamese), zh-CN (Chinese simplified), zh-TW (Chinese traditional) +func GherkinDialectsBuildin() GherkinDialectProvider { + return buildinDialects +} + +const ( + feature = "feature" + background = "background" + scenario = "scenario" + scenarioOutline = "scenarioOutline" + examples = "examples" + given = "given" + when = "when" + then = "then" + and = "and" + but = "but" +) + +var buildinDialects = gherkinDialectMap{ + "af": &GherkinDialect{ + "af", "Afrikaans", "Afrikaans", map[string][]string{ + and: []string{ + "* ", + "En ", + }, + background: []string{ + "Agtergrond", + }, + but: []string{ + "* ", + "Maar ", + }, + examples: []string{ + "Voorbeelde", + }, + feature: []string{ + "Funksie", + "Besigheid Behoefte", + "Vermoë", + }, + given: []string{ + "* ", + "Gegewe ", + }, + scenario: []string{ + "Situasie", + }, + scenarioOutline: []string{ + "Situasie Uiteensetting", + }, + then: []string{ + "* ", + "Dan ", + }, + when: []string{ + "* ", + "Wanneer ", + }, + }, + }, + "am": &GherkinDialect{ + "am", "Armenian", "հայերեն", map[string][]string{ + and: []string{ + "* ", + "Եվ ", + }, + background: []string{ + "Կոնտեքստ", + }, + but: []string{ + "* ", + "Բայց ", + }, + examples: []string{ + "Օրինակներ", + }, + feature: []string{ + "Ֆունկցիոնալություն", + "Հատկություն", + }, + given: []string{ + "* ", + "Դիցուք ", + }, + scenario: []string{ + "Սցենար", + }, + scenarioOutline: []string{ + "Սցենարի կառուցվացքը", + }, + then: []string{ + "* ", + "Ապա ", + }, + when: []string{ + "* ", + "Եթե ", + "Երբ ", + }, + }, + }, + "ar": &GherkinDialect{ + "ar", "Arabic", "العربية", map[string][]string{ + and: []string{ + "* ", + "و ", + }, + background: []string{ + "الخلفية", + }, + but: []string{ + "* ", + "لكن ", + }, + examples: []string{ + "امثلة", + }, + feature: []string{ + "خاصية", + }, + given: []string{ + "* ", + "بفرض ", + }, + scenario: []string{ + "سيناريو", + }, + scenarioOutline: []string{ + "سيناريو مخطط", + }, + then: []string{ + "* ", + "اذاً ", + "ثم ", + }, + when: []string{ + "* ", + "متى ", + "عندما ", + }, + }, + }, + "bg": &GherkinDialect{ + "bg", "Bulgarian", "български", map[string][]string{ + and: []string{ + "* ", + "И ", + }, + background: []string{ + "Предистория", + }, + but: []string{ + "* ", + "Но ", + }, + examples: []string{ + "Примери", + }, + feature: []string{ + "Функционалност", + }, + given: []string{ + "* ", + "Дадено ", + }, + scenario: []string{ + "Сценарий", + }, + scenarioOutline: []string{ + "Рамка на сценарий", + }, + then: []string{ + "* ", + "То ", + }, + when: []string{ + "* ", + "Когато ", + }, + }, + }, + "bm": &GherkinDialect{ + "bm", "Malay", "Bahasa Melayu", map[string][]string{ + and: []string{ + "* ", + "Dan ", + }, + background: []string{ + "Latar Belakang", + }, + but: []string{ + "* ", + "Tetapi ", + "Tapi ", + }, + examples: []string{ + "Contoh", + }, + feature: []string{ + "Fungsi", + }, + given: []string{ + "* ", + "Diberi ", + "Bagi ", + }, + scenario: []string{ + "Senario", + "Situasi", + "Keadaan", + }, + scenarioOutline: []string{ + "Kerangka Senario", + "Kerangka Situasi", + "Kerangka Keadaan", + "Garis Panduan Senario", + }, + then: []string{ + "* ", + "Maka ", + "Kemudian ", + }, + when: []string{ + "* ", + "Apabila ", + }, + }, + }, + "bs": &GherkinDialect{ + "bs", "Bosnian", "Bosanski", map[string][]string{ + and: []string{ + "* ", + "I ", + "A ", + }, + background: []string{ + "Pozadina", + }, + but: []string{ + "* ", + "Ali ", + }, + examples: []string{ + "Primjeri", + }, + feature: []string{ + "Karakteristika", + }, + given: []string{ + "* ", + "Dato ", + }, + scenario: []string{ + "Scenariju", + "Scenario", + }, + scenarioOutline: []string{ + "Scenariju-obris", + "Scenario-outline", + }, + then: []string{ + "* ", + "Zatim ", + }, + when: []string{ + "* ", + "Kada ", + }, + }, + }, + "ca": &GherkinDialect{ + "ca", "Catalan", "català", map[string][]string{ + and: []string{ + "* ", + "I ", + }, + background: []string{ + "Rerefons", + "Antecedents", + }, + but: []string{ + "* ", + "Però ", + }, + examples: []string{ + "Exemples", + }, + feature: []string{ + "Característica", + "Funcionalitat", + }, + given: []string{ + "* ", + "Donat ", + "Donada ", + "Atès ", + "Atesa ", + }, + scenario: []string{ + "Escenari", + }, + scenarioOutline: []string{ + "Esquema de l'escenari", + }, + then: []string{ + "* ", + "Aleshores ", + "Cal ", + }, + when: []string{ + "* ", + "Quan ", + }, + }, + }, + "cs": &GherkinDialect{ + "cs", "Czech", "Česky", map[string][]string{ + and: []string{ + "* ", + "A také ", + "A ", + }, + background: []string{ + "Pozadí", + "Kontext", + }, + but: []string{ + "* ", + "Ale ", + }, + examples: []string{ + "Příklady", + }, + feature: []string{ + "Požadavek", + }, + given: []string{ + "* ", + "Pokud ", + "Za předpokladu ", + }, + scenario: []string{ + "Scénář", + }, + scenarioOutline: []string{ + "Náčrt Scénáře", + "Osnova scénáře", + }, + then: []string{ + "* ", + "Pak ", + }, + when: []string{ + "* ", + "Když ", + }, + }, + }, + "cy-GB": &GherkinDialect{ + "cy-GB", "Welsh", "Cymraeg", map[string][]string{ + and: []string{ + "* ", + "A ", + }, + background: []string{ + "Cefndir", + }, + but: []string{ + "* ", + "Ond ", + }, + examples: []string{ + "Enghreifftiau", + }, + feature: []string{ + "Arwedd", + }, + given: []string{ + "* ", + "Anrhegedig a ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Scenario Amlinellol", + }, + then: []string{ + "* ", + "Yna ", + }, + when: []string{ + "* ", + "Pryd ", + }, + }, + }, + "da": &GherkinDialect{ + "da", "Danish", "dansk", map[string][]string{ + and: []string{ + "* ", + "Og ", + }, + background: []string{ + "Baggrund", + }, + but: []string{ + "* ", + "Men ", + }, + examples: []string{ + "Eksempler", + }, + feature: []string{ + "Egenskab", + }, + given: []string{ + "* ", + "Givet ", + }, + scenario: []string{ + "Scenarie", + }, + scenarioOutline: []string{ + "Abstrakt Scenario", + }, + then: []string{ + "* ", + "Så ", + }, + when: []string{ + "* ", + "Når ", + }, + }, + }, + "de": &GherkinDialect{ + "de", "German", "Deutsch", map[string][]string{ + and: []string{ + "* ", + "Und ", + }, + background: []string{ + "Grundlage", + }, + but: []string{ + "* ", + "Aber ", + }, + examples: []string{ + "Beispiele", + }, + feature: []string{ + "Funktionalität", + }, + given: []string{ + "* ", + "Angenommen ", + "Gegeben sei ", + "Gegeben seien ", + }, + scenario: []string{ + "Szenario", + }, + scenarioOutline: []string{ + "Szenariogrundriss", + }, + then: []string{ + "* ", + "Dann ", + }, + when: []string{ + "* ", + "Wenn ", + }, + }, + }, + "el": &GherkinDialect{ + "el", "Greek", "Ελληνικά", map[string][]string{ + and: []string{ + "* ", + "Και ", + }, + background: []string{ + "Υπόβαθρο", + }, + but: []string{ + "* ", + "Αλλά ", + }, + examples: []string{ + "Παραδείγματα", + "Σενάρια", + }, + feature: []string{ + "Δυνατότητα", + "Λειτουργία", + }, + given: []string{ + "* ", + "Δεδομένου ", + }, + scenario: []string{ + "Σενάριο", + }, + scenarioOutline: []string{ + "Περιγραφή Σεναρίου", + }, + then: []string{ + "* ", + "Τότε ", + }, + when: []string{ + "* ", + "Όταν ", + }, + }, + }, + "em": &GherkinDialect{ + "em", "Emoji", "😀", map[string][]string{ + and: []string{ + "* ", + "😂", + }, + background: []string{ + "💤", + }, + but: []string{ + "* ", + "😔", + }, + examples: []string{ + "📓", + }, + feature: []string{ + "📚", + }, + given: []string{ + "* ", + "😐", + }, + scenario: []string{ + "📕", + }, + scenarioOutline: []string{ + "📖", + }, + then: []string{ + "* ", + "🙏", + }, + when: []string{ + "* ", + "🎬", + }, + }, + }, + "en": &GherkinDialect{ + "en", "English", "English", map[string][]string{ + and: []string{ + "* ", + "And ", + }, + background: []string{ + "Background", + }, + but: []string{ + "* ", + "But ", + }, + examples: []string{ + "Examples", + "Scenarios", + }, + feature: []string{ + "Feature", + "Business Need", + "Ability", + }, + given: []string{ + "* ", + "Given ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Scenario Outline", + "Scenario Template", + }, + then: []string{ + "* ", + "Then ", + }, + when: []string{ + "* ", + "When ", + }, + }, + }, + "en-Scouse": &GherkinDialect{ + "en-Scouse", "Scouse", "Scouse", map[string][]string{ + and: []string{ + "* ", + "An ", + }, + background: []string{ + "Dis is what went down", + }, + but: []string{ + "* ", + "Buh ", + }, + examples: []string{ + "Examples", + }, + feature: []string{ + "Feature", + }, + given: []string{ + "* ", + "Givun ", + "Youse know when youse got ", + }, + scenario: []string{ + "The thing of it is", + }, + scenarioOutline: []string{ + "Wharrimean is", + }, + then: []string{ + "* ", + "Dun ", + "Den youse gotta ", + }, + when: []string{ + "* ", + "Wun ", + "Youse know like when ", + }, + }, + }, + "en-au": &GherkinDialect{ + "en-au", "Australian", "Australian", map[string][]string{ + and: []string{ + "* ", + "Too right ", + }, + background: []string{ + "First off", + }, + but: []string{ + "* ", + "Yeah nah ", + }, + examples: []string{ + "You'll wanna", + }, + feature: []string{ + "Pretty much", + }, + given: []string{ + "* ", + "Y'know ", + }, + scenario: []string{ + "Awww, look mate", + }, + scenarioOutline: []string{ + "Reckon it's like", + }, + then: []string{ + "* ", + "But at the end of the day I reckon ", + }, + when: []string{ + "* ", + "It's just unbelievable ", + }, + }, + }, + "en-lol": &GherkinDialect{ + "en-lol", "LOLCAT", "LOLCAT", map[string][]string{ + and: []string{ + "* ", + "AN ", + }, + background: []string{ + "B4", + }, + but: []string{ + "* ", + "BUT ", + }, + examples: []string{ + "EXAMPLZ", + }, + feature: []string{ + "OH HAI", + }, + given: []string{ + "* ", + "I CAN HAZ ", + }, + scenario: []string{ + "MISHUN", + }, + scenarioOutline: []string{ + "MISHUN SRSLY", + }, + then: []string{ + "* ", + "DEN ", + }, + when: []string{ + "* ", + "WEN ", + }, + }, + }, + "en-old": &GherkinDialect{ + "en-old", "Old English", "Englisc", map[string][]string{ + and: []string{ + "* ", + "Ond ", + "7 ", + }, + background: []string{ + "Aer", + "Ær", + }, + but: []string{ + "* ", + "Ac ", + }, + examples: []string{ + "Se the", + "Se þe", + "Se ðe", + }, + feature: []string{ + "Hwaet", + "Hwæt", + }, + given: []string{ + "* ", + "Thurh ", + "Þurh ", + "Ðurh ", + }, + scenario: []string{ + "Swa", + }, + scenarioOutline: []string{ + "Swa hwaer swa", + "Swa hwær swa", + }, + then: []string{ + "* ", + "Tha ", + "Þa ", + "Ða ", + "Tha the ", + "Þa þe ", + "Ða ðe ", + }, + when: []string{ + "* ", + "Tha ", + "Þa ", + "Ða ", + }, + }, + }, + "en-pirate": &GherkinDialect{ + "en-pirate", "Pirate", "Pirate", map[string][]string{ + and: []string{ + "* ", + "Aye ", + }, + background: []string{ + "Yo-ho-ho", + }, + but: []string{ + "* ", + "Avast! ", + }, + examples: []string{ + "Dead men tell no tales", + }, + feature: []string{ + "Ahoy matey!", + }, + given: []string{ + "* ", + "Gangway! ", + }, + scenario: []string{ + "Heave to", + }, + scenarioOutline: []string{ + "Shiver me timbers", + }, + then: []string{ + "* ", + "Let go and haul ", + }, + when: []string{ + "* ", + "Blimey! ", + }, + }, + }, + "eo": &GherkinDialect{ + "eo", "Esperanto", "Esperanto", map[string][]string{ + and: []string{ + "* ", + "Kaj ", + }, + background: []string{ + "Fono", + }, + but: []string{ + "* ", + "Sed ", + }, + examples: []string{ + "Ekzemploj", + }, + feature: []string{ + "Trajto", + }, + given: []string{ + "* ", + "Donitaĵo ", + "Komence ", + }, + scenario: []string{ + "Scenaro", + "Kazo", + }, + scenarioOutline: []string{ + "Konturo de la scenaro", + "Skizo", + "Kazo-skizo", + }, + then: []string{ + "* ", + "Do ", + }, + when: []string{ + "* ", + "Se ", + }, + }, + }, + "es": &GherkinDialect{ + "es", "Spanish", "español", map[string][]string{ + and: []string{ + "* ", + "Y ", + "E ", + }, + background: []string{ + "Antecedentes", + }, + but: []string{ + "* ", + "Pero ", + }, + examples: []string{ + "Ejemplos", + }, + feature: []string{ + "Característica", + }, + given: []string{ + "* ", + "Dado ", + "Dada ", + "Dados ", + "Dadas ", + }, + scenario: []string{ + "Escenario", + }, + scenarioOutline: []string{ + "Esquema del escenario", + }, + then: []string{ + "* ", + "Entonces ", + }, + when: []string{ + "* ", + "Cuando ", + }, + }, + }, + "et": &GherkinDialect{ + "et", "Estonian", "eesti keel", map[string][]string{ + and: []string{ + "* ", + "Ja ", + }, + background: []string{ + "Taust", + }, + but: []string{ + "* ", + "Kuid ", + }, + examples: []string{ + "Juhtumid", + }, + feature: []string{ + "Omadus", + }, + given: []string{ + "* ", + "Eeldades ", + }, + scenario: []string{ + "Stsenaarium", + }, + scenarioOutline: []string{ + "Raamstsenaarium", + }, + then: []string{ + "* ", + "Siis ", + }, + when: []string{ + "* ", + "Kui ", + }, + }, + }, + "fa": &GherkinDialect{ + "fa", "Persian", "فارسی", map[string][]string{ + and: []string{ + "* ", + "و ", + }, + background: []string{ + "زمینه", + }, + but: []string{ + "* ", + "اما ", + }, + examples: []string{ + "نمونه ها", + }, + feature: []string{ + "وِیژگی", + }, + given: []string{ + "* ", + "با فرض ", + }, + scenario: []string{ + "سناریو", + }, + scenarioOutline: []string{ + "الگوی سناریو", + }, + then: []string{ + "* ", + "آنگاه ", + }, + when: []string{ + "* ", + "هنگامی ", + }, + }, + }, + "fi": &GherkinDialect{ + "fi", "Finnish", "suomi", map[string][]string{ + and: []string{ + "* ", + "Ja ", + }, + background: []string{ + "Tausta", + }, + but: []string{ + "* ", + "Mutta ", + }, + examples: []string{ + "Tapaukset", + }, + feature: []string{ + "Ominaisuus", + }, + given: []string{ + "* ", + "Oletetaan ", + }, + scenario: []string{ + "Tapaus", + }, + scenarioOutline: []string{ + "Tapausaihio", + }, + then: []string{ + "* ", + "Niin ", + }, + when: []string{ + "* ", + "Kun ", + }, + }, + }, + "fr": &GherkinDialect{ + "fr", "French", "français", map[string][]string{ + and: []string{ + "* ", + "Et que ", + "Et qu'", + "Et ", + }, + background: []string{ + "Contexte", + }, + but: []string{ + "* ", + "Mais que ", + "Mais qu'", + "Mais ", + }, + examples: []string{ + "Exemples", + }, + feature: []string{ + "Fonctionnalité", + }, + given: []string{ + "* ", + "Soit ", + "Etant donné que ", + "Etant donné qu'", + "Etant donné ", + "Etant donnée ", + "Etant donnés ", + "Etant données ", + "Étant donné que ", + "Étant donné qu'", + "Étant donné ", + "Étant donnée ", + "Étant donnés ", + "Étant données ", + }, + scenario: []string{ + "Scénario", + }, + scenarioOutline: []string{ + "Plan du scénario", + "Plan du Scénario", + }, + then: []string{ + "* ", + "Alors ", + }, + when: []string{ + "* ", + "Quand ", + "Lorsque ", + "Lorsqu'", + }, + }, + }, + "ga": &GherkinDialect{ + "ga", "Irish", "Gaeilge", map[string][]string{ + and: []string{ + "* ", + "Agus", + }, + background: []string{ + "Cúlra", + }, + but: []string{ + "* ", + "Ach", + }, + examples: []string{ + "Samplaí", + }, + feature: []string{ + "Gné", + }, + given: []string{ + "* ", + "Cuir i gcás go", + "Cuir i gcás nach", + "Cuir i gcás gur", + "Cuir i gcás nár", + }, + scenario: []string{ + "Cás", + }, + scenarioOutline: []string{ + "Cás Achomair", + }, + then: []string{ + "* ", + "Ansin", + }, + when: []string{ + "* ", + "Nuair a", + "Nuair nach", + "Nuair ba", + "Nuair nár", + }, + }, + }, + "gj": &GherkinDialect{ + "gj", "Gujarati", "ગુજરાતી", map[string][]string{ + and: []string{ + "* ", + "અને ", + }, + background: []string{ + "બેકગ્રાઉન્ડ", + }, + but: []string{ + "* ", + "પણ ", + }, + examples: []string{ + "ઉદાહરણો", + }, + feature: []string{ + "લક્ષણ", + "વ્યાપાર જરૂર", + "ક્ષમતા", + }, + given: []string{ + "* ", + "આપેલ છે ", + }, + scenario: []string{ + "સ્થિતિ", + }, + scenarioOutline: []string{ + "પરિદ્દશ્ય રૂપરેખા", + "પરિદ્દશ્ય ઢાંચો", + }, + then: []string{ + "* ", + "પછી ", + }, + when: []string{ + "* ", + "ક્યારે ", + }, + }, + }, + "gl": &GherkinDialect{ + "gl", "Galician", "galego", map[string][]string{ + and: []string{ + "* ", + "E ", + }, + background: []string{ + "Contexto", + }, + but: []string{ + "* ", + "Mais ", + "Pero ", + }, + examples: []string{ + "Exemplos", + }, + feature: []string{ + "Característica", + }, + given: []string{ + "* ", + "Dado ", + "Dada ", + "Dados ", + "Dadas ", + }, + scenario: []string{ + "Escenario", + }, + scenarioOutline: []string{ + "Esbozo do escenario", + }, + then: []string{ + "* ", + "Entón ", + "Logo ", + }, + when: []string{ + "* ", + "Cando ", + }, + }, + }, + "he": &GherkinDialect{ + "he", "Hebrew", "עברית", map[string][]string{ + and: []string{ + "* ", + "וגם ", + }, + background: []string{ + "רקע", + }, + but: []string{ + "* ", + "אבל ", + }, + examples: []string{ + "דוגמאות", + }, + feature: []string{ + "תכונה", + }, + given: []string{ + "* ", + "בהינתן ", + }, + scenario: []string{ + "תרחיש", + }, + scenarioOutline: []string{ + "תבנית תרחיש", + }, + then: []string{ + "* ", + "אז ", + "אזי ", + }, + when: []string{ + "* ", + "כאשר ", + }, + }, + }, + "hi": &GherkinDialect{ + "hi", "Hindi", "हिंदी", map[string][]string{ + and: []string{ + "* ", + "और ", + "तथा ", + }, + background: []string{ + "पृष्ठभूमि", + }, + but: []string{ + "* ", + "पर ", + "परन्तु ", + "किन्तु ", + }, + examples: []string{ + "उदाहरण", + }, + feature: []string{ + "रूप लेख", + }, + given: []string{ + "* ", + "अगर ", + "यदि ", + "चूंकि ", + }, + scenario: []string{ + "परिदृश्य", + }, + scenarioOutline: []string{ + "परिदृश्य रूपरेखा", + }, + then: []string{ + "* ", + "तब ", + "तदा ", + }, + when: []string{ + "* ", + "जब ", + "कदा ", + }, + }, + }, + "hr": &GherkinDialect{ + "hr", "Croatian", "hrvatski", map[string][]string{ + and: []string{ + "* ", + "I ", + }, + background: []string{ + "Pozadina", + }, + but: []string{ + "* ", + "Ali ", + }, + examples: []string{ + "Primjeri", + "Scenariji", + }, + feature: []string{ + "Osobina", + "Mogućnost", + "Mogucnost", + }, + given: []string{ + "* ", + "Zadan ", + "Zadani ", + "Zadano ", + }, + scenario: []string{ + "Scenarij", + }, + scenarioOutline: []string{ + "Skica", + "Koncept", + }, + then: []string{ + "* ", + "Onda ", + }, + when: []string{ + "* ", + "Kada ", + "Kad ", + }, + }, + }, + "ht": &GherkinDialect{ + "ht", "Creole", "kreyòl", map[string][]string{ + and: []string{ + "* ", + "Ak ", + "Epi ", + "E ", + }, + background: []string{ + "Kontèks", + "Istorik", + }, + but: []string{ + "* ", + "Men ", + }, + examples: []string{ + "Egzanp", + }, + feature: []string{ + "Karakteristik", + "Mak", + "Fonksyonalite", + }, + given: []string{ + "* ", + "Sipoze ", + "Sipoze ke ", + "Sipoze Ke ", + }, + scenario: []string{ + "Senaryo", + }, + scenarioOutline: []string{ + "Plan senaryo", + "Plan Senaryo", + "Senaryo deskripsyon", + "Senaryo Deskripsyon", + "Dyagram senaryo", + "Dyagram Senaryo", + }, + then: []string{ + "* ", + "Lè sa a ", + "Le sa a ", + }, + when: []string{ + "* ", + "Lè ", + "Le ", + }, + }, + }, + "hu": &GherkinDialect{ + "hu", "Hungarian", "magyar", map[string][]string{ + and: []string{ + "* ", + "És ", + }, + background: []string{ + "Háttér", + }, + but: []string{ + "* ", + "De ", + }, + examples: []string{ + "Példák", + }, + feature: []string{ + "Jellemző", + }, + given: []string{ + "* ", + "Amennyiben ", + "Adott ", + }, + scenario: []string{ + "Forgatókönyv", + }, + scenarioOutline: []string{ + "Forgatókönyv vázlat", + }, + then: []string{ + "* ", + "Akkor ", + }, + when: []string{ + "* ", + "Majd ", + "Ha ", + "Amikor ", + }, + }, + }, + "id": &GherkinDialect{ + "id", "Indonesian", "Bahasa Indonesia", map[string][]string{ + and: []string{ + "* ", + "Dan ", + }, + background: []string{ + "Dasar", + }, + but: []string{ + "* ", + "Tapi ", + }, + examples: []string{ + "Contoh", + }, + feature: []string{ + "Fitur", + }, + given: []string{ + "* ", + "Dengan ", + }, + scenario: []string{ + "Skenario", + }, + scenarioOutline: []string{ + "Skenario konsep", + }, + then: []string{ + "* ", + "Maka ", + }, + when: []string{ + "* ", + "Ketika ", + }, + }, + }, + "is": &GherkinDialect{ + "is", "Icelandic", "Íslenska", map[string][]string{ + and: []string{ + "* ", + "Og ", + }, + background: []string{ + "Bakgrunnur", + }, + but: []string{ + "* ", + "En ", + }, + examples: []string{ + "Dæmi", + "Atburðarásir", + }, + feature: []string{ + "Eiginleiki", + }, + given: []string{ + "* ", + "Ef ", + }, + scenario: []string{ + "Atburðarás", + }, + scenarioOutline: []string{ + "Lýsing Atburðarásar", + "Lýsing Dæma", + }, + then: []string{ + "* ", + "Þá ", + }, + when: []string{ + "* ", + "Þegar ", + }, + }, + }, + "it": &GherkinDialect{ + "it", "Italian", "italiano", map[string][]string{ + and: []string{ + "* ", + "E ", + }, + background: []string{ + "Contesto", + }, + but: []string{ + "* ", + "Ma ", + }, + examples: []string{ + "Esempi", + }, + feature: []string{ + "Funzionalità", + }, + given: []string{ + "* ", + "Dato ", + "Data ", + "Dati ", + "Date ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Schema dello scenario", + }, + then: []string{ + "* ", + "Allora ", + }, + when: []string{ + "* ", + "Quando ", + }, + }, + }, + "ja": &GherkinDialect{ + "ja", "Japanese", "日本語", map[string][]string{ + and: []string{ + "* ", + "かつ", + }, + background: []string{ + "背景", + }, + but: []string{ + "* ", + "しかし", + "但し", + "ただし", + }, + examples: []string{ + "例", + "サンプル", + }, + feature: []string{ + "フィーチャ", + "機能", + }, + given: []string{ + "* ", + "前提", + }, + scenario: []string{ + "シナリオ", + }, + scenarioOutline: []string{ + "シナリオアウトライン", + "シナリオテンプレート", + "テンプレ", + "シナリオテンプレ", + }, + then: []string{ + "* ", + "ならば", + }, + when: []string{ + "* ", + "もし", + }, + }, + }, + "jv": &GherkinDialect{ + "jv", "Javanese", "Basa Jawa", map[string][]string{ + and: []string{ + "* ", + "Lan ", + }, + background: []string{ + "Dasar", + }, + but: []string{ + "* ", + "Tapi ", + "Nanging ", + "Ananging ", + }, + examples: []string{ + "Conto", + "Contone", + }, + feature: []string{ + "Fitur", + }, + given: []string{ + "* ", + "Nalika ", + "Nalikaning ", + }, + scenario: []string{ + "Skenario", + }, + scenarioOutline: []string{ + "Konsep skenario", + }, + then: []string{ + "* ", + "Njuk ", + "Banjur ", + }, + when: []string{ + "* ", + "Manawa ", + "Menawa ", + }, + }, + }, + "kn": &GherkinDialect{ + "kn", "Kannada", "ಕನ್ನಡ", map[string][]string{ + and: []string{ + "* ", + "ಮತ್ತು ", + }, + background: []string{ + "ಹಿನ್ನೆಲೆ", + }, + but: []string{ + "* ", + "ಆದರೆ ", + }, + examples: []string{ + "ಉದಾಹರಣೆಗಳು", + }, + feature: []string{ + "ಹೆಚ್ಚಳ", + }, + given: []string{ + "* ", + "ನೀಡಿದ ", + }, + scenario: []string{ + "ಕಥಾಸಾರಾಂಶ", + }, + scenarioOutline: []string{ + "ವಿವರಣೆ", + }, + then: []string{ + "* ", + "ನಂತರ ", + }, + when: []string{ + "* ", + "ಸ್ಥಿತಿಯನ್ನು ", + }, + }, + }, + "ko": &GherkinDialect{ + "ko", "Korean", "한국어", map[string][]string{ + and: []string{ + "* ", + "그리고", + }, + background: []string{ + "배경", + }, + but: []string{ + "* ", + "하지만", + "단", + }, + examples: []string{ + "예", + }, + feature: []string{ + "기능", + }, + given: []string{ + "* ", + "조건", + "먼저", + }, + scenario: []string{ + "시나리오", + }, + scenarioOutline: []string{ + "시나리오 개요", + }, + then: []string{ + "* ", + "그러면", + }, + when: []string{ + "* ", + "만일", + "만약", + }, + }, + }, + "lt": &GherkinDialect{ + "lt", "Lithuanian", "lietuvių kalba", map[string][]string{ + and: []string{ + "* ", + "Ir ", + }, + background: []string{ + "Kontekstas", + }, + but: []string{ + "* ", + "Bet ", + }, + examples: []string{ + "Pavyzdžiai", + "Scenarijai", + "Variantai", + }, + feature: []string{ + "Savybė", + }, + given: []string{ + "* ", + "Duota ", + }, + scenario: []string{ + "Scenarijus", + }, + scenarioOutline: []string{ + "Scenarijaus šablonas", + }, + then: []string{ + "* ", + "Tada ", + }, + when: []string{ + "* ", + "Kai ", + }, + }, + }, + "lu": &GherkinDialect{ + "lu", "Luxemburgish", "Lëtzebuergesch", map[string][]string{ + and: []string{ + "* ", + "an ", + "a ", + }, + background: []string{ + "Hannergrond", + }, + but: []string{ + "* ", + "awer ", + "mä ", + }, + examples: []string{ + "Beispiller", + }, + feature: []string{ + "Funktionalitéit", + }, + given: []string{ + "* ", + "ugeholl ", + }, + scenario: []string{ + "Szenario", + }, + scenarioOutline: []string{ + "Plang vum Szenario", + }, + then: []string{ + "* ", + "dann ", + }, + when: []string{ + "* ", + "wann ", + }, + }, + }, + "lv": &GherkinDialect{ + "lv", "Latvian", "latviešu", map[string][]string{ + and: []string{ + "* ", + "Un ", + }, + background: []string{ + "Konteksts", + "Situācija", + }, + but: []string{ + "* ", + "Bet ", + }, + examples: []string{ + "Piemēri", + "Paraugs", + }, + feature: []string{ + "Funkcionalitāte", + "Fīča", + }, + given: []string{ + "* ", + "Kad ", + }, + scenario: []string{ + "Scenārijs", + }, + scenarioOutline: []string{ + "Scenārijs pēc parauga", + }, + then: []string{ + "* ", + "Tad ", + }, + when: []string{ + "* ", + "Ja ", + }, + }, + }, + "mn": &GherkinDialect{ + "mn", "Mongolian", "монгол", map[string][]string{ + and: []string{ + "* ", + "Мөн ", + "Тэгээд ", + }, + background: []string{ + "Агуулга", + }, + but: []string{ + "* ", + "Гэхдээ ", + "Харин ", + }, + examples: []string{ + "Тухайлбал", + }, + feature: []string{ + "Функц", + "Функционал", + }, + given: []string{ + "* ", + "Өгөгдсөн нь ", + "Анх ", + }, + scenario: []string{ + "Сценар", + }, + scenarioOutline: []string{ + "Сценарын төлөвлөгөө", + }, + then: []string{ + "* ", + "Тэгэхэд ", + "Үүний дараа ", + }, + when: []string{ + "* ", + "Хэрэв ", + }, + }, + }, + "nl": &GherkinDialect{ + "nl", "Dutch", "Nederlands", map[string][]string{ + and: []string{ + "* ", + "En ", + }, + background: []string{ + "Achtergrond", + }, + but: []string{ + "* ", + "Maar ", + }, + examples: []string{ + "Voorbeelden", + }, + feature: []string{ + "Functionaliteit", + }, + given: []string{ + "* ", + "Gegeven ", + "Stel ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Abstract Scenario", + }, + then: []string{ + "* ", + "Dan ", + }, + when: []string{ + "* ", + "Als ", + }, + }, + }, + "no": &GherkinDialect{ + "no", "Norwegian", "norsk", map[string][]string{ + and: []string{ + "* ", + "Og ", + }, + background: []string{ + "Bakgrunn", + }, + but: []string{ + "* ", + "Men ", + }, + examples: []string{ + "Eksempler", + }, + feature: []string{ + "Egenskap", + }, + given: []string{ + "* ", + "Gitt ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Scenariomal", + "Abstrakt Scenario", + }, + then: []string{ + "* ", + "Så ", + }, + when: []string{ + "* ", + "Når ", + }, + }, + }, + "pa": &GherkinDialect{ + "pa", "Panjabi", "ਪੰਜਾਬੀ", map[string][]string{ + and: []string{ + "* ", + "ਅਤੇ ", + }, + background: []string{ + "ਪਿਛੋਕੜ", + }, + but: []string{ + "* ", + "ਪਰ ", + }, + examples: []string{ + "ਉਦਾਹਰਨਾਂ", + }, + feature: []string{ + "ਖਾਸੀਅਤ", + "ਮੁਹਾਂਦਰਾ", + "ਨਕਸ਼ ਨੁਹਾਰ", + }, + given: []string{ + "* ", + "ਜੇਕਰ ", + "ਜਿਵੇਂ ਕਿ ", + }, + scenario: []string{ + "ਪਟਕਥਾ", + }, + scenarioOutline: []string{ + "ਪਟਕਥਾ ਢਾਂਚਾ", + "ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ", + }, + then: []string{ + "* ", + "ਤਦ ", + }, + when: []string{ + "* ", + "ਜਦੋਂ ", + }, + }, + }, + "pl": &GherkinDialect{ + "pl", "Polish", "polski", map[string][]string{ + and: []string{ + "* ", + "Oraz ", + "I ", + }, + background: []string{ + "Założenia", + }, + but: []string{ + "* ", + "Ale ", + }, + examples: []string{ + "Przykłady", + }, + feature: []string{ + "Właściwość", + "Funkcja", + "Aspekt", + "Potrzeba biznesowa", + }, + given: []string{ + "* ", + "Zakładając ", + "Mając ", + "Zakładając, że ", + }, + scenario: []string{ + "Scenariusz", + }, + scenarioOutline: []string{ + "Szablon scenariusza", + }, + then: []string{ + "* ", + "Wtedy ", + }, + when: []string{ + "* ", + "Jeżeli ", + "Jeśli ", + "Gdy ", + "Kiedy ", + }, + }, + }, + "pt": &GherkinDialect{ + "pt", "Portuguese", "português", map[string][]string{ + and: []string{ + "* ", + "E ", + }, + background: []string{ + "Contexto", + "Cenário de Fundo", + "Cenario de Fundo", + "Fundo", + }, + but: []string{ + "* ", + "Mas ", + }, + examples: []string{ + "Exemplos", + "Cenários", + "Cenarios", + }, + feature: []string{ + "Funcionalidade", + "Característica", + "Caracteristica", + }, + given: []string{ + "* ", + "Dado ", + "Dada ", + "Dados ", + "Dadas ", + }, + scenario: []string{ + "Cenário", + "Cenario", + }, + scenarioOutline: []string{ + "Esquema do Cenário", + "Esquema do Cenario", + "Delineação do Cenário", + "Delineacao do Cenario", + }, + then: []string{ + "* ", + "Então ", + "Entao ", + }, + when: []string{ + "* ", + "Quando ", + }, + }, + }, + "ro": &GherkinDialect{ + "ro", "Romanian", "română", map[string][]string{ + and: []string{ + "* ", + "Si ", + "Și ", + "Şi ", + }, + background: []string{ + "Context", + }, + but: []string{ + "* ", + "Dar ", + }, + examples: []string{ + "Exemple", + }, + feature: []string{ + "Functionalitate", + "Funcționalitate", + "Funcţionalitate", + }, + given: []string{ + "* ", + "Date fiind ", + "Dat fiind ", + "Dati fiind ", + "Dați fiind ", + "Daţi fiind ", + }, + scenario: []string{ + "Scenariu", + }, + scenarioOutline: []string{ + "Structura scenariu", + "Structură scenariu", + }, + then: []string{ + "* ", + "Atunci ", + }, + when: []string{ + "* ", + "Cand ", + "Când ", + }, + }, + }, + "ru": &GherkinDialect{ + "ru", "Russian", "русский", map[string][]string{ + and: []string{ + "* ", + "И ", + "К тому же ", + "Также ", + }, + background: []string{ + "Предыстория", + "Контекст", + }, + but: []string{ + "* ", + "Но ", + "А ", + }, + examples: []string{ + "Примеры", + }, + feature: []string{ + "Функция", + "Функционал", + "Свойство", + }, + given: []string{ + "* ", + "Допустим ", + "Дано ", + "Пусть ", + }, + scenario: []string{ + "Сценарий", + }, + scenarioOutline: []string{ + "Структура сценария", + }, + then: []string{ + "* ", + "То ", + "Тогда ", + }, + when: []string{ + "* ", + "Если ", + "Когда ", + }, + }, + }, + "sk": &GherkinDialect{ + "sk", "Slovak", "Slovensky", map[string][]string{ + and: []string{ + "* ", + "A ", + "A tiež ", + "A taktiež ", + "A zároveň ", + }, + background: []string{ + "Pozadie", + }, + but: []string{ + "* ", + "Ale ", + }, + examples: []string{ + "Príklady", + }, + feature: []string{ + "Požiadavka", + "Funkcia", + "Vlastnosť", + }, + given: []string{ + "* ", + "Pokiaľ ", + "Za predpokladu ", + }, + scenario: []string{ + "Scenár", + }, + scenarioOutline: []string{ + "Náčrt Scenáru", + "Náčrt Scenára", + "Osnova Scenára", + }, + then: []string{ + "* ", + "Tak ", + "Potom ", + }, + when: []string{ + "* ", + "Keď ", + "Ak ", + }, + }, + }, + "sl": &GherkinDialect{ + "sl", "Slovenian", "Slovenski", map[string][]string{ + and: []string{ + "In ", + "Ter ", + }, + background: []string{ + "Kontekst", + "Osnova", + "Ozadje", + }, + but: []string{ + "Toda ", + "Ampak ", + "Vendar ", + }, + examples: []string{ + "Primeri", + "Scenariji", + }, + feature: []string{ + "Funkcionalnost", + "Funkcija", + "Možnosti", + "Moznosti", + "Lastnost", + "Značilnost", + }, + given: []string{ + "Dano ", + "Podano ", + "Zaradi ", + "Privzeto ", + }, + scenario: []string{ + "Scenarij", + "Primer", + }, + scenarioOutline: []string{ + "Struktura scenarija", + "Skica", + "Koncept", + "Oris scenarija", + "Osnutek", + }, + then: []string{ + "Nato ", + "Potem ", + "Takrat ", + }, + when: []string{ + "Ko ", + "Ce ", + "Če ", + "Kadar ", + }, + }, + }, + "sr-Cyrl": &GherkinDialect{ + "sr-Cyrl", "Serbian", "Српски", map[string][]string{ + and: []string{ + "* ", + "И ", + }, + background: []string{ + "Контекст", + "Основа", + "Позадина", + }, + but: []string{ + "* ", + "Али ", + }, + examples: []string{ + "Примери", + "Сценарији", + }, + feature: []string{ + "Функционалност", + "Могућност", + "Особина", + }, + given: []string{ + "* ", + "За дато ", + "За дате ", + "За дати ", + }, + scenario: []string{ + "Сценарио", + "Пример", + }, + scenarioOutline: []string{ + "Структура сценарија", + "Скица", + "Концепт", + }, + then: []string{ + "* ", + "Онда ", + }, + when: []string{ + "* ", + "Када ", + "Кад ", + }, + }, + }, + "sr-Latn": &GherkinDialect{ + "sr-Latn", "Serbian (Latin)", "Srpski (Latinica)", map[string][]string{ + and: []string{ + "* ", + "I ", + }, + background: []string{ + "Kontekst", + "Osnova", + "Pozadina", + }, + but: []string{ + "* ", + "Ali ", + }, + examples: []string{ + "Primeri", + "Scenariji", + }, + feature: []string{ + "Funkcionalnost", + "Mogućnost", + "Mogucnost", + "Osobina", + }, + given: []string{ + "* ", + "Za dato ", + "Za date ", + "Za dati ", + }, + scenario: []string{ + "Scenario", + "Primer", + }, + scenarioOutline: []string{ + "Struktura scenarija", + "Skica", + "Koncept", + }, + then: []string{ + "* ", + "Onda ", + }, + when: []string{ + "* ", + "Kada ", + "Kad ", + }, + }, + }, + "sv": &GherkinDialect{ + "sv", "Swedish", "Svenska", map[string][]string{ + and: []string{ + "* ", + "Och ", + }, + background: []string{ + "Bakgrund", + }, + but: []string{ + "* ", + "Men ", + }, + examples: []string{ + "Exempel", + }, + feature: []string{ + "Egenskap", + }, + given: []string{ + "* ", + "Givet ", + }, + scenario: []string{ + "Scenario", + }, + scenarioOutline: []string{ + "Abstrakt Scenario", + "Scenariomall", + }, + then: []string{ + "* ", + "Så ", + }, + when: []string{ + "* ", + "När ", + }, + }, + }, + "ta": &GherkinDialect{ + "ta", "Tamil", "தமிழ்", map[string][]string{ + and: []string{ + "* ", + "மேலும் ", + "மற்றும் ", + }, + background: []string{ + "பின்னணி", + }, + but: []string{ + "* ", + "ஆனால் ", + }, + examples: []string{ + "எடுத்துக்காட்டுகள்", + "காட்சிகள்", + " நிலைமைகளில்", + }, + feature: []string{ + "அம்சம்", + "வணிக தேவை", + "திறன்", + }, + given: []string{ + "* ", + "கொடுக்கப்பட்ட ", + }, + scenario: []string{ + "காட்சி", + }, + scenarioOutline: []string{ + "காட்சி சுருக்கம்", + "காட்சி வார்ப்புரு", + }, + then: []string{ + "* ", + "அப்பொழுது ", + }, + when: []string{ + "* ", + "எப்போது ", + }, + }, + }, + "th": &GherkinDialect{ + "th", "Thai", "ไทย", map[string][]string{ + and: []string{ + "* ", + "และ ", + }, + background: []string{ + "แนวคิด", + }, + but: []string{ + "* ", + "แต่ ", + }, + examples: []string{ + "ชุดของตัวอย่าง", + "ชุดของเหตุการณ์", + }, + feature: []string{ + "โครงหลัก", + "ความต้องการทางธุรกิจ", + "ความสามารถ", + }, + given: []string{ + "* ", + "กำหนดให้ ", + }, + scenario: []string{ + "เหตุการณ์", + }, + scenarioOutline: []string{ + "สรุปเหตุการณ์", + "โครงสร้างของเหตุการณ์", + }, + then: []string{ + "* ", + "ดังนั้น ", + }, + when: []string{ + "* ", + "เมื่อ ", + }, + }, + }, + "tl": &GherkinDialect{ + "tl", "Telugu", "తెలుగు", map[string][]string{ + and: []string{ + "* ", + "మరియు ", + }, + background: []string{ + "నేపథ్యం", + }, + but: []string{ + "* ", + "కాని ", + }, + examples: []string{ + "ఉదాహరణలు", + }, + feature: []string{ + "గుణము", + }, + given: []string{ + "* ", + "చెప్పబడినది ", + }, + scenario: []string{ + "సన్నివేశం", + }, + scenarioOutline: []string{ + "కథనం", + }, + then: []string{ + "* ", + "అప్పుడు ", + }, + when: []string{ + "* ", + "ఈ పరిస్థితిలో ", + }, + }, + }, + "tlh": &GherkinDialect{ + "tlh", "Klingon", "tlhIngan", map[string][]string{ + and: []string{ + "* ", + "'ej ", + "latlh ", + }, + background: []string{ + "mo'", + }, + but: []string{ + "* ", + "'ach ", + "'a ", + }, + examples: []string{ + "ghantoH", + "lutmey", + }, + feature: []string{ + "Qap", + "Qu'meH 'ut", + "perbogh", + "poQbogh malja'", + "laH", + }, + given: []string{ + "* ", + "ghu' noblu' ", + "DaH ghu' bejlu' ", + }, + scenario: []string{ + "lut", + }, + scenarioOutline: []string{ + "lut chovnatlh", + }, + then: []string{ + "* ", + "vaj ", + }, + when: []string{ + "* ", + "qaSDI' ", + }, + }, + }, + "tr": &GherkinDialect{ + "tr", "Turkish", "Türkçe", map[string][]string{ + and: []string{ + "* ", + "Ve ", + }, + background: []string{ + "Geçmiş", + }, + but: []string{ + "* ", + "Fakat ", + "Ama ", + }, + examples: []string{ + "Örnekler", + }, + feature: []string{ + "Özellik", + }, + given: []string{ + "* ", + "Diyelim ki ", + }, + scenario: []string{ + "Senaryo", + }, + scenarioOutline: []string{ + "Senaryo taslağı", + }, + then: []string{ + "* ", + "O zaman ", + }, + when: []string{ + "* ", + "Eğer ki ", + }, + }, + }, + "tt": &GherkinDialect{ + "tt", "Tatar", "Татарча", map[string][]string{ + and: []string{ + "* ", + "Һәм ", + "Вә ", + }, + background: []string{ + "Кереш", + }, + but: []string{ + "* ", + "Ләкин ", + "Әмма ", + }, + examples: []string{ + "Үрнәкләр", + "Мисаллар", + }, + feature: []string{ + "Мөмкинлек", + "Үзенчәлеклелек", + }, + given: []string{ + "* ", + "Әйтик ", + }, + scenario: []string{ + "Сценарий", + }, + scenarioOutline: []string{ + "Сценарийның төзелеше", + }, + then: []string{ + "* ", + "Нәтиҗәдә ", + }, + when: []string{ + "* ", + "Әгәр ", + }, + }, + }, + "uk": &GherkinDialect{ + "uk", "Ukrainian", "Українська", map[string][]string{ + and: []string{ + "* ", + "І ", + "А також ", + "Та ", + }, + background: []string{ + "Передумова", + }, + but: []string{ + "* ", + "Але ", + }, + examples: []string{ + "Приклади", + }, + feature: []string{ + "Функціонал", + }, + given: []string{ + "* ", + "Припустимо ", + "Припустимо, що ", + "Нехай ", + "Дано ", + }, + scenario: []string{ + "Сценарій", + }, + scenarioOutline: []string{ + "Структура сценарію", + }, + then: []string{ + "* ", + "То ", + "Тоді ", + }, + when: []string{ + "* ", + "Якщо ", + "Коли ", + }, + }, + }, + "ur": &GherkinDialect{ + "ur", "Urdu", "اردو", map[string][]string{ + and: []string{ + "* ", + "اور ", + }, + background: []string{ + "پس منظر", + }, + but: []string{ + "* ", + "لیکن ", + }, + examples: []string{ + "مثالیں", + }, + feature: []string{ + "صلاحیت", + "کاروبار کی ضرورت", + "خصوصیت", + }, + given: []string{ + "* ", + "اگر ", + "بالفرض ", + "فرض کیا ", + }, + scenario: []string{ + "منظرنامہ", + }, + scenarioOutline: []string{ + "منظر نامے کا خاکہ", + }, + then: []string{ + "* ", + "پھر ", + "تب ", + }, + when: []string{ + "* ", + "جب ", + }, + }, + }, + "uz": &GherkinDialect{ + "uz", "Uzbek", "Узбекча", map[string][]string{ + and: []string{ + "* ", + "Ва ", + }, + background: []string{ + "Тарих", + }, + but: []string{ + "* ", + "Лекин ", + "Бирок ", + "Аммо ", + }, + examples: []string{ + "Мисоллар", + }, + feature: []string{ + "Функционал", + }, + given: []string{ + "* ", + "Агар ", + }, + scenario: []string{ + "Сценарий", + }, + scenarioOutline: []string{ + "Сценарий структураси", + }, + then: []string{ + "* ", + "Унда ", + }, + when: []string{ + "* ", + "Агар ", + }, + }, + }, + "vi": &GherkinDialect{ + "vi", "Vietnamese", "Tiếng Việt", map[string][]string{ + and: []string{ + "* ", + "Và ", + }, + background: []string{ + "Bối cảnh", + }, + but: []string{ + "* ", + "Nhưng ", + }, + examples: []string{ + "Dữ liệu", + }, + feature: []string{ + "Tính năng", + }, + given: []string{ + "* ", + "Biết ", + "Cho ", + }, + scenario: []string{ + "Tình huống", + "Kịch bản", + }, + scenarioOutline: []string{ + "Khung tình huống", + "Khung kịch bản", + }, + then: []string{ + "* ", + "Thì ", + }, + when: []string{ + "* ", + "Khi ", + }, + }, + }, + "zh-CN": &GherkinDialect{ + "zh-CN", "Chinese simplified", "简体中文", map[string][]string{ + and: []string{ + "* ", + "而且", + "并且", + "同时", + }, + background: []string{ + "背景", + }, + but: []string{ + "* ", + "但是", + }, + examples: []string{ + "例子", + }, + feature: []string{ + "功能", + }, + given: []string{ + "* ", + "假如", + "假设", + "假定", + }, + scenario: []string{ + "场景", + "剧本", + }, + scenarioOutline: []string{ + "场景大纲", + "剧本大纲", + }, + then: []string{ + "* ", + "那么", + }, + when: []string{ + "* ", + "当", + }, + }, + }, + "zh-TW": &GherkinDialect{ + "zh-TW", "Chinese traditional", "繁體中文", map[string][]string{ + and: []string{ + "* ", + "而且", + "並且", + "同時", + }, + background: []string{ + "背景", + }, + but: []string{ + "* ", + "但是", + }, + examples: []string{ + "例子", + }, + feature: []string{ + "功能", + }, + given: []string{ + "* ", + "假如", + "假設", + "假定", + }, + scenario: []string{ + "場景", + "劇本", + }, + scenarioOutline: []string{ + "場景大綱", + "劇本大綱", + }, + then: []string{ + "* ", + "那麼", + }, + when: []string{ + "* ", + "當", + }, + }, + }, +} diff --git a/gherkin/gherkin.go b/gherkin/gherkin.go new file mode 100644 index 0000000..0dc469c --- /dev/null +++ b/gherkin/gherkin.go @@ -0,0 +1,137 @@ +package gherkin + +import ( + "bufio" + "fmt" + "io" + "strings" +) + +type Parser interface { + StopAtFirstError(b bool) + Parse(s Scanner, m Matcher) (err error) +} + +/* +The scanner reads a gherkin doc (typically read from a .feature file) and creates a token for +each line. The tokens are passed to the parser, which outputs an AST (Abstract Syntax Tree). + +If the scanner sees a # language header, it will reconfigure itself dynamically to look for +Gherkin keywords for the associated language. The keywords are defined in gherkin-languages.json. +*/ +type Scanner interface { + Scan() (line *Line, atEof bool, err error) +} + +type Builder interface { + Build(*Token) (bool, error) + StartRule(RuleType) (bool, error) + EndRule(RuleType) (bool, error) + Reset() +} + +type Token struct { + Type TokenType + Keyword string + Text string + Items []*LineSpan + GherkinDialect string + Indent string + Location *Location +} + +func (t *Token) IsEOF() bool { + return t.Type == TokenType_EOF +} +func (t *Token) String() string { + return fmt.Sprintf("%s: %s/%s", t.Type.Name(), t.Keyword, t.Text) +} + +type LineSpan struct { + Column int + Text string +} + +func (l *LineSpan) String() string { + return fmt.Sprintf("%d:%s", l.Column, l.Text) +} + +type parser struct { + builder Builder + stopAtFirstError bool +} + +func NewParser(b Builder) Parser { + return &parser{ + builder: b, + } +} + +func (p *parser) StopAtFirstError(b bool) { + p.stopAtFirstError = b +} + +func NewScanner(r io.Reader) Scanner { + return &scanner{ + s: bufio.NewScanner(r), + line: 0, + } +} + +type scanner struct { + s *bufio.Scanner + line int +} + +func (t *scanner) Scan() (line *Line, atEof bool, err error) { + scanning := t.s.Scan() + if !scanning { + err = t.s.Err() + if err == nil { + atEof = true + } + } + if err == nil { + t.line += 1 + str := t.s.Text() + line = &Line{str, t.line, strings.TrimLeft(str, " \t"), atEof} + } + return +} + +type Line struct { + LineText string + LineNumber int + TrimmedLineText string + AtEof bool +} + +func (g *Line) Indent() int { + return len(g.LineText) - len(g.TrimmedLineText) +} + +func (g *Line) IsEmpty() bool { + return len(g.TrimmedLineText) == 0 +} + +func (g *Line) IsEof() bool { + return g.AtEof +} + +func (g *Line) StartsWith(prefix string) bool { + return strings.HasPrefix(g.TrimmedLineText, prefix) +} + +func ParseFeature(in io.Reader) (feature *Feature, err error) { + + builder := NewAstBuilder() + parser := NewParser(builder) + parser.StopAtFirstError(false) + matcher := NewMatcher(GherkinDialectsBuildin()) + + scanner := NewScanner(in) + + err = parser.Parse(scanner, matcher) + + return builder.GetFeature(), err +} diff --git a/gherkin/matcher.go b/gherkin/matcher.go new file mode 100644 index 0000000..d8c7efc --- /dev/null +++ b/gherkin/matcher.go @@ -0,0 +1,270 @@ +package gherkin + +import ( + "regexp" + "strings" + "unicode/utf8" +) + +const ( + DEFAULT_DIALECT = "en" + COMMENT_PREFIX = "#" + TAG_PREFIX = "@" + TITLE_KEYWORD_SEPARATOR = ":" + TABLE_CELL_SEPARATOR = '|' + ESCAPE_CHAR = '\\' + ESCAPED_NEWLINE = 'n' + DOCSTRING_SEPARATOR = "\"\"\"" + DOCSTRING_ALTERNATIVE_SEPARATOR = "```" +) + +type matcher struct { + gdp GherkinDialectProvider + default_lang string + lang string + dialect *GherkinDialect + activeDocStringSeparator string + indentToRemove int + languagePattern *regexp.Regexp +} + +func NewMatcher(gdp GherkinDialectProvider) Matcher { + return &matcher{ + gdp: gdp, + default_lang: DEFAULT_DIALECT, + lang: DEFAULT_DIALECT, + dialect: gdp.GetDialect(DEFAULT_DIALECT), + languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"), + } +} + +func NewLanguageMatcher(gdp GherkinDialectProvider, language string) Matcher { + return &matcher{ + gdp: gdp, + default_lang: language, + lang: language, + dialect: gdp.GetDialect(language), + languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"), + } +} + +func (m *matcher) Reset() { + m.indentToRemove = 0 + m.activeDocStringSeparator = "" + if m.lang != "en" { + m.dialect = m.gdp.GetDialect(m.default_lang) + m.lang = "en" + } +} + +func (m *matcher) newTokenAtLocation(line, index int) (token *Token) { + column := index + 1 + token = new(Token) + token.GherkinDialect = m.lang + token.Location = &Location{line, column} + return +} + +func (m *matcher) MatchEOF(line *Line) (ok bool, token *Token, err error) { + if line.IsEof() { + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_EOF + } + return +} + +func (m *matcher) MatchEmpty(line *Line) (ok bool, token *Token, err error) { + if line.IsEmpty() { + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_Empty + } + return +} + +func (m *matcher) MatchComment(line *Line) (ok bool, token *Token, err error) { + if line.StartsWith(COMMENT_PREFIX) { + token, ok = m.newTokenAtLocation(line.LineNumber, 0), true + token.Type = TokenType_Comment + token.Text = line.LineText + } + return +} + +func (m *matcher) MatchTagLine(line *Line) (ok bool, token *Token, err error) { + if line.StartsWith(TAG_PREFIX) { + var tags []*LineSpan + var column = line.Indent() + splits := strings.Split(line.TrimmedLineText, TAG_PREFIX) + for i := range splits { + txt := strings.Trim(splits[i], " ") + if txt != "" { + tags = append(tags, &LineSpan{column, TAG_PREFIX + txt}) + } + column = column + len(splits[i]) + 1 + } + + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_TagLine + token.Items = tags + } + return +} + +func (m *matcher) matchTitleLine(line *Line, tokenType TokenType, keywords []string) (ok bool, token *Token, err error) { + for i := range keywords { + keyword := keywords[i] + if line.StartsWith(keyword + TITLE_KEYWORD_SEPARATOR) { + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = tokenType + token.Keyword = keyword + token.Text = strings.Trim(line.TrimmedLineText[len(keyword)+1:], " ") + return + } + } + return +} + +func (m *matcher) MatchFeatureLine(line *Line) (ok bool, token *Token, err error) { + return m.matchTitleLine(line, TokenType_FeatureLine, m.dialect.FeatureKeywords()) +} +func (m *matcher) MatchBackgroundLine(line *Line) (ok bool, token *Token, err error) { + return m.matchTitleLine(line, TokenType_BackgroundLine, m.dialect.BackgroundKeywords()) +} +func (m *matcher) MatchScenarioLine(line *Line) (ok bool, token *Token, err error) { + return m.matchTitleLine(line, TokenType_ScenarioLine, m.dialect.ScenarioKeywords()) +} +func (m *matcher) MatchScenarioOutlineLine(line *Line) (ok bool, token *Token, err error) { + return m.matchTitleLine(line, TokenType_ScenarioOutlineLine, m.dialect.ScenarioOutlineKeywords()) +} +func (m *matcher) MatchExamplesLine(line *Line) (ok bool, token *Token, err error) { + return m.matchTitleLine(line, TokenType_ExamplesLine, m.dialect.ExamplesKeywords()) +} +func (m *matcher) MatchStepLine(line *Line) (ok bool, token *Token, err error) { + keywords := m.dialect.StepKeywords() + for i := range keywords { + keyword := keywords[i] + if line.StartsWith(keyword) { + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_StepLine + token.Keyword = keyword + token.Text = strings.Trim(line.TrimmedLineText[len(keyword):], " ") + return + } + } + return +} + +func (m *matcher) MatchDocStringSeparator(line *Line) (ok bool, token *Token, err error) { + if m.activeDocStringSeparator != "" { + if line.StartsWith(m.activeDocStringSeparator) { + // close + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_DocStringSeparator + + m.indentToRemove = 0 + m.activeDocStringSeparator = "" + } + return + } + if line.StartsWith(DOCSTRING_SEPARATOR) { + m.activeDocStringSeparator = DOCSTRING_SEPARATOR + } else if line.StartsWith(DOCSTRING_ALTERNATIVE_SEPARATOR) { + m.activeDocStringSeparator = DOCSTRING_ALTERNATIVE_SEPARATOR + } + if m.activeDocStringSeparator != "" { + // open + contentType := line.TrimmedLineText[len(m.activeDocStringSeparator):] + m.indentToRemove = line.Indent() + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_DocStringSeparator + token.Text = contentType + } + return +} + +func (m *matcher) MatchTableRow(line *Line) (ok bool, token *Token, err error) { + var firstChar, firstPos = utf8.DecodeRuneInString(line.TrimmedLineText) + if firstChar == TABLE_CELL_SEPARATOR { + var cells []*LineSpan + var cell []rune + var startCol = line.Indent() + 2 // column where the current cell started + // start after the first separator, it's not included in the cell + for i, w, col := firstPos, 0, startCol; i < len(line.TrimmedLineText); i += w { + var char rune + char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:]) + if char == TABLE_CELL_SEPARATOR { + // append current cell + txt := string(cell) + txtTrimmed := strings.TrimLeft(txt, " ") + ind := len(txt) - len(txtTrimmed) + cells = append(cells, &LineSpan{startCol + ind, strings.TrimRight(txtTrimmed, " ")}) + // start building next + cell = make([]rune, 0) + startCol = col + 1 + } else if char == ESCAPE_CHAR { + // skip this character but count the column + i += w + col++ + char, w = utf8.DecodeRuneInString(line.TrimmedLineText[i:]) + if char == ESCAPED_NEWLINE { + cell = append(cell, '\n') + } else { + if char != TABLE_CELL_SEPARATOR && char != ESCAPE_CHAR { + cell = append(cell, ESCAPE_CHAR) + } + cell = append(cell, char) + } + } else { + cell = append(cell, char) + } + col++ + } + + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_TableRow + token.Items = cells + } + return +} + +func (m *matcher) MatchLanguage(line *Line) (ok bool, token *Token, err error) { + matches := m.languagePattern.FindStringSubmatch(line.TrimmedLineText) + if len(matches) > 0 { + lang := matches[1] + token, ok = m.newTokenAtLocation(line.LineNumber, line.Indent()), true + token.Type = TokenType_Language + token.Text = lang + + dialect := m.gdp.GetDialect(lang) + if dialect == nil { + err = &parseError{"Language not supported: " + lang, token.Location} + } else { + m.lang = lang + m.dialect = dialect + } + } + return +} + +func (m *matcher) MatchOther(line *Line) (ok bool, token *Token, err error) { + token, ok = m.newTokenAtLocation(line.LineNumber, 0), true + token.Type = TokenType_Other + + element := line.LineText + txt := strings.TrimLeft(element, " ") + + if len(element)-len(txt) > m.indentToRemove { + token.Text = m.unescapeDocString(element[m.indentToRemove:]) + } else { + token.Text = m.unescapeDocString(txt) + } + return +} + +func (m *matcher) unescapeDocString(text string) string { + if m.activeDocStringSeparator != "" { + return strings.Replace(text, "\\\"\\\"\\\"", "\"\"\"", -1) + } else { + return text + } +} diff --git a/gherkin/parser.go b/gherkin/parser.go new file mode 100644 index 0000000..0e26200 --- /dev/null +++ b/gherkin/parser.go @@ -0,0 +1,2270 @@ +// +// This file is generated. Do not edit! Edit gherkin-golang.razor instead. + +// +package gherkin + +import ( + "fmt" + "strings" +) + +type TokenType int + +const ( + TokenType_None TokenType = iota + TokenType_EOF + TokenType_Empty + TokenType_Comment + TokenType_TagLine + TokenType_FeatureLine + TokenType_BackgroundLine + TokenType_ScenarioLine + TokenType_ScenarioOutlineLine + TokenType_ExamplesLine + TokenType_StepLine + TokenType_DocStringSeparator + TokenType_TableRow + TokenType_Language + TokenType_Other +) + +func tokenTypeForRule(rt RuleType) TokenType { + return TokenType_None +} + +func (t TokenType) Name() string { + switch t { + case TokenType_EOF: + return "EOF" + case TokenType_Empty: + return "Empty" + case TokenType_Comment: + return "Comment" + case TokenType_TagLine: + return "TagLine" + case TokenType_FeatureLine: + return "FeatureLine" + case TokenType_BackgroundLine: + return "BackgroundLine" + case TokenType_ScenarioLine: + return "ScenarioLine" + case TokenType_ScenarioOutlineLine: + return "ScenarioOutlineLine" + case TokenType_ExamplesLine: + return "ExamplesLine" + case TokenType_StepLine: + return "StepLine" + case TokenType_DocStringSeparator: + return "DocStringSeparator" + case TokenType_TableRow: + return "TableRow" + case TokenType_Language: + return "Language" + case TokenType_Other: + return "Other" + } + return "" +} + +func (t TokenType) RuleType() RuleType { + switch t { + case TokenType_EOF: + return RuleType__EOF + case TokenType_Empty: + return RuleType__Empty + case TokenType_Comment: + return RuleType__Comment + case TokenType_TagLine: + return RuleType__TagLine + case TokenType_FeatureLine: + return RuleType__FeatureLine + case TokenType_BackgroundLine: + return RuleType__BackgroundLine + case TokenType_ScenarioLine: + return RuleType__ScenarioLine + case TokenType_ScenarioOutlineLine: + return RuleType__ScenarioOutlineLine + case TokenType_ExamplesLine: + return RuleType__ExamplesLine + case TokenType_StepLine: + return RuleType__StepLine + case TokenType_DocStringSeparator: + return RuleType__DocStringSeparator + case TokenType_TableRow: + return RuleType__TableRow + case TokenType_Language: + return RuleType__Language + case TokenType_Other: + return RuleType__Other + } + return RuleType_None +} + +type RuleType int + +const ( + RuleType_None RuleType = iota + + RuleType__EOF + RuleType__Empty + RuleType__Comment + RuleType__TagLine + RuleType__FeatureLine + RuleType__BackgroundLine + RuleType__ScenarioLine + RuleType__ScenarioOutlineLine + RuleType__ExamplesLine + RuleType__StepLine + RuleType__DocStringSeparator + RuleType__TableRow + RuleType__Language + RuleType__Other + RuleType_Feature + RuleType_Feature_Header + RuleType_Background + RuleType_Scenario_Definition + RuleType_Scenario + RuleType_ScenarioOutline + RuleType_Examples_Definition + RuleType_Examples + RuleType_Scenario_Step + RuleType_ScenarioOutline_Step + RuleType_Step + RuleType_Step_Arg + RuleType_DataTable + RuleType_DocString + RuleType_Tags + RuleType_Feature_Description + RuleType_Background_Description + RuleType_Scenario_Description + RuleType_ScenarioOutline_Description + RuleType_Examples_Description + RuleType_Description_Helper + RuleType_Description +) + +func (t RuleType) IsEOF() bool { + return t == RuleType__EOF +} +func (t RuleType) Name() string { + switch t { + case RuleType__EOF: + return "#EOF" + case RuleType__Empty: + return "#Empty" + case RuleType__Comment: + return "#Comment" + case RuleType__TagLine: + return "#TagLine" + case RuleType__FeatureLine: + return "#FeatureLine" + case RuleType__BackgroundLine: + return "#BackgroundLine" + case RuleType__ScenarioLine: + return "#ScenarioLine" + case RuleType__ScenarioOutlineLine: + return "#ScenarioOutlineLine" + case RuleType__ExamplesLine: + return "#ExamplesLine" + case RuleType__StepLine: + return "#StepLine" + case RuleType__DocStringSeparator: + return "#DocStringSeparator" + case RuleType__TableRow: + return "#TableRow" + case RuleType__Language: + return "#Language" + case RuleType__Other: + return "#Other" + case RuleType_Feature: + return "Feature" + case RuleType_Feature_Header: + return "Feature_Header" + case RuleType_Background: + return "Background" + case RuleType_Scenario_Definition: + return "Scenario_Definition" + case RuleType_Scenario: + return "Scenario" + case RuleType_ScenarioOutline: + return "ScenarioOutline" + case RuleType_Examples_Definition: + return "Examples_Definition" + case RuleType_Examples: + return "Examples" + case RuleType_Scenario_Step: + return "Scenario_Step" + case RuleType_ScenarioOutline_Step: + return "ScenarioOutline_Step" + case RuleType_Step: + return "Step" + case RuleType_Step_Arg: + return "Step_Arg" + case RuleType_DataTable: + return "DataTable" + case RuleType_DocString: + return "DocString" + case RuleType_Tags: + return "Tags" + case RuleType_Feature_Description: + return "Feature_Description" + case RuleType_Background_Description: + return "Background_Description" + case RuleType_Scenario_Description: + return "Scenario_Description" + case RuleType_ScenarioOutline_Description: + return "ScenarioOutline_Description" + case RuleType_Examples_Description: + return "Examples_Description" + case RuleType_Description_Helper: + return "Description_Helper" + case RuleType_Description: + return "Description" + } + return "" +} + +type parseError struct { + msg string + loc *Location +} + +func (a *parseError) Error() string { + return fmt.Sprintf("(%d:%d): %s", a.loc.Line, a.loc.Column, a.msg) +} + +type parseErrors []error + +func (pe parseErrors) Error() string { + var ret = []string{"Parser errors:"} + for i := range pe { + ret = append(ret, pe[i].Error()) + } + return strings.Join(ret, "\n") +} + +func (p *parser) Parse(s Scanner, m Matcher) (err error) { + p.builder.Reset() + m.Reset() + ctxt := &parseContext{p, s, p.builder, m, nil, nil} + var state int + ctxt.startRule(RuleType_Feature) + for { + gl, eof, err := ctxt.scan() + if err != nil { + ctxt.addError(err) + if p.stopAtFirstError { + break + } + } + state, err = ctxt.match(state, gl) + if err != nil { + ctxt.addError(err) + if p.stopAtFirstError { + break + } + } + if eof { + // done! \o/ + break + } + } + ctxt.endRule(RuleType_Feature) + if len(ctxt.errors) > 0 { + return ctxt.errors + } + return +} + +type parseContext struct { + p *parser + s Scanner + b Builder + m Matcher + queue []*scanResult + errors parseErrors +} + +func (ctxt *parseContext) addError(e error) { + ctxt.errors = append(ctxt.errors, e) + // if (p.errors.length > 10) + // throw Errors.CompositeParserException.create(p.errors); +} + +type scanResult struct { + line *Line + atEof bool + err error +} + +func (ctxt *parseContext) scan() (*Line, bool, error) { + l := len(ctxt.queue) + if l > 0 { + x := ctxt.queue[0] + ctxt.queue = ctxt.queue[1:] + return x.line, x.atEof, x.err + } + return ctxt.s.Scan() +} + +func (ctxt *parseContext) startRule(r RuleType) (bool, error) { + ok, err := ctxt.b.StartRule(r) + if err != nil { + ctxt.addError(err) + } + return ok, err +} + +func (ctxt *parseContext) endRule(r RuleType) (bool, error) { + ok, err := ctxt.b.EndRule(r) + if err != nil { + ctxt.addError(err) + } + return ok, err +} + +func (ctxt *parseContext) build(t *Token) (bool, error) { + ok, err := ctxt.b.Build(t) + if err != nil { + ctxt.addError(err) + } + return ok, err +} + +func (ctxt *parseContext) match(state int, line *Line) (newState int, err error) { + switch state { + case 0: + return ctxt.matchAt_0(line) + case 1: + return ctxt.matchAt_1(line) + case 2: + return ctxt.matchAt_2(line) + case 3: + return ctxt.matchAt_3(line) + case 4: + return ctxt.matchAt_4(line) + case 5: + return ctxt.matchAt_5(line) + case 6: + return ctxt.matchAt_6(line) + case 7: + return ctxt.matchAt_7(line) + case 8: + return ctxt.matchAt_8(line) + case 9: + return ctxt.matchAt_9(line) + case 10: + return ctxt.matchAt_10(line) + case 11: + return ctxt.matchAt_11(line) + case 12: + return ctxt.matchAt_12(line) + case 13: + return ctxt.matchAt_13(line) + case 14: + return ctxt.matchAt_14(line) + case 15: + return ctxt.matchAt_15(line) + case 16: + return ctxt.matchAt_16(line) + case 17: + return ctxt.matchAt_17(line) + case 18: + return ctxt.matchAt_18(line) + case 19: + return ctxt.matchAt_19(line) + case 20: + return ctxt.matchAt_20(line) + case 21: + return ctxt.matchAt_21(line) + case 22: + return ctxt.matchAt_22(line) + case 23: + return ctxt.matchAt_23(line) + case 24: + return ctxt.matchAt_24(line) + case 25: + return ctxt.matchAt_25(line) + case 26: + return ctxt.matchAt_26(line) + case 27: + return ctxt.matchAt_27(line) + case 29: + return ctxt.matchAt_29(line) + case 30: + return ctxt.matchAt_30(line) + case 31: + return ctxt.matchAt_31(line) + case 32: + return ctxt.matchAt_32(line) + case 33: + return ctxt.matchAt_33(line) + case 34: + return ctxt.matchAt_34(line) + default: + return state, fmt.Errorf("Unknown state: %+v", state) + } +} + +// Start +func (ctxt *parseContext) matchAt_0(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Language(line); ok { + ctxt.startRule(RuleType_Feature_Header) + ctxt.build(token) + return 1, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.startRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 2, err + } + if ok, token, err := ctxt.match_FeatureLine(line); ok { + ctxt.startRule(RuleType_Feature_Header) + ctxt.build(token) + return 3, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 0, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 0, err + } + + // var stateComment = "State: 0 - Start" + var expectedTokens = []string{"#Language", "#TagLine", "#FeatureLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 0, err +} + +// Feature:0>Feature_Header:0>#Language:0 +func (ctxt *parseContext) matchAt_1(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 2, err + } + if ok, token, err := ctxt.match_FeatureLine(line); ok { + ctxt.build(token) + return 3, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 1, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 1, err + } + + // var stateComment = "State: 1 - Feature:0>Feature_Header:0>#Language:0" + var expectedTokens = []string{"#TagLine", "#FeatureLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 1, err +} + +// Feature:0>Feature_Header:1>Tags:0>#TagLine:0 +func (ctxt *parseContext) matchAt_2(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.build(token) + return 2, err + } + if ok, token, err := ctxt.match_FeatureLine(line); ok { + ctxt.endRule(RuleType_Tags) + ctxt.build(token) + return 3, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 2, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 2, err + } + + // var stateComment = "State: 2 - Feature:0>Feature_Header:1>Tags:0>#TagLine:0" + var expectedTokens = []string{"#TagLine", "#FeatureLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 2, err +} + +// Feature:0>Feature_Header:2>#FeatureLine:0 +func (ctxt *parseContext) matchAt_3(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 3, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 5, err + } + if ok, token, err := ctxt.match_BackgroundLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Background) + ctxt.build(token) + return 6, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.startRule(RuleType_Description) + ctxt.build(token) + return 4, err + } + + // var stateComment = "State: 3 - Feature:0>Feature_Header:2>#FeatureLine:0" + var expectedTokens = []string{"#EOF", "#Empty", "#Comment", "#BackgroundLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 3, err +} + +// Feature:0>Feature_Header:3>Feature_Description:0>Description_Helper:1>Description:0>#Other:0 +func (ctxt *parseContext) matchAt_4(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Feature_Header) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 5, err + } + if ok, token, err := ctxt.match_BackgroundLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Background) + ctxt.build(token) + return 6, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 4, err + } + + // var stateComment = "State: 4 - Feature:0>Feature_Header:3>Feature_Description:0>Description_Helper:1>Description:0>#Other:0" + var expectedTokens = []string{"#EOF", "#Comment", "#BackgroundLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 4, err +} + +// Feature:0>Feature_Header:3>Feature_Description:0>Description_Helper:2>#Comment:0 +func (ctxt *parseContext) matchAt_5(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 5, err + } + if ok, token, err := ctxt.match_BackgroundLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Background) + ctxt.build(token) + return 6, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Feature_Header) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 5, err + } + + // var stateComment = "State: 5 - Feature:0>Feature_Header:3>Feature_Description:0>Description_Helper:2>#Comment:0" + var expectedTokens = []string{"#EOF", "#Comment", "#BackgroundLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 5, err +} + +// Feature:1>Background:0>#BackgroundLine:0 +func (ctxt *parseContext) matchAt_6(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 6, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 8, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.startRule(RuleType_Description) + ctxt.build(token) + return 7, err + } + + // var stateComment = "State: 6 - Feature:1>Background:0>#BackgroundLine:0" + var expectedTokens = []string{"#EOF", "#Empty", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 6, err +} + +// Feature:1>Background:1>Background_Description:0>Description_Helper:1>Description:0>#Other:0 +func (ctxt *parseContext) matchAt_7(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 8, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 7, err + } + + // var stateComment = "State: 7 - Feature:1>Background:1>Background_Description:0>Description_Helper:1>Description:0>#Other:0" + var expectedTokens = []string{"#EOF", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 7, err +} + +// Feature:1>Background:1>Background_Description:0>Description_Helper:2>#Comment:0 +func (ctxt *parseContext) matchAt_8(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 8, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 8, err + } + + // var stateComment = "State: 8 - Feature:1>Background:1>Background_Description:0>Description_Helper:2>#Comment:0" + var expectedTokens = []string{"#EOF", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 8, err +} + +// Feature:1>Background:2>Scenario_Step:0>Step:0>#StepLine:0 +func (ctxt *parseContext) matchAt_9(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.startRule(RuleType_DataTable) + ctxt.build(token) + return 10, err + } + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.startRule(RuleType_DocString) + ctxt.build(token) + return 33, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 9, err + } + + // var stateComment = "State: 9 - Feature:1>Background:2>Scenario_Step:0>Step:0>#StepLine:0" + var expectedTokens = []string{"#EOF", "#TableRow", "#DocStringSeparator", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 9, err +} + +// Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0 +func (ctxt *parseContext) matchAt_10(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 10, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 10, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 10, err + } + + // var stateComment = "State: 10 - Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0" + var expectedTokens = []string{"#EOF", "#TableRow", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 10, err +} + +// Feature:2>Scenario_Definition:0>Tags:0>#TagLine:0 +func (ctxt *parseContext) matchAt_11(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Tags) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Tags) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 11, err + } + + // var stateComment = "State: 11 - Feature:2>Scenario_Definition:0>Tags:0>#TagLine:0" + var expectedTokens = []string{"#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 11, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:0>#ScenarioLine:0 +func (ctxt *parseContext) matchAt_12(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 14, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.startRule(RuleType_Description) + ctxt.build(token) + return 13, err + } + + // var stateComment = "State: 12 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:0>#ScenarioLine:0" + var expectedTokens = []string{"#EOF", "#Empty", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 12, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:1>Scenario_Description:0>Description_Helper:1>Description:0>#Other:0 +func (ctxt *parseContext) matchAt_13(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 14, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 13, err + } + + // var stateComment = "State: 13 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:1>Scenario_Description:0>Description_Helper:1>Description:0>#Other:0" + var expectedTokens = []string{"#EOF", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 13, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:1>Scenario_Description:0>Description_Helper:2>#Comment:0 +func (ctxt *parseContext) matchAt_14(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 14, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 14, err + } + + // var stateComment = "State: 14 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:1>Scenario_Description:0>Description_Helper:2>#Comment:0" + var expectedTokens = []string{"#EOF", "#Comment", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 14, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:0>#StepLine:0 +func (ctxt *parseContext) matchAt_15(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.startRule(RuleType_DataTable) + ctxt.build(token) + return 16, err + } + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.startRule(RuleType_DocString) + ctxt.build(token) + return 31, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 15, err + } + + // var stateComment = "State: 15 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:0>#StepLine:0" + var expectedTokens = []string{"#EOF", "#TableRow", "#DocStringSeparator", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 15, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0 +func (ctxt *parseContext) matchAt_16(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 16, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 16, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 16, err + } + + // var stateComment = "State: 16 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0" + var expectedTokens = []string{"#EOF", "#TableRow", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 16, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:0>#ScenarioOutlineLine:0 +func (ctxt *parseContext) matchAt_17(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 19, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.startRule(RuleType_Description) + ctxt.build(token) + return 18, err + } + + // var stateComment = "State: 17 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:0>#ScenarioOutlineLine:0" + var expectedTokens = []string{"#Empty", "#Comment", "#StepLine", "#TagLine", "#ExamplesLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 17, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:1>ScenarioOutline_Description:0>Description_Helper:1>Description:0>#Other:0 +func (ctxt *parseContext) matchAt_18(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 19, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 18, err + } + + // var stateComment = "State: 18 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:1>ScenarioOutline_Description:0>Description_Helper:1>Description:0>#Other:0" + var expectedTokens = []string{"#Comment", "#StepLine", "#TagLine", "#ExamplesLine", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 18, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:1>ScenarioOutline_Description:0>Description_Helper:2>#Comment:0 +func (ctxt *parseContext) matchAt_19(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 19, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 19, err + } + + // var stateComment = "State: 19 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:1>ScenarioOutline_Description:0>Description_Helper:2>#Comment:0" + var expectedTokens = []string{"#Comment", "#StepLine", "#TagLine", "#ExamplesLine", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 19, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:0>#StepLine:0 +func (ctxt *parseContext) matchAt_20(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.startRule(RuleType_DataTable) + ctxt.build(token) + return 21, err + } + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.startRule(RuleType_DocString) + ctxt.build(token) + return 29, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 20, err + } + + // var stateComment = "State: 20 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:0>#StepLine:0" + var expectedTokens = []string{"#TableRow", "#DocStringSeparator", "#StepLine", "#TagLine", "#ExamplesLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 20, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0 +func (ctxt *parseContext) matchAt_21(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 21, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_DataTable) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 21, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 21, err + } + + // var stateComment = "State: 21 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:0>DataTable:0>#TableRow:0" + var expectedTokens = []string{"#TableRow", "#StepLine", "#TagLine", "#ExamplesLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 21, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:0>Tags:0>#TagLine:0 +func (ctxt *parseContext) matchAt_22(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_Tags) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 22, err + } + + // var stateComment = "State: 22 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:0>Tags:0>#TagLine:0" + var expectedTokens = []string{"#TagLine", "#ExamplesLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 22, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:0>#ExamplesLine:0 +func (ctxt *parseContext) matchAt_23(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 25, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 26, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.startRule(RuleType_Description) + ctxt.build(token) + return 24, err + } + + // var stateComment = "State: 23 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:0>#ExamplesLine:0" + var expectedTokens = []string{"#Empty", "#Comment", "#TableRow", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 23, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:1>Examples_Description:0>Description_Helper:1>Description:0>#Other:0 +func (ctxt *parseContext) matchAt_24(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 25, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.endRule(RuleType_Description) + ctxt.build(token) + return 26, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 24, err + } + + // var stateComment = "State: 24 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:1>Examples_Description:0>Description_Helper:1>Description:0>#Other:0" + var expectedTokens = []string{"#Comment", "#TableRow", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 24, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:1>Examples_Description:0>Description_Helper:2>#Comment:0 +func (ctxt *parseContext) matchAt_25(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 25, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 26, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 25, err + } + + // var stateComment = "State: 25 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:1>Examples_Description:0>Description_Helper:2>#Comment:0" + var expectedTokens = []string{"#Comment", "#TableRow", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 25, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:2>#TableRow:0 +func (ctxt *parseContext) matchAt_26(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 27, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 26, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 26, err + } + + // var stateComment = "State: 26 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:2>#TableRow:0" + var expectedTokens = []string{"#TableRow", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 26, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:3>#TableRow:0 +func (ctxt *parseContext) matchAt_27(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.endRule(RuleType_ScenarioOutline) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_TableRow(line); ok { + ctxt.build(token) + return 27, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + if ctxt.lookahead_0(line) { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.endRule(RuleType_ScenarioOutline) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.endRule(RuleType_ScenarioOutline) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_Examples) + ctxt.endRule(RuleType_Examples_Definition) + ctxt.endRule(RuleType_ScenarioOutline) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 27, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 27, err + } + + // var stateComment = "State: 27 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:3>Examples_Definition:1>Examples:3>#TableRow:0" + var expectedTokens = []string{"#EOF", "#TableRow", "#TagLine", "#ExamplesLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 27, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_29(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.build(token) + return 30, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 29, err + } + + // var stateComment = "State: 29 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0" + var expectedTokens = []string{"#DocStringSeparator", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 29, err +} + +// Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_30(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 20, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 22, err + } + if ok, token, err := ctxt.match_ExamplesLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Examples_Definition) + ctxt.startRule(RuleType_Examples) + ctxt.build(token) + return 23, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 30, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 30, err + } + + // var stateComment = "State: 30 - Feature:2>Scenario_Definition:1>__alt0:1>ScenarioOutline:2>ScenarioOutline_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0" + var expectedTokens = []string{"#StepLine", "#TagLine", "#ExamplesLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 30, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_31(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.build(token) + return 32, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 31, err + } + + // var stateComment = "State: 31 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0" + var expectedTokens = []string{"#DocStringSeparator", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 31, err +} + +// Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_32(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 15, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Scenario) + ctxt.endRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 32, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 32, err + } + + // var stateComment = "State: 32 - Feature:2>Scenario_Definition:1>__alt0:0>Scenario:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0" + var expectedTokens = []string{"#EOF", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 32, err +} + +// Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_33(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_DocStringSeparator(line); ok { + ctxt.build(token) + return 34, err + } + if ok, token, err := ctxt.match_Other(line); ok { + ctxt.build(token) + return 33, err + } + + // var stateComment = "State: 33 - Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:0>#DocStringSeparator:0" + var expectedTokens = []string{"#DocStringSeparator", "#Other"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 33, err +} + +// Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0 +func (ctxt *parseContext) matchAt_34(line *Line) (newState int, err error) { + if ok, token, err := ctxt.match_EOF(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.build(token) + return 28, err + } + if ok, token, err := ctxt.match_StepLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.startRule(RuleType_Step) + ctxt.build(token) + return 9, err + } + if ok, token, err := ctxt.match_TagLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Tags) + ctxt.build(token) + return 11, err + } + if ok, token, err := ctxt.match_ScenarioLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_Scenario) + ctxt.build(token) + return 12, err + } + if ok, token, err := ctxt.match_ScenarioOutlineLine(line); ok { + ctxt.endRule(RuleType_DocString) + ctxt.endRule(RuleType_Step) + ctxt.endRule(RuleType_Background) + ctxt.startRule(RuleType_Scenario_Definition) + ctxt.startRule(RuleType_ScenarioOutline) + ctxt.build(token) + return 17, err + } + if ok, token, err := ctxt.match_Comment(line); ok { + ctxt.build(token) + return 34, err + } + if ok, token, err := ctxt.match_Empty(line); ok { + ctxt.build(token) + return 34, err + } + + // var stateComment = "State: 34 - Feature:1>Background:2>Scenario_Step:0>Step:1>Step_Arg:0>__alt1:1>DocString:2>#DocStringSeparator:0" + var expectedTokens = []string{"#EOF", "#StepLine", "#TagLine", "#ScenarioLine", "#ScenarioOutlineLine", "#Comment", "#Empty"} + if line.IsEof() { + err = fmt.Errorf("(%d:0): unexpected end of file, expected: %s", line.LineNumber, strings.Join(expectedTokens, ", ")) + } else { + err = fmt.Errorf("(%d:%d): expected: %s, got '%s'", line.LineNumber, line.Indent()+1, strings.Join(expectedTokens, ", "), line.LineText) + } + // if (ctxt.p.stopAtFirstError) throw error; + //ctxt.addError(err) + return 34, err +} + +type Matcher interface { + MatchEOF(line *Line) (bool, *Token, error) + MatchEmpty(line *Line) (bool, *Token, error) + MatchComment(line *Line) (bool, *Token, error) + MatchTagLine(line *Line) (bool, *Token, error) + MatchFeatureLine(line *Line) (bool, *Token, error) + MatchBackgroundLine(line *Line) (bool, *Token, error) + MatchScenarioLine(line *Line) (bool, *Token, error) + MatchScenarioOutlineLine(line *Line) (bool, *Token, error) + MatchExamplesLine(line *Line) (bool, *Token, error) + MatchStepLine(line *Line) (bool, *Token, error) + MatchDocStringSeparator(line *Line) (bool, *Token, error) + MatchTableRow(line *Line) (bool, *Token, error) + MatchLanguage(line *Line) (bool, *Token, error) + MatchOther(line *Line) (bool, *Token, error) + Reset() +} + +func (ctxt *parseContext) isMatch_EOF(line *Line) bool { + ok, _, _ := ctxt.match_EOF(line) + return ok +} +func (ctxt *parseContext) match_EOF(line *Line) (bool, *Token, error) { + return ctxt.m.MatchEOF(line) +} + +func (ctxt *parseContext) isMatch_Empty(line *Line) bool { + ok, _, _ := ctxt.match_Empty(line) + return ok +} +func (ctxt *parseContext) match_Empty(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchEmpty(line) +} + +func (ctxt *parseContext) isMatch_Comment(line *Line) bool { + ok, _, _ := ctxt.match_Comment(line) + return ok +} +func (ctxt *parseContext) match_Comment(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchComment(line) +} + +func (ctxt *parseContext) isMatch_TagLine(line *Line) bool { + ok, _, _ := ctxt.match_TagLine(line) + return ok +} +func (ctxt *parseContext) match_TagLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchTagLine(line) +} + +func (ctxt *parseContext) isMatch_FeatureLine(line *Line) bool { + ok, _, _ := ctxt.match_FeatureLine(line) + return ok +} +func (ctxt *parseContext) match_FeatureLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchFeatureLine(line) +} + +func (ctxt *parseContext) isMatch_BackgroundLine(line *Line) bool { + ok, _, _ := ctxt.match_BackgroundLine(line) + return ok +} +func (ctxt *parseContext) match_BackgroundLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchBackgroundLine(line) +} + +func (ctxt *parseContext) isMatch_ScenarioLine(line *Line) bool { + ok, _, _ := ctxt.match_ScenarioLine(line) + return ok +} +func (ctxt *parseContext) match_ScenarioLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchScenarioLine(line) +} + +func (ctxt *parseContext) isMatch_ScenarioOutlineLine(line *Line) bool { + ok, _, _ := ctxt.match_ScenarioOutlineLine(line) + return ok +} +func (ctxt *parseContext) match_ScenarioOutlineLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchScenarioOutlineLine(line) +} + +func (ctxt *parseContext) isMatch_ExamplesLine(line *Line) bool { + ok, _, _ := ctxt.match_ExamplesLine(line) + return ok +} +func (ctxt *parseContext) match_ExamplesLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchExamplesLine(line) +} + +func (ctxt *parseContext) isMatch_StepLine(line *Line) bool { + ok, _, _ := ctxt.match_StepLine(line) + return ok +} +func (ctxt *parseContext) match_StepLine(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchStepLine(line) +} + +func (ctxt *parseContext) isMatch_DocStringSeparator(line *Line) bool { + ok, _, _ := ctxt.match_DocStringSeparator(line) + return ok +} +func (ctxt *parseContext) match_DocStringSeparator(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchDocStringSeparator(line) +} + +func (ctxt *parseContext) isMatch_TableRow(line *Line) bool { + ok, _, _ := ctxt.match_TableRow(line) + return ok +} +func (ctxt *parseContext) match_TableRow(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchTableRow(line) +} + +func (ctxt *parseContext) isMatch_Language(line *Line) bool { + ok, _, _ := ctxt.match_Language(line) + return ok +} +func (ctxt *parseContext) match_Language(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchLanguage(line) +} + +func (ctxt *parseContext) isMatch_Other(line *Line) bool { + ok, _, _ := ctxt.match_Other(line) + return ok +} +func (ctxt *parseContext) match_Other(line *Line) (bool, *Token, error) { + if line.IsEof() { + return false, nil, nil + } + return ctxt.m.MatchOther(line) +} + +func (ctxt *parseContext) lookahead_0(initialLine *Line) bool { + var queue []*scanResult + var match bool + + for { + line, atEof, err := ctxt.scan() + queue = append(queue, &scanResult{line, atEof, err}) + + if false || ctxt.isMatch_ExamplesLine(line) { + match = true + break + } + if !(false || ctxt.isMatch_Empty(line) || ctxt.isMatch_Comment(line) || ctxt.isMatch_TagLine(line)) { + break + } + if atEof { + break + } + } + + ctxt.queue = append(ctxt.queue, queue...) + + return match +} diff --git a/go.mod b/go.mod index b2a6f37..2924270 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cucumber/godog go 1.13 require ( - github.com/cucumber/gherkin-go/v9 v9.2.0 - github.com/cucumber/messages-go/v9 v9.0.3 + github.com/DATA-DOG/go-txdb v0.1.3 + github.com/go-sql-driver/mysql v1.5.0 github.com/stretchr/testify v1.4.0 ) diff --git a/go.sum b/go.sum index b1692fa..77d1d3f 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,15 @@ -github.com/aslakhellesoy/gox v1.0.100/go.mod h1:AJl542QsKKG96COVsv0N74HHzVQgDIQPceVUh1aeU2M= -github.com/cucumber/gherkin-go/v9 v9.2.0 h1:vxpzP4JtfNSDGH4s0u4TIxv+RaX533MCD+XNakz5kLY= -github.com/cucumber/gherkin-go/v9 v9.2.0/go.mod h1:W/+Z5yOowYWXRMlC6lJvM9LFDAFfsicZ1sstjPKfWWQ= -github.com/cucumber/messages-go/v9 v9.0.3 h1:xXYjyj2aUOdkakEJAQIvP+1Bn2gOQNN+pY5pCRZQZzI= -github.com/cucumber/messages-go/v9 v9.0.3/go.mod h1:TICon2O2emBWMY1eeQvog6b+zK5c+puAFO6avjzC/JA= +github.com/DATA-DOG/go-txdb v0.1.3 h1:R4v6OuOcy2O147e2zHxU0B4NDtF+INb5R9q/CV7AEMg= +github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/run_test.go b/run_test.go index ac25831..bce07ff 100644 --- a/run_test.go +++ b/run_test.go @@ -10,12 +10,11 @@ import ( "strings" "testing" - "github.com/cucumber/gherkin-go/v9" - "github.com/cucumber/messages-go/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/gherkin" ) func okStep() error { @@ -60,16 +59,12 @@ func TestPrintsNoStepDefinitionsIfNoneFound(t *testing.T) { } func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) - r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { return ErrPending }) @@ -83,16 +78,12 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasPendingSteps(t *testing.T) { } func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) - r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) // two - is undefined @@ -106,16 +97,12 @@ func TestFailsOrPassesBasedOnStrictModeWhenHasUndefinedSteps(t *testing.T) { } func TestShouldFailOnError(t *testing.T) { - const path = "any.feature" - - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(basicGherkinFeature), (&messages.Incrementing{}).NewId) + feat, err := gherkin.ParseFeature(strings.NewReader(basicGherkinFeature)) require.NoError(t, err) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) - r := runner{ fmt: progressFunc("progress", ioutil.Discard), - features: []*feature{{GherkinDocument: gd, pickles: pickles}}, + features: []*feature{&feature{Feature: feat}}, initializer: func(s *Suite) { s.Step(`^one$`, func() error { return nil }) s.Step(`^two$`, func() error { return fmt.Errorf("error") }) @@ -256,10 +243,11 @@ type succeedRunTestCase struct { filename string // expected output file } -func TestConcurrencyRun(t *testing.T) { +func TestSucceedRun(t *testing.T) { testCases := []succeedRunTestCase{ {format: "progress", concurrency: 4, filename: "fixtures/progress_output.txt"}, {format: "junit", concurrency: 4, filename: "fixtures/junit_output.xml"}, + {format: "cucumber", concurrency: 2, filename: "fixtures/cucumber_output.json"}, } for _, tc := range testCases { @@ -277,7 +265,7 @@ func TestConcurrencyRun(t *testing.T) { } } -func testSucceedRun(t *testing.T, format string, concurrency int, expected string) { +func testSucceedRun(t *testing.T, format string, concurrency int, expectedOutput string) { output := new(bytes.Buffer) opt := Options{ @@ -294,12 +282,11 @@ func testSucceedRun(t *testing.T, format string, concurrency int, expected strin b, err := ioutil.ReadAll(output) require.NoError(t, err) - suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`) - - expected = suiteCtxReg.ReplaceAllString(expected, `suite_context.go:0`) - actual := strings.TrimSpace(string(b)) + + suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`) + expectedOutput = suiteCtxReg.ReplaceAllString(expectedOutput, `suite_context.go:0`) actual = suiteCtxReg.ReplaceAllString(actual, `suite_context.go:0`) - assert.Equalf(t, expected, actual, "[%s]", actual) + assert.Equalf(t, expectedOutput, actual, "[%s]", actual) } diff --git a/stepdef.go b/stepdef.go index a2f1dd6..1361d11 100644 --- a/stepdef.go +++ b/stepdef.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`) @@ -31,7 +31,7 @@ var matchFuncDefRef = regexp.MustCompile(`\(([^\)]+)\)`) // will result in main step failure. type Steps []string -// StepDefinition is a registered step definition +// StepDef is a registered step definition // contains a StepHandler and regexp which // is used to match a step. Args which // were matched by last executed step @@ -39,7 +39,7 @@ type Steps []string // This structure is passed to the formatter // when step is matched and is either failed // or successful -type StepDefinition struct { +type StepDef struct { args []interface{} hv reflect.Value Expr *regexp.Regexp @@ -50,7 +50,7 @@ type StepDefinition struct { undefined []string } -func (sd *StepDefinition) definitionID() string { +func (sd *StepDef) definitionID() string { ptr := sd.hv.Pointer() f := runtime.FuncForPC(ptr) file, line := f.FileLine(ptr) @@ -80,7 +80,7 @@ func (sd *StepDefinition) definitionID() string { // run a step with the matched arguments using // reflect -func (sd *StepDefinition) run() interface{} { +func (sd *StepDef) run() interface{} { typ := sd.hv.Type() if len(sd.args) < typ.NumIn() { return fmt.Errorf("func expects %d arguments, which is more than %d matched from step", typ.NumIn(), len(sd.args)) @@ -168,32 +168,20 @@ func (sd *StepDefinition) run() interface{} { case reflect.Ptr: arg := sd.args[i] switch param.Elem().String() { - case "messages.PickleStepArgument_PickleDocString": - if v, ok := arg.(*messages.PickleStepArgument); ok { - values = append(values, reflect.ValueOf(v.GetDocString())) - break + case "gherkin.DocString": + v, ok := arg.(*gherkin.DocString) + if !ok { + return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg) } - - if v, ok := arg.(*messages.PickleStepArgument_PickleDocString); ok { - values = append(values, reflect.ValueOf(v)) - break + values = append(values, reflect.ValueOf(v)) + case "gherkin.DataTable": + v, ok := arg.(*gherkin.DataTable) + if !ok { + return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *gherkin.DocString`, i, arg, arg) } - - return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleDocString`, i, arg, arg) - case "messages.PickleStepArgument_PickleTable": - if v, ok := arg.(*messages.PickleStepArgument); ok { - values = append(values, reflect.ValueOf(v.GetDataTable())) - break - } - - if v, ok := arg.(*messages.PickleStepArgument_PickleTable); ok { - values = append(values, reflect.ValueOf(v)) - break - } - - return fmt.Errorf(`cannot convert argument %d: "%v" of type "%T" to *messages.PickleStepArgument_PickleTable`, i, arg, arg) + values = append(values, reflect.ValueOf(v)) default: - return fmt.Errorf("the argument %d type %T is not supported %s", i, arg, param.Elem().String()) + return fmt.Errorf("the argument %d type %T is not supported", i, arg) } case reflect.Slice: switch param { @@ -210,11 +198,10 @@ func (sd *StepDefinition) run() interface{} { return fmt.Errorf("the argument %d type %s is not supported", i, param.Kind()) } } - return sd.hv.Call(values)[0].Interface() } -func (sd *StepDefinition) shouldBeString(idx int) (string, error) { +func (sd *StepDef) shouldBeString(idx int) (string, error) { arg := sd.args[idx] s, ok := arg.(string) if !ok { diff --git a/stepdef_test.go b/stepdef_test.go index 0f13bce..6e92a90 100644 --- a/stepdef_test.go +++ b/stepdef_test.go @@ -5,13 +5,13 @@ import ( "strings" "testing" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) func TestShouldSupportIntTypes(t *testing.T) { fn := func(a int64, b int32, c int16, d int8) error { return nil } - def := &StepDefinition{ + def := &StepDef{ Handler: fn, hv: reflect.ValueOf(fn), } @@ -30,7 +30,7 @@ func TestShouldSupportIntTypes(t *testing.T) { func TestShouldSupportFloatTypes(t *testing.T) { fn := func(a float64, b float32) error { return nil } - def := &StepDefinition{ + def := &StepDef{ Handler: fn, hv: reflect.ValueOf(fn), } @@ -48,12 +48,12 @@ func TestShouldSupportFloatTypes(t *testing.T) { func TestShouldNotSupportOtherPointerTypesThanGherkin(t *testing.T) { fn1 := func(a *int) error { return nil } - fn2 := func(a *messages.PickleStepArgument_PickleDocString) error { return nil } - fn3 := func(a *messages.PickleStepArgument_PickleTable) error { return nil } + fn2 := func(a *gherkin.DocString) error { return nil } + fn3 := func(a *gherkin.DataTable) error { return nil } - def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{(*int)(nil)}} - def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{&messages.PickleStepArgument_PickleDocString{}}} - def3 := &StepDefinition{Handler: fn3, hv: reflect.ValueOf(fn3), args: []interface{}{(*messages.PickleStepArgument_PickleTable)(nil)}} + def1 := &StepDef{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{(*int)(nil)}} + def2 := &StepDef{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{(*gherkin.DocString)(nil)}} + def3 := &StepDef{Handler: fn3, hv: reflect.ValueOf(fn3), args: []interface{}{(*gherkin.DataTable)(nil)}} if err := def1.run(); err == nil { t.Fatalf("expected conversion error, but got none") @@ -70,8 +70,8 @@ func TestShouldSupportOnlyByteSlice(t *testing.T) { fn1 := func(a []byte) error { return nil } fn2 := func(a []string) error { return nil } - def1 := &StepDefinition{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{"str"}} - def2 := &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{[]string{}}} + def1 := &StepDef{Handler: fn1, hv: reflect.ValueOf(fn1), args: []interface{}{"str"}} + def2 := &StepDef{Handler: fn2, hv: reflect.ValueOf(fn2), args: []interface{}{[]string{}}} if err := def1.run(); err != nil { t.Fatalf("unexpected error: %v", err) @@ -83,7 +83,7 @@ func TestShouldSupportOnlyByteSlice(t *testing.T) { func TestUnexpectedArguments(t *testing.T) { fn := func(a, b int) error { return nil } - def := &StepDefinition{Handler: fn, hv: reflect.ValueOf(fn)} + def := &StepDef{Handler: fn, hv: reflect.ValueOf(fn)} def.args = []interface{}{"1"} if err := def.run(); err == nil { @@ -97,7 +97,7 @@ func TestUnexpectedArguments(t *testing.T) { // @TODO maybe we should support duration // fn2 := func(err time.Duration) error { return nil } - // def = &StepDefinition{Handler: fn2, hv: reflect.ValueOf(fn2)} + // def = &StepDef{Handler: fn2, hv: reflect.ValueOf(fn2)} // def.args = []interface{}{"1"} // if err := def.run(); err == nil { diff --git a/suite.go b/suite.go index 07848bb..5b9a2c9 100644 --- a/suite.go +++ b/suite.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "io" + "math/rand" "os" "path/filepath" "reflect" @@ -14,17 +15,16 @@ import ( "time" "unicode/utf8" - "github.com/cucumber/gherkin-go/v9" - "github.com/cucumber/messages-go/v9" + "github.com/cucumber/godog/gherkin" ) var errorInterface = reflect.TypeOf((*error)(nil)).Elem() var typeOfBytes = reflect.TypeOf([]byte(nil)) type feature struct { - *messages.GherkinDocument - pickles []*messages.Pickle - pickleResults []*pickleResult + *gherkin.Feature + + Scenarios []*scenario time time.Time Content []byte `json:"-"` @@ -32,118 +32,47 @@ type feature struct { order int } -func (f feature) findScenario(astScenarioID string) *messages.GherkinDocument_Feature_Scenario { - for _, child := range f.GherkinDocument.Feature.Children { - if sc := child.GetScenario(); sc != nil && sc.Id == astScenarioID { - return sc - } - } - - return nil -} - -func (f feature) findBackground(astScenarioID string) *messages.GherkinDocument_Feature_Background { - var bg *messages.GherkinDocument_Feature_Background - - for _, child := range f.GherkinDocument.Feature.Children { - if tmp := child.GetBackground(); tmp != nil { - bg = tmp - } - - if sc := child.GetScenario(); sc != nil && sc.Id == astScenarioID { - return bg - } - } - - return nil -} - -func (f feature) findExample(exampleAstID string) (*messages.GherkinDocument_Feature_Scenario_Examples, *messages.GherkinDocument_Feature_TableRow) { - for _, child := range f.GherkinDocument.Feature.Children { - if sc := child.GetScenario(); sc != nil { - for _, example := range sc.Examples { - for _, row := range example.TableBody { - if row.Id == exampleAstID { - return example, row - } - } - } - } - } - - return nil, nil -} - -func (f feature) findStep(astStepID string) *messages.GherkinDocument_Feature_Step { - for _, child := range f.GherkinDocument.Feature.Children { - if sc := child.GetScenario(); sc != nil { - for _, step := range sc.GetSteps() { - if step.Id == astStepID { - return step - } - } - } - - if bg := child.GetBackground(); bg != nil { - for _, step := range bg.GetSteps() { - if step.Id == astStepID { - return step - } - } - } - } - - return nil -} - func (f feature) startedAt() time.Time { return f.time } func (f feature) finishedAt() time.Time { - if len(f.pickleResults) == 0 { + if len(f.Scenarios) == 0 { return f.startedAt() } - return f.pickleResults[len(f.pickleResults)-1].finishedAt() + return f.Scenarios[len(f.Scenarios)-1].finishedAt() } func (f feature) appendStepResult(s *stepResult) { - pickles := f.pickleResults[len(f.pickleResults)-1] - pickles.stepResults = append(pickles.stepResults, s) -} - -func (f feature) lastPickleResult() *pickleResult { - return f.pickleResults[len(f.pickleResults)-1] -} - -func (f feature) lastStepResult() *stepResult { - last := f.lastPickleResult() - return last.stepResults[len(last.stepResults)-1] + scenario := f.Scenarios[len(f.Scenarios)-1] + scenario.Steps = append(scenario.Steps, s) } type sortByName []*feature func (s sortByName) Len() int { return len(s) } -func (s sortByName) Less(i, j int) bool { return s[i].Feature.Name < s[j].Feature.Name } +func (s sortByName) Less(i, j int) bool { return s[i].Name < s[j].Name } func (s sortByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -type pickleResult struct { +type scenario struct { Name string + OutlineName string + ExampleNo int time time.Time - stepResults []*stepResult + Steps []*stepResult } -func (s pickleResult) startedAt() time.Time { +func (s scenario) startedAt() time.Time { return s.time } -func (s pickleResult) finishedAt() time.Time { - if len(s.stepResults) == 0 { +func (s scenario) finishedAt() time.Time { + if len(s.Steps) == 0 { return s.startedAt() } - return s.stepResults[len(s.stepResults)-1].time + return s.Steps[len(s.Steps)-1].time } // ErrUndefined is returned in case if step definition was not found @@ -165,7 +94,7 @@ var ErrPending = fmt.Errorf("step implementation is pending") // executions are catching panic error since it may // be a context specific error. type Suite struct { - steps []*StepDefinition + steps []*StepDef features []*feature fmt Formatter @@ -176,16 +105,16 @@ type Suite struct { // suite event handlers beforeSuiteHandlers []func() - beforeFeatureHandlers []func(*messages.GherkinDocument) - beforeScenarioHandlers []func(*messages.Pickle) - beforeStepHandlers []func(*messages.Pickle_PickleStep) - afterStepHandlers []func(*messages.Pickle_PickleStep, error) - afterScenarioHandlers []func(*messages.Pickle, error) - afterFeatureHandlers []func(*messages.GherkinDocument) + beforeFeatureHandlers []func(*gherkin.Feature) + beforeScenarioHandlers []func(interface{}) + beforeStepHandlers []func(*gherkin.Step) + afterStepHandlers []func(*gherkin.Step, error) + afterScenarioHandlers []func(interface{}, error) + afterFeatureHandlers []func(*gherkin.Feature) afterSuiteHandlers []func() } -// Step allows to register a *StepDefinition in Godog +// Step allows to register a *StepDef in Godog // feature suite, the definition will be applied // to all steps matching the given Regexp expr. // @@ -197,7 +126,7 @@ type Suite struct { // the same step, then only the first matched handler // will be applied. // -// If none of the *StepDefinition is matched, then +// If none of the *StepDef is matched, then // ErrUndefined error will be returned when // running steps. func (s *Suite) Step(expr interface{}, stepFunc interface{}) { @@ -224,7 +153,7 @@ func (s *Suite) Step(expr interface{}, stepFunc interface{}) { panic(fmt.Sprintf("expected handler to return only one value, but it has: %d", typ.NumOut())) } - def := &StepDefinition{ + def := &StepDef{ Handler: stepFunc, Expr: regex, hv: v, @@ -271,28 +200,31 @@ func (s *Suite) BeforeSuite(fn func()) { // scenario to restart it. // // Use it wisely and avoid sharing state between scenarios. -func (s *Suite) BeforeFeature(fn func(*messages.GherkinDocument)) { +func (s *Suite) BeforeFeature(fn func(*gherkin.Feature)) { s.beforeFeatureHandlers = append(s.beforeFeatureHandlers, fn) } // BeforeScenario registers a function or method -// to be run before every pickle. +// to be run before every scenario or scenario outline. +// +// The interface argument may be *gherkin.Scenario +// or *gherkin.ScenarioOutline // // It is a good practice to restore the default state // before every scenario so it would be isolated from // any kind of state. -func (s *Suite) BeforeScenario(fn func(*messages.Pickle)) { +func (s *Suite) BeforeScenario(fn func(interface{})) { s.beforeScenarioHandlers = append(s.beforeScenarioHandlers, fn) } // BeforeStep registers a function or method -// to be run before every step. -func (s *Suite) BeforeStep(fn func(*messages.Pickle_PickleStep)) { +// to be run before every scenario +func (s *Suite) BeforeStep(fn func(*gherkin.Step)) { s.beforeStepHandlers = append(s.beforeStepHandlers, fn) } // AfterStep registers an function or method -// to be run after every step. +// to be run after every scenario // // It may be convenient to return a different kind of error // in order to print more state details which may help @@ -300,19 +232,22 @@ func (s *Suite) BeforeStep(fn func(*messages.Pickle_PickleStep)) { // // In some cases, for example when running a headless // browser, to take a screenshot after failure. -func (s *Suite) AfterStep(fn func(*messages.Pickle_PickleStep, error)) { +func (s *Suite) AfterStep(fn func(*gherkin.Step, error)) { s.afterStepHandlers = append(s.afterStepHandlers, fn) } // AfterScenario registers an function or method -// to be run after every pickle. -func (s *Suite) AfterScenario(fn func(*messages.Pickle, error)) { +// to be run after every scenario or scenario outline +// +// The interface argument may be *gherkin.Scenario +// or *gherkin.ScenarioOutline +func (s *Suite) AfterScenario(fn func(interface{}, error)) { s.afterScenarioHandlers = append(s.afterScenarioHandlers, fn) } // AfterFeature registers a function or method // to be run once after feature executed all scenarios. -func (s *Suite) AfterFeature(fn func(*messages.GherkinDocument)) { +func (s *Suite) AfterFeature(fn func(*gherkin.Feature)) { s.afterFeatureHandlers = append(s.afterFeatureHandlers, fn) } @@ -341,7 +276,7 @@ func (s *Suite) run() { } } -func (s *Suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition { +func (s *Suite) matchStep(step *gherkin.Step) *StepDef { def := s.matchStepText(step.Text) if def != nil && step.Argument != nil { def.args = append(def.args, step.Argument) @@ -349,14 +284,14 @@ func (s *Suite) matchStep(step *messages.Pickle_PickleStep) *StepDefinition { return def } -func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleStep, prevStepErr error) (err error) { +func (s *Suite) runStep(step *gherkin.Step, prevStepErr error) (err error) { // run before step handlers for _, f := range s.beforeStepHandlers { f(step) } match := s.matchStep(step) - s.fmt.Defined(pickle, step, match) + s.fmt.Defined(step, match) // user multistep definitions may panic defer func() { @@ -377,11 +312,11 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte switch err { case nil: - s.fmt.Passed(pickle, step, match) + s.fmt.Passed(step, match) case ErrPending: - s.fmt.Pending(pickle, step, match) + s.fmt.Pending(step, match) default: - s.fmt.Failed(pickle, step, match, err) + s.fmt.Failed(step, match, err) } // run after step handlers @@ -394,7 +329,7 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte return err } else if len(undef) > 0 { if match != nil { - match = &StepDefinition{ + match = &StepDef{ args: match.args, hv: match.hv, Expr: match.Expr, @@ -403,12 +338,12 @@ func (s *Suite) runStep(pickle *messages.Pickle, step *messages.Pickle_PickleSte undefined: undef, } } - s.fmt.Undefined(pickle, step, match) + s.fmt.Undefined(step, match) return ErrUndefined } if prevStepErr != nil { - s.fmt.Skipped(pickle, step, match) + s.fmt.Skipped(step, match) return nil } @@ -473,7 +408,7 @@ func (s *Suite) maybeSubSteps(result interface{}) error { return nil } -func (s *Suite) matchStepText(text string) *StepDefinition { +func (s *Suite) matchStepText(text string) *StepDef { for _, h := range s.steps { if m := h.Expr.FindStringSubmatch(text); len(m) > 0 { var args []interface{} @@ -483,7 +418,7 @@ func (s *Suite) matchStepText(text string) *StepDefinition { // since we need to assign arguments // better to copy the step definition - return &StepDefinition{ + return &StepDef{ args: args, hv: h.hv, Expr: h.Expr, @@ -495,9 +430,9 @@ func (s *Suite) matchStepText(text string) *StepDefinition { return nil } -func (s *Suite) runSteps(pickle *messages.Pickle, steps []*messages.Pickle_PickleStep) (err error) { +func (s *Suite) runSteps(steps []*gherkin.Step) (err error) { for _, step := range steps { - stepErr := s.runStep(pickle, step, err) + stepErr := s.runStep(step, err) switch stepErr { case ErrUndefined: // do not overwrite failed error @@ -514,6 +449,110 @@ func (s *Suite) runSteps(pickle *messages.Pickle, steps []*messages.Pickle_Pickl return } +func (s *Suite) runOutline(outline *gherkin.ScenarioOutline, b *gherkin.Background) (failErr error) { + s.fmt.Node(outline) + + for _, ex := range outline.Examples { + example, hasExamples := examples(ex) + if !hasExamples { + // @TODO: may need to print empty example node, but + // for backward compatibility, cannot cast to *gherkin.ExamplesBase + // at the moment + continue + } + + s.fmt.Node(example) + placeholders := example.TableHeader.Cells + groups := example.TableBody + + for _, group := range groups { + if !isEmptyScenario(outline) { + for _, f := range s.beforeScenarioHandlers { + f(outline) + } + } + var steps []*gherkin.Step + for _, outlineStep := range outline.Steps { + text := outlineStep.Text + for i, placeholder := range placeholders { + text = strings.Replace(text, "<"+placeholder.Value+">", group.Cells[i].Value, -1) + } + + // translate argument + arg := outlineStep.Argument + switch t := outlineStep.Argument.(type) { + case *gherkin.DataTable: + tbl := &gherkin.DataTable{ + Node: t.Node, + Rows: make([]*gherkin.TableRow, len(t.Rows)), + } + for i, row := range t.Rows { + cells := make([]*gherkin.TableCell, len(row.Cells)) + for j, cell := range row.Cells { + trans := cell.Value + for i, placeholder := range placeholders { + trans = strings.Replace(trans, "<"+placeholder.Value+">", group.Cells[i].Value, -1) + } + cells[j] = &gherkin.TableCell{ + Node: cell.Node, + Value: trans, + } + } + tbl.Rows[i] = &gherkin.TableRow{ + Node: row.Node, + Cells: cells, + } + } + arg = tbl + case *gherkin.DocString: + trans := t.Content + for i, placeholder := range placeholders { + trans = strings.Replace(trans, "<"+placeholder.Value+">", group.Cells[i].Value, -1) + } + arg = &gherkin.DocString{ + Node: t.Node, + Content: trans, + ContentType: t.ContentType, + Delimitter: t.Delimitter, + } + } + + // clone a step + step := &gherkin.Step{ + Node: outlineStep.Node, + Text: text, + Keyword: outlineStep.Keyword, + Argument: arg, + } + steps = append(steps, step) + } + + // run example table row + s.fmt.Node(group) + + if b != nil { + steps = append(b.Steps, steps...) + } + + err := s.runSteps(steps) + + if !isEmptyScenario(outline) { + for _, f := range s.afterScenarioHandlers { + f(outline, err) + } + } + + if s.shouldFail(err) { + failErr = err + if s.stopOnFailure { + return + } + } + } + } + return +} + func (s *Suite) shouldFail(err error) bool { if err == nil { return false @@ -527,24 +566,46 @@ func (s *Suite) shouldFail(err error) bool { } func (s *Suite) runFeature(f *feature) { - if !isEmptyFeature(f.pickles) { + if !isEmptyFeature(f.Feature) { for _, fn := range s.beforeFeatureHandlers { - fn(f.GherkinDocument) + fn(f.Feature) } } - s.fmt.Feature(f.GherkinDocument, f.Path, f.Content) + s.fmt.Feature(f.Feature, f.Path, f.Content) + + // make a local copy of the feature scenario defenitions, + // then shuffle it if we are randomizing scenarios + scenarios := make([]interface{}, len(f.ScenarioDefinitions)) + if s.randomSeed != 0 { + r := rand.New(rand.NewSource(s.randomSeed)) + perm := r.Perm(len(f.ScenarioDefinitions)) + for i, v := range perm { + scenarios[v] = f.ScenarioDefinitions[i] + } + } else { + copy(scenarios, f.ScenarioDefinitions) + } defer func() { - if !isEmptyFeature(f.pickles) { + if !isEmptyFeature(f.Feature) { for _, fn := range s.afterFeatureHandlers { - fn(f.GherkinDocument) + fn(f.Feature) } } }() - for _, pickle := range f.pickles { - err := s.runPickle(pickle) + for _, scenario := range scenarios { + var err error + if f.Background != nil { + s.fmt.Node(f.Background) + } + switch t := scenario.(type) { + case *gherkin.ScenarioOutline: + err = s.runOutline(t, f.Background) + case *gherkin.Scenario: + err = s.runScenario(t, f.Background) + } if s.shouldFail(err) { s.failed = true if s.stopOnFailure { @@ -554,35 +615,31 @@ func (s *Suite) runFeature(f *feature) { } } -func isEmptyFeature(pickles []*messages.Pickle) bool { - for _, pickle := range pickles { - if len(pickle.Steps) > 0 { - return false - } - } - - return true -} - -func (s *Suite) runPickle(pickle *messages.Pickle) (err error) { - if len(pickle.Steps) == 0 { - s.fmt.Pickle(pickle) +func (s *Suite) runScenario(scenario *gherkin.Scenario, b *gherkin.Background) (err error) { + if isEmptyScenario(scenario) { + s.fmt.Node(scenario) return ErrUndefined } // run before scenario handlers for _, f := range s.beforeScenarioHandlers { - f(pickle) + f(scenario) } - s.fmt.Pickle(pickle) + s.fmt.Node(scenario) + + // background + steps := scenario.Steps + if b != nil { + steps = append(b.Steps, steps...) + } // scenario - err = s.runSteps(pickle, pickle.Steps) + err = s.runSteps(steps) // run after scenario handlers for _, f := range s.afterScenarioHandlers { - f(pickle, err) + f(scenario, err) } return @@ -621,7 +678,7 @@ func extractFeaturePathLine(p string) (string, int) { return retPath, line } -func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) { +func parseFeatureFile(path string) (*feature, error) { reader, err := os.Open(path) if err != nil { return nil, err @@ -629,22 +686,19 @@ func parseFeatureFile(path string, newIDFunc func() string) (*feature, error) { defer reader.Close() var buf bytes.Buffer - gherkinDocument, err := gherkin.ParseGherkinDocument(io.TeeReader(reader, &buf), newIDFunc) + ft, err := gherkin.ParseFeature(io.TeeReader(reader, &buf)) if err != nil { return nil, fmt.Errorf("%s - %v", path, err) } - pickles := gherkin.Pickles(*gherkinDocument, path, newIDFunc) - return &feature{ - GherkinDocument: gherkinDocument, - pickles: pickles, - Content: buf.Bytes(), - Path: path, + Path: path, + Feature: ft, + Content: buf.Bytes(), }, nil } -func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) { +func parseFeatureDir(dir string) ([]*feature, error) { var features []*feature return features, filepath.Walk(dir, func(p string, f os.FileInfo, err error) error { if err != nil { @@ -659,7 +713,7 @@ func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) { return nil } - feat, err := parseFeatureFile(p, newIDFunc) + feat, err := parseFeatureFile(p) if err != nil { return err } @@ -670,36 +724,39 @@ func parseFeatureDir(dir string, newIDFunc func() string) ([]*feature, error) { func parsePath(path string) ([]*feature, error) { var features []*feature - - path, line := extractFeaturePathLine(path) + // check if line number is specified + var line int + path, line = extractFeaturePathLine(path) fi, err := os.Stat(path) if err != nil { return features, err } - newIDFunc := (&messages.Incrementing{}).NewId - if fi.IsDir() { - return parseFeatureDir(path, newIDFunc) + return parseFeatureDir(path) } - ft, err := parseFeatureFile(path, newIDFunc) + ft, err := parseFeatureFile(path) if err != nil { return features, err } // filter scenario by line number - var pickles []*messages.Pickle - for _, pickle := range ft.pickles { - sc := ft.findScenario(pickle.AstNodeIds[0]) - - if line == -1 || uint32(line) == sc.Location.Line { - pickles = append(pickles, pickle) + var scenarios []interface{} + for _, def := range ft.ScenarioDefinitions { + var ln int + switch t := def.(type) { + case *gherkin.Scenario: + ln = t.Location.Line + case *gherkin.ScenarioOutline: + ln = t.Location.Line + } + if line == -1 || ln == line { + scenarios = append(scenarios, def) } } - ft.pickles = pickles - + ft.ScenarioDefinitions = scenarios return append(features, ft), nil } @@ -727,7 +784,6 @@ func parseFeatures(filter string, paths []string) ([]*feature, error) { byPath[ft.Path] = ft } } - return filterFeatures(filter, byPath), nil } @@ -739,7 +795,7 @@ func (s sortByOrderGiven) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func filterFeatures(tags string, collected map[string]*feature) (features []*feature) { for _, ft := range collected { - applyTagFilter(tags, ft) + applyTagFilter(tags, ft.Feature) features = append(features, ft) } @@ -748,23 +804,81 @@ func filterFeatures(tags string, collected map[string]*feature) (features []*fea return features } -func applyTagFilter(tags string, ft *feature) { +func applyTagFilter(tags string, ft *gherkin.Feature) { if len(tags) == 0 { return } - var pickles []*messages.Pickle - for _, pickle := range ft.pickles { - if matchesTags(tags, pickle.Tags) { - pickles = append(pickles, pickle) + var scenarios []interface{} + for _, scenario := range ft.ScenarioDefinitions { + switch t := scenario.(type) { + case *gherkin.ScenarioOutline: + var allExamples []*gherkin.Examples + for _, examples := range t.Examples { + if matchesTags(tags, allTags(ft, t, examples)) { + allExamples = append(allExamples, examples) + } + } + t.Examples = allExamples + if len(t.Examples) > 0 { + scenarios = append(scenarios, scenario) + } + case *gherkin.Scenario: + if matchesTags(tags, allTags(ft, t)) { + scenarios = append(scenarios, scenario) + } } } + ft.ScenarioDefinitions = scenarios +} - ft.pickles = pickles +func allTags(nodes ...interface{}) []string { + var tags, tmp []string + for _, node := range nodes { + var gr []*gherkin.Tag + switch t := node.(type) { + case *gherkin.Feature: + gr = t.Tags + case *gherkin.ScenarioOutline: + gr = t.Tags + case *gherkin.Scenario: + gr = t.Tags + case *gherkin.Examples: + gr = t.Tags + } + + for _, gtag := range gr { + tag := strings.TrimSpace(gtag.Name) + if tag[0] == '@' { + tag = tag[1:] + } + copy(tmp, tags) + var found bool + for _, tg := range tmp { + if tg == tag { + found = true + break + } + } + if !found { + tags = append(tags, tag) + } + } + } + return tags +} + +func hasTag(tags []string, tag string) bool { + for _, t := range tags { + if t == tag { + return true + } + } + return false } // based on http://behat.readthedocs.org/en/v2.5/guides/6.cli.html#gherkin-filters -func matchesTags(filter string, tags []*messages.Pickle_PickleTag) (ok bool) { +func matchesTags(filter string, tags []string) (ok bool) { ok = true for _, andTags := range strings.Split(filter, "&&") { var okComma bool @@ -781,14 +895,3 @@ func matchesTags(filter string, tags []*messages.Pickle_PickleTag) (ok bool) { } return } - -func hasTag(tags []*messages.Pickle_PickleTag, tag string) bool { - for _, t := range tags { - tName := strings.Replace(t.Name, "@", "", -1) - - if tName == tag { - return true - } - } - return false -} diff --git a/suite_context.go b/suite_context.go index 11670dd..679fa9c 100644 --- a/suite_context.go +++ b/suite_context.go @@ -12,10 +12,8 @@ import ( "strconv" "strings" - "github.com/cucumber/gherkin-go/v9" - "github.com/cucumber/messages-go/v9" - "github.com/cucumber/godog/colors" + "github.com/cucumber/godog/gherkin" ) // SuiteContext provides steps for godog suite execution and @@ -114,7 +112,7 @@ type suiteContext struct { out bytes.Buffer } -func (s *suiteContext) ResetBeforeEachScenario(*messages.Pickle) { +func (s *suiteContext) ResetBeforeEachScenario(interface{}) { // reset whole suite with the state s.out.Reset() s.paths = []string{} @@ -130,7 +128,7 @@ func (s *suiteContext) iRunFeatureSuiteWithTags(tags string) error { return err } for _, feat := range s.testedSuite.features { - applyTagFilter(tags, feat) + applyTagFilter(tags, feat.Feature) } s.testedSuite.fmt = testFormatterFunc("godog", &s.out) s.testedSuite.run() @@ -153,7 +151,7 @@ func (s *suiteContext) iRunFeatureSuiteWithFormatter(name string) error { return nil } -func (s *suiteContext) thereShouldBeEventsFired(doc *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) thereShouldBeEventsFired(doc *gherkin.DocString) error { actual := strings.Split(strings.TrimSpace(s.out.String()), "\n") expect := strings.Split(strings.TrimSpace(doc.Content), "\n") if len(expect) != len(actual) { @@ -186,7 +184,7 @@ func (s *suiteContext) cleanupSnippet(snip string) string { return strings.Join(lines, "\n") } -func (s *suiteContext) theUndefinedStepSnippetsShouldBe(body *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) theUndefinedStepSnippetsShouldBe(body *gherkin.DocString) error { f, ok := s.testedSuite.fmt.(*testFormatter) if !ok { return fmt.Errorf("this step requires testFormatter, but there is: %T", s.testedSuite.fmt) @@ -199,7 +197,7 @@ func (s *suiteContext) theUndefinedStepSnippetsShouldBe(body *messages.PickleSte return nil } -func (s *suiteContext) followingStepsShouldHave(status string, steps *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) followingStepsShouldHave(status string, steps *gherkin.DocString) error { var expected = strings.Split(steps.Content, "\n") var actual, unmatched, matched []string @@ -209,23 +207,23 @@ func (s *suiteContext) followingStepsShouldHave(status string, steps *messages.P } switch status { case "passed": - for _, st := range f.findStepResults(passed) { + for _, st := range f.passed { actual = append(actual, st.step.Text) } case "failed": - for _, st := range f.findStepResults(failed) { + for _, st := range f.failed { actual = append(actual, st.step.Text) } case "skipped": - for _, st := range f.findStepResults(skipped) { + for _, st := range f.skipped { actual = append(actual, st.step.Text) } case "undefined": - for _, st := range f.findStepResults(undefined) { + for _, st := range f.undefined { actual = append(actual, st.step.Text) } case "pending": - for _, st := range f.findStepResults(pending) { + for _, st := range f.pending { actual = append(actual, st.step.Text) } default: @@ -270,23 +268,19 @@ func (s *suiteContext) allStepsShouldHave(status string) error { return fmt.Errorf("this step requires testFormatter, but there is: %T", s.testedSuite.fmt) } - total := len(f.findStepResults(passed)) + - len(f.findStepResults(failed)) + - len(f.findStepResults(skipped)) + - len(f.findStepResults(undefined)) + - len(f.findStepResults(pending)) + total := len(f.passed) + len(f.failed) + len(f.skipped) + len(f.undefined) + len(f.pending) var actual int switch status { case "passed": - actual = len(f.findStepResults(passed)) + actual = len(f.passed) case "failed": - actual = len(f.findStepResults(failed)) + actual = len(f.failed) case "skipped": - actual = len(f.findStepResults(skipped)) + actual = len(f.skipped) case "undefined": - actual = len(f.findStepResults(undefined)) + actual = len(f.undefined) case "pending": - actual = len(f.findStepResults(pending)) + actual = len(f.pending) default: return fmt.Errorf("unexpected step status wanted: %s", status) } @@ -304,22 +298,22 @@ func (s *suiteContext) iAmListeningToSuiteEvents() error { s.testedSuite.AfterSuite(func() { s.events = append(s.events, &firedEvent{"AfterSuite", []interface{}{}}) }) - s.testedSuite.BeforeFeature(func(ft *messages.GherkinDocument) { + s.testedSuite.BeforeFeature(func(ft *gherkin.Feature) { s.events = append(s.events, &firedEvent{"BeforeFeature", []interface{}{ft}}) }) - s.testedSuite.AfterFeature(func(ft *messages.GherkinDocument) { + s.testedSuite.AfterFeature(func(ft *gherkin.Feature) { s.events = append(s.events, &firedEvent{"AfterFeature", []interface{}{ft}}) }) - s.testedSuite.BeforeScenario(func(pickle *messages.Pickle) { - s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{pickle}}) + s.testedSuite.BeforeScenario(func(scenario interface{}) { + s.events = append(s.events, &firedEvent{"BeforeScenario", []interface{}{scenario}}) }) - s.testedSuite.AfterScenario(func(pickle *messages.Pickle, err error) { - s.events = append(s.events, &firedEvent{"AfterScenario", []interface{}{pickle, err}}) + s.testedSuite.AfterScenario(func(scenario interface{}, err error) { + s.events = append(s.events, &firedEvent{"AfterScenario", []interface{}{scenario, err}}) }) - s.testedSuite.BeforeStep(func(step *messages.Pickle_PickleStep) { + s.testedSuite.BeforeStep(func(step *gherkin.Step) { s.events = append(s.events, &firedEvent{"BeforeStep", []interface{}{step}}) }) - s.testedSuite.AfterStep(func(step *messages.Pickle_PickleStep, err error) { + s.testedSuite.AfterStep(func(step *gherkin.Step, err error) { s.events = append(s.events, &firedEvent{"AfterStep", []interface{}{step, err}}) }) return nil @@ -330,10 +324,9 @@ func (s *suiteContext) aFailingStep() error { } // parse a given feature file body as a feature -func (s *suiteContext) aFeatureFile(path string, body *messages.PickleStepArgument_PickleDocString) error { - gd, err := gherkin.ParseGherkinDocument(strings.NewReader(body.Content), (&messages.Incrementing{}).NewId) - pickles := gherkin.Pickles(*gd, path, (&messages.Incrementing{}).NewId) - s.testedSuite.features = append(s.testedSuite.features, &feature{GherkinDocument: gd, pickles: pickles, Path: path}) +func (s *suiteContext) aFeatureFile(name string, body *gherkin.DocString) error { + ft, err := gherkin.ParseFeature(strings.NewReader(body.Content)) + s.testedSuite.features = append(s.testedSuite.features, &feature{Feature: ft, Path: name}) return err } @@ -361,7 +354,7 @@ func (s *suiteContext) theSuiteShouldHave(state string) error { return nil } -func (s *suiteContext) iShouldHaveNumFeatureFiles(num int, files *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) iShouldHaveNumFeatureFiles(num int, files *gherkin.DocString) error { if len(s.testedSuite.features) != num { return fmt.Errorf("expected %d features to be parsed, but have %d", num, len(s.testedSuite.features)) } @@ -406,7 +399,7 @@ func (s *suiteContext) iRunFeatureSuite() error { func (s *suiteContext) numScenariosRegistered(expected int) (err error) { var num int for _, ft := range s.testedSuite.features { - num += len(ft.pickles) + num += len(ft.ScenarioDefinitions) } if num != expected { err = fmt.Errorf("expected %d scenarios to be registered, but got %d", expected, num) @@ -436,7 +429,9 @@ func (s *suiteContext) thereWasEventTriggeredBeforeScenario(expected string) err var name string switch t := event.args[0].(type) { - case *messages.Pickle: + case *gherkin.Scenario: + name = t.Name + case *gherkin.ScenarioOutline: name = t.Name } if name == expected { @@ -453,7 +448,7 @@ func (s *suiteContext) thereWasEventTriggeredBeforeScenario(expected string) err return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, expected, `"`+strings.Join(found, `", "`)+`"`) } -func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *messages.PickleStepArgument_PickleTable) error { +func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *gherkin.DataTable) error { if len(tbl.Rows[0].Cells) != 2 { return fmt.Errorf("expected two columns for event table row, got: %d", len(tbl.Rows[0].Cells)) } @@ -470,7 +465,7 @@ func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *messages.Pic return nil } -func (s *suiteContext) theRenderJSONWillBe(docstring *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) theRenderJSONWillBe(docstring *gherkin.DocString) error { suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`) expectedString := docstring.Content @@ -494,7 +489,7 @@ func (s *suiteContext) theRenderJSONWillBe(docstring *messages.PickleStepArgumen return nil } -func (s *suiteContext) theRenderOutputWillBe(docstring *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) theRenderOutputWillBe(docstring *gherkin.DocString) error { suiteCtxReg := regexp.MustCompile(`suite_context.go:\d+`) suiteCtxFuncReg := regexp.MustCompile(`github.com/cucumber/godog.SuiteContext.func(\d+)`) @@ -513,7 +508,7 @@ func (s *suiteContext) theRenderOutputWillBe(docstring *messages.PickleStepArgum return nil } -func (s *suiteContext) theRenderXMLWillBe(docstring *messages.PickleStepArgument_PickleDocString) error { +func (s *suiteContext) theRenderXMLWillBe(docstring *gherkin.DocString) error { expectedString := docstring.Content actualString := s.out.String() @@ -528,23 +523,28 @@ func (s *suiteContext) theRenderXMLWillBe(docstring *messages.PickleStepArgument } if !reflect.DeepEqual(expected, actual) { - return fmt.Errorf("expected json does not match actual: %s", actualString) + return fmt.Errorf("expected xml does not match actual: %s", actualString) } return nil } type testFormatter struct { *basefmt - pickles []*messages.Pickle + scenarios []interface{} } func testFormatterFunc(suite string, out io.Writer) Formatter { return &testFormatter{basefmt: newBaseFmt(suite, out)} } -func (f *testFormatter) Pickle(p *messages.Pickle) { - f.basefmt.Pickle(p) - f.pickles = append(f.pickles, p) +func (f *testFormatter) Node(node interface{}) { + f.basefmt.Node(node) + switch t := node.(type) { + case *gherkin.Scenario: + f.scenarios = append(f.scenarios, t) + case *gherkin.ScenarioOutline: + f.scenarios = append(f.scenarios, t) + } } func (f *testFormatter) Summary() {} diff --git a/tag_filter_test.go b/tag_filter_test.go index 593e36e..57440e2 100644 --- a/tag_filter_test.go +++ b/tag_filter_test.go @@ -2,32 +2,28 @@ package godog import ( "testing" - - "github.com/cucumber/messages-go/v9" ) -func assertNotMatchesTagFilter(tags []*messages.Pickle_PickleTag, filter string, t *testing.T) { +func assertNotMatchesTagFilter(tags []string, filter string, t *testing.T) { if matchesTags(filter, tags) { t.Errorf(`expected tags: %v not to match tag filter "%s", but it did`, tags, filter) } } -func assertMatchesTagFilter(tags []*messages.Pickle_PickleTag, filter string, t *testing.T) { +func assertMatchesTagFilter(tags []string, filter string, t *testing.T) { if !matchesTags(filter, tags) { t.Errorf(`expected tags: %v to match tag filter "%s", but it did not`, tags, filter) } } func TestTagFilter(t *testing.T) { - assertMatchesTagFilter([]*tag{{Name: "wip"}}, "@wip", t) - assertMatchesTagFilter([]*tag{}, "~@wip", t) - assertMatchesTagFilter([]*tag{{Name: "one"}, {Name: "two"}}, "@two,@three", t) - assertMatchesTagFilter([]*tag{{Name: "one"}, {Name: "two"}}, "@one&&@two", t) - assertMatchesTagFilter([]*tag{{Name: "one"}, {Name: "two"}}, "one && two", 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([]*tag{}, "@wip", t) - assertNotMatchesTagFilter([]*tag{{Name: "one"}, {Name: "two"}}, "@one&&~@two", t) - assertNotMatchesTagFilter([]*tag{{Name: "one"}, {Name: "two"}}, "@one && ~@two", t) + assertNotMatchesTagFilter([]string{}, "@wip", t) + assertNotMatchesTagFilter([]string{"one", "two"}, "@one&&~@two", t) + assertNotMatchesTagFilter([]string{"one", "two"}, "@one && ~@two", t) } - -type tag = messages.Pickle_PickleTag