Этот коммит содержится в:
Stettler, Robert 2017-03-02 10:50:30 -05:00
родитель 7a4d079c2a
коммит 819ce03090
3 изменённых файлов: 594 добавлений и 42 удалений

551
features/formatter/cucumber.feature Обычный файл
Просмотреть файл

@ -0,0 +1,551 @@
Feature: cucumber json formatter
In order to support tools that import cucumber json output
I need to be able to support cucumber json formatted output
Scenario: Support of Feature Plus Scenario Node
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Scenario: simple scenario
simple scenario description
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
""" application/json
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple scenario description",
"line": 3,
"type": "scenario"
}
]
}
]
"""
Scenario: Support of Feature Plus Scenario Node With Tags
Given a feature "features/simple.feature" file:
"""
@TAG1
Feature: simple feature
simple feature description
@TAG2 @TAG3
Scenario: simple scenario
simple scenario description
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
""" application/json
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 2,
"tags": [
{
"name": "@TAG1",
"line": 1
}
],
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple scenario description",
"line": 5,
"type": "scenario",
"tags": [
{
"name": "@TAG1",
"line": 1
},
{
"name": "@TAG2",
"line": 4
},
{
"name": "@TAG3",
"line": 4
}
]
}
]
}
]
"""
Scenario: Support of Feature Plus Scenario Outline
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Scenario Outline: simple scenario
simple scenario description
Examples: simple examples
| status |
| pass |
| fail |
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario;simple-examples;2",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 9,
"type": "scenario"
},
{
"id": "simple-feature;simple-scenario;simple-examples;3",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 10,
"type": "scenario"
}
]
}
]
"""
Scenario: Support of Feature Plus Scenario Outline With Tags
Given a feature "features/simple.feature" file:
"""
@TAG1
Feature: simple feature
simple feature description
@TAG2
Scenario Outline: simple scenario
simple scenario description
@TAG3
Examples: simple examples
| status |
| pass |
| fail |
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 2,
"tags": [
{
"name": "@TAG1",
"line": 1
}
],
"elements": [
{
"id": "simple-feature;simple-scenario;simple-examples;2",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 12,
"type": "scenario",
"tags": [
{
"name": "@TAG1",
"line": 1
},
{
"name": "@TAG2",
"line": 5
},
{
"name": "@TAG3",
"line": 9
}
]
},
{
"id": "simple-feature;simple-scenario;simple-examples;3",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 13,
"type": "scenario",
"tags": [
{
"name": "@TAG1",
"line": 1
},
{
"name": "@TAG2",
"line": 5
},
{
"name": "@TAG3",
"line": 9
}
]
}
]
}
]
"""
Scenario: Support of Feature Plus Scenario With Steps
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Scenario: simple scenario
simple scenario description
Given passing step
Then a failing step
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple scenario description",
"line": 4,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "passing step",
"line": 7,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "passed",
"duration": -1
}
},
{
"keyword": "Then ",
"name": "a failing step",
"line": 8,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "failed",
"error_message": "intentional failure",
"duration": -1
}
}
]
}
]
}
]
"""
Scenario: Support of Feature Plus Scenario Outline With Steps
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Scenario Outline: simple scenario
simple scenario description
Given <status> step
Examples: simple examples
| status |
| passing |
| failing |
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario;simple-examples;2",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 11,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "passing step",
"line": 11,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "passed",
"duration": -1
}
}
]
},
{
"id": "simple-feature;simple-scenario;simple-examples;3",
"keyword": "Scenario Outline",
"name": "simple scenario",
"description": " simple scenario description",
"line": 12,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "failing step",
"line": 12,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "failed",
"error_message": "intentional failure",
"duration": -1
}
}
]
}
]
}
]
"""
# Currently godog only supports comments on Feature and not
# scenario and steps.
Scenario: Support of Comments
Given a feature "features/simple.feature" file:
"""
#Feature comment
Feature: simple feature
simple description
Scenario: simple scenario
simple feature description
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple description",
"line": 2,
"comments": [
{
"value": "#Feature comment",
"line": 1
}
],
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple feature description",
"line": 5,
"type": "scenario"
}
]
}
]
"""
Scenario: Support of Docstrings
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple description
Scenario: simple scenario
simple feature description
Given passing step
\"\"\" content type
step doc string
\"\"\"
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple feature description",
"line": 4,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "passing step",
"line": 7,
"doc_string": {
"value": "step doc string",
"content_type": "content type",
"line": 8
},
"match": {
"location": "STEP_ID"
},
"result": {
"status": "passed",
"duration": -1
}
}
]
}
]
}
]
"""
Scenario: Support of Undefined, Pending and Skipped status
Given a feature "features/simple.feature" file:
"""
Feature: simple feature
simple feature description
Scenario: simple scenario
simple scenario description
Given passing step
And pending step
And undefined
And passing step
"""
When I run feature suite with formatter "cucumber"
Then the rendered json will be as follows:
"""
[
{
"uri": "features/simple.feature",
"id": "simple-feature",
"keyword": "Feature",
"name": "simple feature",
"description": " simple feature description",
"line": 1,
"elements": [
{
"id": "simple-feature;simple-scenario",
"keyword": "Scenario",
"name": "simple scenario",
"description": " simple scenario description",
"line": 4,
"type": "scenario",
"steps": [
{
"keyword": "Given ",
"name": "passing step",
"line": 7,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "passed",
"duration": 397087
}
},
{
"keyword": "And ",
"name": "pending step",
"line": 8,
"match": {
"location": "FEATURE_PATH features/simple.feature:8"
},
"result": {
"status": "pending"
}
},
{
"keyword": "And ",
"name": "undefined",
"line": 9,
"match": {
"location": "FEATURE_PATH features/simple.feature:9"
},
"result": {
"status": "undefined"
}
},
{
"keyword": "And ",
"name": "passing step",
"line": 10,
"match": {
"location": "STEP_ID"
},
"result": {
"status": "skipped"
}
}
]
}
]
}
]
"""

Просмотреть файл

@ -157,7 +157,7 @@ func (f *cukefmt) Node(n interface{}) {
f.curOutline.Keyword = t.Keyword f.curOutline.Keyword = t.Keyword
f.curOutline.ID = f.curFeature.ID + ";" + makeID(t.Name) f.curOutline.ID = f.curFeature.ID + ";" + makeID(t.Name)
f.curOutline.Type = "scenario" f.curOutline.Type = "scenario"
f.curOutline.Tags = make([]cukeTag, len(t.Tags) + len(f.curFeature.Tags)) f.curOutline.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags))
// apply feature level tags // apply feature level tags
if len(f.curOutline.Tags) > 0 { if len(f.curOutline.Tags) > 0 {
@ -165,15 +165,15 @@ func (f *cukefmt) Node(n interface{}) {
// apply outline level tags. // apply outline level tags.
for idx, element := range t.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)].Line = element.Location.Line
f.curOutline.Tags[idx + len(f.curFeature.Tags)].Name = element.Name f.curOutline.Tags[idx+len(f.curFeature.Tags)].Name = element.Name
} }
} }
// This scenario adds the element to the output immediately. // This scenario adds the element to the output immediately.
case *gherkin.Scenario: case *gherkin.Scenario:
f.curFeature.Elements = append(f.curFeature.Elements, cukeElement{}) f.curFeature.Elements = append(f.curFeature.Elements, cukeElement{})
f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements) - 1] f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1]
f.curElement.Name = t.Name f.curElement.Name = t.Name
f.curElement.Line = t.Location.Line f.curElement.Line = t.Location.Line
@ -181,7 +181,7 @@ func (f *cukefmt) Node(n interface{}) {
f.curElement.Keyword = t.Keyword f.curElement.Keyword = t.Keyword
f.curElement.ID = f.curFeature.ID + ";" + makeID(t.Name) f.curElement.ID = f.curFeature.ID + ";" + makeID(t.Name)
f.curElement.Type = "scenario" f.curElement.Type = "scenario"
f.curElement.Tags = make([]cukeTag, len(t.Tags) + len(f.curFeature.Tags)) f.curElement.Tags = make([]cukeTag, len(t.Tags)+len(f.curFeature.Tags))
if len(f.curElement.Tags) > 0 { if len(f.curElement.Tags) > 0 {
// apply feature level tags // apply feature level tags
@ -189,8 +189,8 @@ func (f *cukefmt) Node(n interface{}) {
// apply scenario level tags. // apply scenario level tags.
for idx, element := range t.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)].Line = element.Location.Line
f.curElement.Tags[idx + len(f.curFeature.Tags)].Name = element.Name f.curElement.Tags[idx+len(f.curFeature.Tags)].Name = element.Name
} }
} }
@ -202,7 +202,7 @@ func (f *cukefmt) Node(n interface{}) {
tmpElem.ID = tmpElem.ID + ";" + f.curExampleName + ";" + strconv.Itoa(f.curRow) tmpElem.ID = tmpElem.ID + ";" + f.curExampleName + ";" + strconv.Itoa(f.curRow)
f.curRow++ f.curRow++
f.curFeature.Elements = append(f.curFeature.Elements, tmpElem) f.curFeature.Elements = append(f.curFeature.Elements, tmpElem)
f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements) - 1] f.curElement = &f.curFeature.Elements[len(f.curFeature.Elements)-1]
// copy in example level tags. // copy in example level tags.
f.curElement.Tags = append(f.curElement.Tags, f.curExampleTags...) f.curElement.Tags = append(f.curElement.Tags, f.curExampleTags...)
@ -218,7 +218,7 @@ func (f *cukefmt) Feature(ft *gherkin.Feature, p string, c []byte) {
f.ID = makeID(ft.Name) f.ID = makeID(ft.Name)
f.results = append(f.results, cukeFeatureJSON{}) f.results = append(f.results, cukeFeatureJSON{})
f.curFeature = &f.results[len(f.results) - 1] f.curFeature = &f.results[len(f.results)-1]
f.curFeature.URI = p f.curFeature.URI = p
f.curFeature.Name = ft.Name f.curFeature.Name = ft.Name
f.curFeature.Keyword = ft.Keyword f.curFeature.Keyword = ft.Keyword
@ -274,7 +274,7 @@ func (f *cukefmt) Defined(step *gherkin.Step, def *StepDef) {
f.startTime = time.Now() // start timing the step f.startTime = time.Now() // start timing the step
f.curElement.Steps = append(f.curElement.Steps, cukeStep{}) f.curElement.Steps = append(f.curElement.Steps, cukeStep{})
f.curStep = &f.curElement.Steps[len(f.curElement.Steps) - 1] f.curStep = &f.curElement.Steps[len(f.curElement.Steps)-1]
f.curStep.Name = step.Text f.curStep.Name = step.Text
f.curStep.Line = step.Location.Line f.curStep.Line = step.Location.Line
@ -295,12 +295,12 @@ func (f *cukefmt) Defined(step *gherkin.Step, def *StepDef) {
func (f *cukefmt) Passed(step *gherkin.Step, match *StepDef) { func (f *cukefmt) Passed(step *gherkin.Step, match *StepDef) {
f.basefmt.Passed(step, match) f.basefmt.Passed(step, match)
f.stat = passed f.stat = passed
f.step(f.passed[len(f.passed) - 1]) f.step(f.passed[len(f.passed)-1])
} }
func (f *cukefmt) Skipped(step *gherkin.Step) { func (f *cukefmt) Skipped(step *gherkin.Step) {
f.basefmt.Skipped(step) f.basefmt.Skipped(step)
f.step(f.skipped[len(f.skipped) - 1]) f.step(f.skipped[len(f.skipped)-1])
// no duration reported for skipped. // no duration reported for skipped.
f.curStep.Result.Duration = nil f.curStep.Result.Duration = nil
@ -309,7 +309,7 @@ func (f *cukefmt) Skipped(step *gherkin.Step) {
func (f *cukefmt) Undefined(step *gherkin.Step) { func (f *cukefmt) Undefined(step *gherkin.Step) {
f.basefmt.Undefined(step) f.basefmt.Undefined(step)
f.stat = undefined f.stat = undefined
f.step(f.undefined[len(f.undefined) - 1]) f.step(f.undefined[len(f.undefined)-1])
// the location for undefined is the feature file location not the step file. // the location for undefined is the feature file location not the step file.
f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line) f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line)
@ -319,13 +319,13 @@ func (f *cukefmt) Undefined(step *gherkin.Step) {
func (f *cukefmt) Failed(step *gherkin.Step, match *StepDef, err error) { func (f *cukefmt) Failed(step *gherkin.Step, match *StepDef, err error) {
f.basefmt.Failed(step, match, err) f.basefmt.Failed(step, match, err)
f.stat = failed f.stat = failed
f.step(f.failed[len(f.failed) - 1]) f.step(f.failed[len(f.failed)-1])
} }
func (f *cukefmt) Pending(step *gherkin.Step, match *StepDef) { func (f *cukefmt) Pending(step *gherkin.Step, match *StepDef) {
f.stat = pending f.stat = pending
f.basefmt.Pending(step, match) f.basefmt.Pending(step, match)
f.step(f.pending[len(f.pending) - 1]) f.step(f.pending[len(f.pending)-1])
// the location for pending is the feature file location not the step file. // the location for pending is the feature file location not the step file.
f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line) f.curStep.Match.Location = fmt.Sprintf("%s:%d", f.path, step.Location.Line)

Просмотреть файл

@ -366,7 +366,7 @@ func (s *suiteContext) thereWasEventTriggeredBeforeScenario(expected string) err
return fmt.Errorf("before scenario event was never triggered or listened") return fmt.Errorf("before scenario event was never triggered or listened")
} }
return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, expected, `"` + strings.Join(found, `", "`) + `"`) return fmt.Errorf(`expected "%s" scenario, but got these fired %s`, expected, `"`+strings.Join(found, `", "`)+`"`)
} }
func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *gherkin.DataTable) error { func (s *suiteContext) theseEventsHadToBeFiredForNumberOfTimes(tbl *gherkin.DataTable) error {
@ -503,6 +503,7 @@ func (s *suiteContext) mapCompare(expected map[string]interface{}, actual map[st
return nil return nil
} }
/* /*
Due to specialize matching logic to ignore exact matches on the "location" and "duration" fields. It was Due to specialize matching logic to ignore exact matches on the "location" and "duration" fields. It was
necessary to create this compare function to validate the values of the map. necessary to create this compare function to validate the values of the map.