Skip to content

Commit

Permalink
format: report total steps correctly in progress bar (#1669)
Browse files Browse the repository at this point in the history
* make cck fail: remove reordering of testCase messages

* add new function to deal with testCase

* dont emit testCase from PickleRunner

* include in result

* fix up some tests

* move tests to right places

* emit test cases from serial runtime

* scrappy impl to get serial working

* remove unused field

* refactor structures, fix tests

* make coordinator api more promisey

* start to hook up parallel

* assemble test cases without ITestStep

* remove unused function

* TestCase is source of truth

* TestCaseRunner is more accurate than PickleRunner?

* make parallel runtime work with this

* add explanatory comment

* fix progress bar formatter counts

* changelog

* remove temp tag

Co-authored-by: Aurélien Reeves <[email protected]>

* clarify changelog entry audience

* cleanup

Co-authored-by: Aurélien Reeves <[email protected]>
  • Loading branch information
davidjgoss and aurelien-reeves authored Jun 1, 2021
1 parent ee90922 commit 226f0fb
Show file tree
Hide file tree
Showing 16 changed files with 732 additions and 437 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO

### Changed

* All `testCase` messages now emitted upfront at the start of the run (relevant for formatter authors) ([#1408](https://github.com/cucumber/cucumber-js/issues/1408)
[#1669](https://github.com/cucumber/cucumber-js/pull/1669))
* Clarify that the JSON formatter will not be removed any time soon

### Deprecated
Expand All @@ -24,6 +26,8 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO

### Fixed

* Progress bar formatter now reports total step count correctly ([#1579](https://github.com/cucumber/cucumber-js/issues/1579)
[#1669](https://github.com/cucumber/cucumber-js/pull/1669))
* All messages now emitted with project-relative `uri`s
([#1534](https://github.com/cucumber/cucumber-js/issues/1534)
[#1672](https://github.com/cucumber/cucumber-js/pull/1672))
Expand Down
14 changes: 1 addition & 13 deletions compatibility/cck_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import path from 'path'
import { PassThrough, pipeline, Writable } from 'stream'
import { Cli } from '../src'
import toString from 'stream-to-string'
import { doesHaveValue, doesNotHaveValue } from '../src/value_checker'
import { normalizeMessageOutput } from '../features/support/formatter_output_helpers'
import * as messages from '@cucumber/messages'
import * as messageStreams from '@cucumber/message-streams'
Expand Down Expand Up @@ -103,16 +102,5 @@ function normalize(messages: any[]): any[] {
messages,
path.join(PROJECT_PATH, 'compatibility')
)
const testCases: any[] = messages.filter((message) =>
doesHaveValue(message.testCase)
)
const everythingElse: any[] = messages.filter((message) =>
doesNotHaveValue(message.testCase)
)
const testRunStarted = everythingElse.findIndex((message) =>
doesHaveValue(message.testRunStarted)
)
// move all `testCase` messages to just after `testRunStarted`
everythingElse.splice(testRunStarted + 1, 0, ...testCases)
return everythingElse
return messages
}
8 changes: 2 additions & 6 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,17 +228,13 @@ export default class Cli {
eventBroadcaster,
eventDataCollector,
options: configuration.runtimeOptions,
newId,
pickleIds,
supportCodeLibrary,
supportCodePaths: configuration.supportCodePaths,
supportCodeRequiredModules: configuration.supportCodeRequiredModules,
})
await new Promise<void>((resolve) => {
parallelRuntimeCoordinator.run(configuration.parallel, (s) => {
success = s
resolve()
})
})
success = await parallelRuntimeCoordinator.run(configuration.parallel)
} else {
const runtime = new Runtime({
eventBroadcaster,
Expand Down
4 changes: 2 additions & 2 deletions src/formatter/progress_bar_formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ export default class ProgressBarFormatter extends Formatter {
parseEnvelope(envelope: messages.Envelope): void {
if (doesHaveValue(envelope.undefinedParameterType)) {
this.logUndefinedParametertype(envelope.undefinedParameterType)
} else if (doesHaveValue(envelope.pickle)) {
this.incrementStepCount(envelope.pickle.id)
} else if (doesHaveValue(envelope.testCase)) {
this.incrementStepCount(envelope.testCase.pickleId)
} else if (doesHaveValue(envelope.testStepStarted)) {
this.initializeProgressBar()
} else if (doesHaveValue(envelope.testStepFinished)) {
Expand Down
29 changes: 29 additions & 0 deletions src/formatter/progress_bar_formatter_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface ITestProgressBarFormatterOptions {
shouldStopFn: (envelope: messages.Envelope) => boolean
sources?: ITestSource[]
supportCodeLibrary?: ISupportCodeLibrary
pickleFilter?: (pickle: messages.Pickle) => boolean
}

interface ITestProgressBarFormatterOutput {
Expand All @@ -39,6 +40,7 @@ async function testProgressBarFormatter({
shouldStopFn,
sources,
supportCodeLibrary,
pickleFilter = () => true,
}: ITestProgressBarFormatterOptions): Promise<ITestProgressBarFormatterOutput> {
if (doesNotHaveValue(supportCodeLibrary)) {
supportCodeLibrary = buildSupportCodeLibrary()
Expand All @@ -48,6 +50,7 @@ async function testProgressBarFormatter({
runtimeOptions,
sources,
supportCodeLibrary,
pickleFilter,
})

let output = ''
Expand Down Expand Up @@ -137,6 +140,32 @@ describe('ProgressBarFormatter', () => {
// Assert
expect(progressBarFormatter.progressBar.total).to.eql(5)
})

it('initializes a progress bar with the total number of steps when some pickles filtered out', async () => {
// Arrange
const sources = [
{
data: '@yep\nFeature: a\nScenario: b\nGiven a step\nThen a step',
uri: 'a.feature',
},
{
data:
'Feature: a\nScenario: b\nGiven a step\nWhen a step\nThen a step',
uri: 'b.feature',
},
]

// Act
const { progressBarFormatter } = await testProgressBarFormatter({
shouldStopFn: (envelope) => doesHaveValue(envelope.testStepStarted),
sources,
pickleFilter: (pickle) =>
pickle.tags.some((tag) => tag.name === '@yep'),
})

// Assert
expect(progressBarFormatter.progressBar.total).to.eql(2)
})
})

describe('testStepFinished', () => {
Expand Down
135 changes: 135 additions & 0 deletions src/runtime/assemble_test_cases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { EventEmitter } from 'events'
import * as messages from '@cucumber/messages'
import { IdGenerator } from '@cucumber/messages'
import { ISupportCodeLibrary } from '../support_code_library_builder/types'
import { Group } from '@cucumber/cucumber-expressions'
import { doesHaveValue } from '../value_checker'
import { clone } from 'lodash'

export declare type IAssembledTestCases = Record<string, messages.TestCase>

export interface IAssembleTestCasesOptions {
eventBroadcaster: EventEmitter
newId: IdGenerator.NewId
pickles: messages.Pickle[]
supportCodeLibrary: ISupportCodeLibrary
}

export async function assembleTestCases({
eventBroadcaster,
newId,
pickles,
supportCodeLibrary,
}: IAssembleTestCasesOptions): Promise<IAssembledTestCases> {
const result: IAssembledTestCases = {}
for (const pickle of pickles) {
const { id: pickleId } = pickle
const testCaseId = newId()
const fromBeforeHooks: messages.TestStep[] = makeBeforeHookSteps({
supportCodeLibrary,
pickle,
newId,
})
const fromStepDefinitions: messages.TestStep[] = makeSteps({
pickle,
supportCodeLibrary,
newId,
})
const fromAfterHooks: messages.TestStep[] = makeAfterHookSteps({
supportCodeLibrary,
pickle,
newId,
})
const testCase: messages.TestCase = {
pickleId,
id: testCaseId,
testSteps: [
...fromBeforeHooks,
...fromStepDefinitions,
...fromAfterHooks,
],
}
eventBroadcaster.emit('envelope', { testCase })
result[pickleId] = testCase
}
return result
}

function makeAfterHookSteps({
supportCodeLibrary,
pickle,
newId,
}: {
supportCodeLibrary: ISupportCodeLibrary
pickle: messages.Pickle
newId: IdGenerator.NewId
}): messages.TestStep[] {
return clone(supportCodeLibrary.afterTestCaseHookDefinitions)
.reverse()
.filter((hookDefinition) => hookDefinition.appliesToTestCase(pickle))
.map((hookDefinition) => ({
id: newId(),
hookId: hookDefinition.id,
}))
}

function makeBeforeHookSteps({
supportCodeLibrary,
pickle,
newId,
}: {
supportCodeLibrary: ISupportCodeLibrary
pickle: messages.Pickle
newId: IdGenerator.NewId
}): messages.TestStep[] {
return supportCodeLibrary.beforeTestCaseHookDefinitions
.filter((hookDefinition) => hookDefinition.appliesToTestCase(pickle))
.map((hookDefinition) => ({
id: newId(),
hookId: hookDefinition.id,
}))
}

function makeSteps({
pickle,
supportCodeLibrary,
newId,
}: {
pickle: messages.Pickle
supportCodeLibrary: ISupportCodeLibrary
newId: () => string
}): messages.TestStep[] {
return pickle.steps.map((pickleStep) => {
const stepDefinitions = supportCodeLibrary.stepDefinitions.filter(
(stepDefinition) => stepDefinition.matchesStepName(pickleStep.text)
)
return {
id: newId(),
pickleStepId: pickleStep.id,
stepDefinitionIds: stepDefinitions.map(
(stepDefinition) => stepDefinition.id
),
stepMatchArgumentsLists: stepDefinitions.map((stepDefinition) => {
const result = stepDefinition.expression.match(pickleStep.text)
return {
stepMatchArguments: result.map((arg) => {
return {
group: mapArgumentGroup(arg.group),
parameterTypeName: arg.parameterType.name,
}
}),
}
}),
}
})
}

function mapArgumentGroup(group: Group): messages.Group {
return {
start: group.start,
value: group.value,
children: doesHaveValue(group.children)
? group.children.map((child) => mapArgumentGroup(child))
: undefined,
}
}
Loading

0 comments on commit 226f0fb

Please sign in to comment.