Skip to content

Commit

Permalink
Fix #1621: Json formatter fails with tagged examples (#1651)
Browse files Browse the repository at this point in the history
* Add a reproduction scenario for #1621

* Add a unit test

* Fix the json-formatter to make tests pass

* [skip ci]Update changelog
  • Loading branch information
aurelien-reeves authored Apr 26, 2021
1 parent 21c0349 commit 238947f
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 15 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO

### Fixed

* Json formatter now works with tagged examples
([#1621](https://github.com/cucumber/cucumber-js/issues/1621)
[#1651](https://github.com/cucumber/cucumber-js/pull/1651))

## [7.2.1] (2021-04-21)

### Fixed
Expand Down
52 changes: 52 additions & 0 deletions features/scenario_outlines.feature
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,55 @@ Feature: Scenario Outlines and Examples
When I run cucumber-js
Then it fails
And it runs 4 scenarios

Scenario: scenario outlines with tagged examples
Given a file named "features/scenario_outline.feature" with:
"""
Feature: a feature
Scenario Outline: a scenario
Given a step <id>
@examples @tag
Examples:
| id |
| 1 |
| 2 |
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
const {Given} = require('@cucumber/cucumber')
Given('a step {int}', function(int) {})
"""
When I run cucumber-js with `-f json`
Then it passes
And it runs 2 scenarios

Scenario: scenario outlines with multiple example tables
Given a file named "features/scenario_outline.feature" with:
"""
Feature: a feature
Scenario Outline: a scenario
Given a step <id>
@example @id-1-2
Examples:
| id |
| 1 |
| 2 |
@example @id-3-4
Examples:
| id |
| 3 |
| 4 |
"""
And a file named "features/step_definitions/cucumber_steps.js" with:
"""
const {Given} = require('@cucumber/cucumber')
Given('a step {int}', function(int) {})
"""
When I run cucumber-js with `-f json`
Then it passes
And it runs 4 scenarios
44 changes: 29 additions & 15 deletions src/formatter/json_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import ITag = messages.GherkinDocument.Feature.ITag
import IFeature = messages.GherkinDocument.IFeature
import IPickle = messages.IPickle
import IScenario = messages.GherkinDocument.Feature.IScenario
import IPickleTag = messages.Pickle.IPickleTag
import IEnvelope = messages.IEnvelope
import IRule = messages.GherkinDocument.Feature.FeatureChild.IRule

Expand Down Expand Up @@ -314,20 +315,33 @@ export default class JsonFormatter extends Formatter {
pickle: IPickle
gherkinScenarioMap: { [id: string]: IScenario }
}): IJsonTag[] {
return _.map(pickle.tags, (tagData) => {
const featureSource = feature.tags.find(
(t: ITag) => t.id === tagData.astNodeId
)
const scenarioSource = gherkinScenarioMap[pickle.astNodeIds[0]].tags.find(
(t: ITag) => t.id === tagData.astNodeId
)
const line = doesHaveValue(featureSource)
? featureSource.location.line
: scenarioSource.location.line
return {
name: tagData.name,
line,
}
})
const scenario = gherkinScenarioMap[pickle.astNodeIds[0]]

return pickle.tags.map(
(tagData: IPickleTag): IJsonTag =>
this.getScenarioTag(tagData, feature, scenario)
)
}

private getScenarioTag(
tagData: IPickleTag,
feature: IFeature,
scenario: IScenario
): IJsonTag {
const byAstNodeId = (tag: ITag): Boolean => tag.id === tagData.astNodeId
const flatten = (acc: ITag[], val: ITag[]): ITag[] => acc.concat(val)

const tag =
feature.tags.find(byAstNodeId) ||
scenario.tags.find(byAstNodeId) ||
scenario.examples
.map((e) => e.tags)
.reduce(flatten, [])
.find(byAstNodeId)

return {
name: tagData.name,
line: tag?.location?.line,
}
}
}
58 changes: 58 additions & 0 deletions src/formatter/json_formatter_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../test/fixtures/json_formatter_steps'
import FakeTimers, { InstalledClock } from '@sinonjs/fake-timers'
import timeMethods from '../time'
import { IJsonFeature, IJsonScenario } from '../../lib/formatter/json_formatter'

describe('JsonFormatter', () => {
let clock: InstalledClock
Expand Down Expand Up @@ -357,6 +358,63 @@ describe('JsonFormatter', () => {
])
})
})

describe(' with tagged examples', () => {
it('outputs the examples', async () => {
// Arrange
const sources = [
{
data: [
'Feature: my feature',
' Scenario: my scenario',
' Given a step <id>',
'',
' @tag-1-2',
' Examples:',
' |id|',
' | 1|',
' | 2|',
'',
' @tag @tag-3-4',
' Examples:',
' |id|',
' | 3|',
' | 4|',
].join('\n'),
uri: 'a.feature',
},
]

const supportCodeLibrary = getJsonFormatterSupportCodeLibrary(clock)

// Act
const output = await testFormatter({
sources,
supportCodeLibrary,
type: 'json',
})

// Assert
const jsonFeature: IJsonFeature = JSON.parse(output)[0]
const jsonScenarios = jsonFeature.elements
const jsonScenarioTags = jsonScenarios.map((s: IJsonScenario) => s.tags)

const expectedTags = [
[{ line: 5, name: '@tag-1-2' }],
[{ line: 5, name: '@tag-1-2' }],
[
{ line: 11, name: '@tag' },
{ line: 11, name: '@tag-3-4' },
],
[
{ line: 11, name: '@tag' },
{ line: 11, name: '@tag-3-4' },
],
]

expect(jsonScenarioTags).to.eql(expectedTags)
})
})
})

describe('one rule with several examples (scenarios)', () => {
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/json_formatter_steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export function getJsonFormatterSupportCodeLibrary(
Given('a step that attaches', async function (this: World) {
await this.attach(Buffer.from([137, 80, 78, 71]), 'image/png')
})

Given('a step {int}', function (_int: Number) {})
})
}

Expand Down

0 comments on commit 238947f

Please sign in to comment.