Skip to content

Commit 4c33d8b

Browse files
committed
output steps
1 parent 7981c70 commit 4c33d8b

File tree

6 files changed

+150
-19
lines changed

6 files changed

+150
-19
lines changed

javascript/package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

javascript/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"dependencies": {
2323
"@cucumber/gherkin-utils": "^9.0.0",
2424
"@cucumber/query": "^12.2.0",
25+
"@teppeis/multimaps": "^3.0.0",
2526
"xmlbuilder": "^15.1.1"
2627
},
2728
"devDependencies": {

javascript/src/ExtendedQuery.ts

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,56 @@ import * as assert from 'node:assert'
22

33
import {
44
Envelope,
5+
Feature,
56
getWorstTestStepResult,
7+
GherkinDocument,
68
Pickle,
9+
PickleStep,
10+
Step,
711
TestCase,
812
TestCaseFinished,
913
TestCaseStarted,
1014
TestRunFinished,
1115
TestRunStarted,
16+
TestStep,
17+
TestStepFinished,
1218
TestStepResultStatus,
1319
TimeConversion,
1420
} from '@cucumber/messages'
15-
import { Query as CucumberQuery } from '@cucumber/query'
21+
import { ArrayMultimap } from '@teppeis/multimaps'
1622

17-
export class ExtendedQuery extends CucumberQuery {
23+
export class ExtendedQuery {
1824
private testRunStarted: TestRunStarted
1925
private testRunFinished: TestRunFinished
2026
private testCaseStarted: Array<TestCaseStarted> = []
27+
private readonly stepById: Map<string, Step> = new Map()
2128
private readonly pickleById: Map<string, Pickle> = new Map()
29+
private readonly pickleStepById: Map<string, PickleStep> = new Map()
2230
private readonly testCaseById: Map<string, TestCase> = new Map()
31+
private readonly testStepById: Map<string, TestStep> = new Map()
2332
private readonly testCaseFinishedByTestCaseStartedId: Map<string, TestCaseFinished> = new Map()
33+
private readonly testStepFinishedByTestCaseStartedId: ArrayMultimap<string, TestStepFinished> =
34+
new ArrayMultimap()
2435

2536
update(envelope: Envelope) {
26-
super.update(envelope)
27-
37+
if (envelope.gherkinDocument) {
38+
this.updateGherkinDocument(envelope.gherkinDocument)
39+
}
2840
if (envelope.pickle) {
29-
this.pickleById.set(envelope.pickle.id, envelope.pickle)
41+
this.updatePickle(envelope.pickle)
3042
}
3143
if (envelope.testRunStarted) {
3244
this.testRunStarted = envelope.testRunStarted
3345
}
3446
if (envelope.testCase) {
35-
this.testCaseById.set(envelope.testCase.id, envelope.testCase)
47+
this.updateTestCase(envelope.testCase)
3648
}
3749
if (envelope.testCaseStarted) {
3850
this.updateTestCaseStarted(envelope.testCaseStarted)
3951
}
52+
if (envelope.testStepFinished) {
53+
this.updateTestStepFinished(envelope.testStepFinished)
54+
}
4055
if (envelope.testCaseFinished) {
4156
this.updateTestCaseFinished(envelope.testCaseFinished)
4257
}
@@ -45,6 +60,47 @@ export class ExtendedQuery extends CucumberQuery {
4560
}
4661
}
4762

63+
private updateGherkinDocument(gherkinDocument: GherkinDocument) {
64+
if (gherkinDocument.feature) {
65+
this.updateFeature(gherkinDocument.feature)
66+
}
67+
}
68+
69+
private updateFeature(feature: Feature) {
70+
feature.children.forEach((featureChild) => {
71+
if (featureChild.background) {
72+
this.updateSteps(featureChild.background.steps)
73+
}
74+
if (featureChild.scenario) {
75+
this.updateSteps(featureChild.scenario.steps)
76+
}
77+
if (featureChild.rule) {
78+
featureChild.rule.children.forEach((ruleChild) => {
79+
if (ruleChild.background) {
80+
this.updateSteps(ruleChild.background.steps)
81+
}
82+
if (ruleChild.scenario) {
83+
this.updateSteps(ruleChild.scenario.steps)
84+
}
85+
})
86+
}
87+
})
88+
}
89+
90+
private updateSteps(steps: ReadonlyArray<Step>) {
91+
steps.forEach((step) => this.stepById.set(step.id, step))
92+
}
93+
94+
private updatePickle(pickle: Pickle) {
95+
this.pickleById.set(pickle.id, pickle)
96+
pickle.steps.forEach((pickleStep) => this.pickleStepById.set(pickleStep.id, pickleStep))
97+
}
98+
99+
private updateTestCase(testCase: TestCase) {
100+
this.testCaseById.set(testCase.id, testCase)
101+
testCase.testSteps.forEach((testStep) => this.testStepById.set(testStep.id, testStep))
102+
}
103+
48104
private updateTestCaseStarted(testCaseStarted: TestCaseStarted) {
49105
// ensure this replaces any previous attempt for the same test case
50106
this.testCaseStarted = [
@@ -55,13 +111,26 @@ export class ExtendedQuery extends CucumberQuery {
55111
]
56112
}
57113

114+
private updateTestStepFinished(testStepFinished: TestStepFinished) {
115+
this.testStepFinishedByTestCaseStartedId.put(
116+
testStepFinished.testCaseStartedId,
117+
testStepFinished
118+
)
119+
}
120+
58121
private updateTestCaseFinished(testCaseFinished: TestCaseFinished) {
59122
this.testCaseFinishedByTestCaseStartedId.set(
60123
testCaseFinished.testCaseStartedId,
61124
testCaseFinished
62125
)
63126
}
64127

128+
findStepBy(pickleStep: PickleStep) {
129+
const [astNodeId] = pickleStep.astNodeIds
130+
assert.ok('Expected PickleStep to have an astNodeId')
131+
return this.stepById.get(astNodeId)
132+
}
133+
65134
findPickleBy(testCaseStarted: TestCaseStarted) {
66135
const testCase = this.findTestCaseBy(testCaseStarted)
67136
if (!testCase) {
@@ -70,6 +139,11 @@ export class ExtendedQuery extends CucumberQuery {
70139
return this.pickleById.get(testCase.pickleId)
71140
}
72141

142+
findPickleStepBy(testStep: TestStep) {
143+
assert.ok(testStep.pickleStepId, 'Expected TestStep to have a pickleStepId')
144+
return this.pickleStepById.get(testStep.pickleStepId)
145+
}
146+
73147
findAllTestCaseStarted(): ReadonlyArray<TestCaseStarted> {
74148
return [...this.testCaseStarted]
75149
}
@@ -93,6 +167,22 @@ export class ExtendedQuery extends CucumberQuery {
93167
)
94168
}
95169

170+
findTestStepBy(testStepFinished: TestStepFinished) {
171+
return this.testStepById.get(testStepFinished.testStepId)
172+
}
173+
174+
findTestStepFinishedAndTestStepBy(
175+
testCaseStarted: TestCaseStarted
176+
): ReadonlyArray<[TestStepFinished, TestStep]> {
177+
return this.testStepFinishedByTestCaseStartedId
178+
.get(testCaseStarted.id)
179+
.map((testStepFinished) => {
180+
const testStep = this.findTestStepBy(testStepFinished)
181+
assert.ok(testStep, 'Expected to find TestStep by TestStepFinished')
182+
return [testStepFinished, testStep]
183+
})
184+
}
185+
96186
findTestRunDuration() {
97187
if (!this.testRunStarted || !this.testRunFinished) {
98188
return undefined
@@ -114,13 +204,11 @@ export class ExtendedQuery extends CucumberQuery {
114204
[TestStepResultStatus.UNKNOWN]: 0,
115205
}
116206
for (const testCaseStarted of this.testCaseStarted) {
117-
const testCase = this.findTestCaseBy(testCaseStarted)
118-
assert.ok(testCase, 'Expected to find TestCase for TestCaseStarted')
119-
const statusesFromSteps = testCase.testSteps.map((testStep) => {
120-
return getWorstTestStepResult(this.getTestStepResults(testStep.id))
121-
})
122-
const overallStatus = getWorstTestStepResult(statusesFromSteps)
123-
result[overallStatus.status]++
207+
const testStepResults = this.findTestStepFinishedAndTestStepBy(testCaseStarted).map(
208+
([testStepFinished]) => testStepFinished.testStepResult
209+
)
210+
const mostSevereResult = getWorstTestStepResult(testStepResults)
211+
result[mostSevereResult.status]++
124212
}
125213
return result
126214
}

javascript/src/helpers.spec.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { TestStepResultStatus } from '@cucumber/messages'
1+
import { PickleStep, Step, TestStepResultStatus } from '@cucumber/messages'
22
import { expect, use } from 'chai'
33
import chaiAlmost from 'chai-almost'
44

5-
import { countStatuses, durationToSeconds } from './helpers.js'
5+
import { countStatuses, durationToSeconds, formatStep } from './helpers.js'
66

77
use(chaiAlmost())
88

@@ -43,4 +43,16 @@ describe('helpers', () => {
4343
)
4444
})
4545
})
46+
47+
describe('formatStep', () => {
48+
it('formats a step', () => {
49+
expect(
50+
formatStep(
51+
{ keyword: 'Given ' } as Step,
52+
{ text: 'I have 42 cukes in my belly' } as PickleStep,
53+
TestStepResultStatus.PASSED
54+
)
55+
).to.eq('Given I have 42 cukes in my belly...........................................passed')
56+
})
57+
})
4658
})

javascript/src/helpers.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { Duration, TestStepResultStatus, TimeConversion } from '@cucumber/messages'
1+
import {
2+
Duration,
3+
PickleStep,
4+
Step,
5+
TestStepResultStatus,
6+
TimeConversion,
7+
} from '@cucumber/messages'
28

39
export function durationToSeconds(duration?: Duration) {
410
if (!duration) {
@@ -15,3 +21,11 @@ export function countStatuses(
1521
.filter(([status]) => predicate(status as TestStepResultStatus))
1622
.reduce((prev, [, curr]) => prev + curr, 0)
1723
}
24+
25+
export function formatStep(step: Step, pickleStep: PickleStep, status: TestStepResultStatus) {
26+
let text = `${step.keyword.trim()} ${pickleStep.text.trim()}.`
27+
do {
28+
text += '.'
29+
} while (text.length < 76)
30+
return text + status.toLowerCase()
31+
}

javascript/src/index.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import * as assert from 'node:assert'
2+
13
import { Query as GherkinQuery } from '@cucumber/gherkin-utils'
2-
import { Envelope, TestCaseStarted, TestStepResultStatus } from '@cucumber/messages'
4+
import { Envelope, TestStepResultStatus } from '@cucumber/messages'
35
import xmlbuilder from 'xmlbuilder'
46

57
import { ExtendedQuery } from './ExtendedQuery.js'
6-
import { countStatuses, durationToSeconds } from './helpers.js'
7-
import * as assert from 'node:assert'
8+
import { countStatuses, durationToSeconds, formatStep } from './helpers.js'
89

910
export default {
1011
type: 'formatter',
@@ -39,6 +40,7 @@ export default {
3940
name: testCase.name,
4041
time: testCase.time,
4142
})
43+
element.ele('system-out', {}).cdata(testCase.output)
4244
}
4345

4446
write(builder.end({ pretty: true }))
@@ -60,6 +62,7 @@ interface TestCase {
6062
classname: string
6163
name: string
6264
time: number
65+
output: string
6366
}
6467

6568
function makeReport(query: ExtendedQuery): Report {
@@ -85,6 +88,18 @@ function makeTestCases(query: ExtendedQuery): ReadonlyArray<TestCase> {
8588
classname: pickle.uri,
8689
name: pickle.name,
8790
time: durationToSeconds(query.findTestCaseDurationBy(testCaseStarted)),
91+
output: query
92+
.findTestStepFinishedAndTestStepBy(testCaseStarted)
93+
// filter out hooks
94+
.filter(([, testStep]) => !!testStep.pickleStepId)
95+
.map(([testStepFinished, testStep]) => {
96+
const pickleStep = query.findPickleStepBy(testStep)
97+
assert.ok(pickleStep, 'Expected to find PickleStep by TestStep')
98+
const gherkinStep = query.findStepBy(pickleStep)
99+
assert.ok(gherkinStep, 'Expected to find Step by PickleStep')
100+
return formatStep(gherkinStep, pickleStep, testStepFinished.testStepResult.status)
101+
})
102+
.join('\n'),
88103
}
89104
})
90105
}

0 commit comments

Comments
 (0)