Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(cucumber): Corrected how steps are reported for scenarios that us…
…e before/after hooks See cucumber/cucumber-js#1195
- Loading branch information
Showing
17 changed files
with
465 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
267 changes: 267 additions & 0 deletions
267
packages/cucumber/spec/listeners/CucumberEventProtocolAdapter.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
import { EventRecorder, expect, PickEvent } from '@integration/testing-tools'; | ||
import { Serenity } from '@serenity-js/core'; | ||
import { SceneFinished, SceneStarts, SceneTagged, TaskFinished, TaskStarts, TestRunnerDetected } from '@serenity-js/core/lib/events'; | ||
import { FileSystemLocation, Path, Version } from '@serenity-js/core/lib/io'; | ||
import { Category, ExecutionFailedWithError, ExecutionSkipped, ExecutionSuccessful, FeatureTag, ImplementationPending, Name, ScenarioDetails } from '@serenity-js/core/lib/model'; | ||
|
||
import { EventEmitter } from 'events'; | ||
|
||
import * as sinon from 'sinon'; | ||
import { JSONObject } from 'tiny-types'; | ||
import { AmbiguousStepDefinitionError } from '../../src/errors'; | ||
import { listenerForCucumber } from '../../src/listeners'; | ||
|
||
describe('CucumberEventProtocolAdapter', () => { | ||
|
||
type CucumberHook = () => Promise<void> | void; | ||
|
||
const fakeCucumber = { | ||
After: (hook: CucumberHook) => Promise.resolve(hook()), | ||
AfterAll: (hook: CucumberHook) => Promise.resolve(hook()), | ||
}; | ||
|
||
let recorder: EventRecorder, | ||
serenity: Serenity, | ||
log: typeof console.log, | ||
eventBroadcaster: EventEmitter, | ||
adapter: any; | ||
|
||
beforeEach(() => { | ||
|
||
log = sinon.spy(); | ||
serenity = new Serenity(); | ||
recorder = new EventRecorder(); | ||
eventBroadcaster = new EventEmitter(); | ||
|
||
serenity.setTheStage( | ||
recorder, | ||
); | ||
|
||
const listener = listenerForCucumber(new Version('5.0.0'), fakeCucumber, serenity); | ||
|
||
adapter = new listener({ eventBroadcaster, log }); | ||
}); | ||
|
||
it('correctly recognises Cucumber Event Protocol events', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-with-hooks.json')); | ||
|
||
const expectedScenarioDetails = new ScenarioDetails( | ||
new Name('Hooks'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/tasty-cucumber.feature'), | ||
3, | ||
3, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails)) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I have a tasty cucumber in my belly'))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Given I have a tasty cucumber in my belly')); | ||
expect(e.outcome).to.equal(new ExecutionSuccessful()); | ||
}) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name(`Then I'm very happy`))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name(`Then I'm very happy`)); | ||
expect(e.outcome).to.equal(new ExecutionSuccessful()); | ||
}) | ||
.next(SceneFinished, e => { | ||
expect(e.value).to.equal(expectedScenarioDetails); | ||
expect(e.outcome).to.equal(new ExecutionSuccessful()); | ||
}) | ||
; | ||
}); | ||
|
||
it('correctly recognises undefined steps', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-with-undefined-steps.json')); | ||
|
||
const expectedScenarioDetails = new ScenarioDetails( | ||
new Name('Undefined steps'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/undefined-steps.feature'), | ||
3, | ||
3, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails)) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I have an undefined step'))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Given I have an undefined step')); | ||
expect(e.outcome).to.be.instanceOf(ImplementationPending); | ||
}) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name(`Then I should implement it`))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Then I should implement it')); | ||
expect(e.outcome).to.be.instanceOf(ImplementationPending); | ||
}) | ||
.next(SceneFinished, e => { | ||
expect(e.value).to.equal(expectedScenarioDetails); | ||
expect(e.outcome).to.be.instanceOf(ImplementationPending); | ||
}) | ||
; | ||
}); | ||
|
||
it('correctly recognises pending steps', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-with-pending-steps.json')); | ||
|
||
const expectedScenarioDetails = new ScenarioDetails( | ||
new Name('Pending steps'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/pending-steps.feature'), | ||
3, | ||
3, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails)) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I have a pending step'))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Given I have a pending step')); | ||
expect(e.outcome).to.be.instanceOf(ImplementationPending); | ||
}) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name(`Then I should implement it`))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Then I should implement it')); | ||
expect(e.outcome).to.be.instanceOf(ExecutionSkipped); | ||
}) | ||
.next(SceneFinished, e => { | ||
expect(e.value).to.equal(expectedScenarioDetails); | ||
expect(e.outcome).to.be.instanceOf(ImplementationPending); | ||
}) | ||
; | ||
}); | ||
|
||
it('correctly recognises ambiguous steps', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-with-ambiguous-steps.json')); | ||
|
||
const expectedScenarioDetails = new ScenarioDetails( | ||
new Name('Ambiguous steps'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/ambiguous-steps.feature'), | ||
3, | ||
3, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails)) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I have an ambiguous step definition'))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Given I have an ambiguous step definition')); | ||
expect(e.outcome).to.be.instanceOf(ExecutionFailedWithError); | ||
expect((e.outcome as ExecutionFailedWithError).error).to.be.instanceOf(AmbiguousStepDefinitionError); | ||
}) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name(`Then I should correct it`))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Then I should correct it')); | ||
expect(e.outcome).to.be.instanceOf(ExecutionSkipped); | ||
}) | ||
.next(SceneFinished, e => { | ||
expect(e.value).to.equal(expectedScenarioDetails); | ||
expect(e.outcome).to.be.instanceOf(ExecutionFailedWithError); | ||
expect((e.outcome as ExecutionFailedWithError).error).to.be.instanceOf(AmbiguousStepDefinitionError); | ||
}) | ||
; | ||
}); | ||
|
||
it('correctly recognises errors thrown in steps', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-with-errors.json')); | ||
|
||
const expectedScenarioDetails = new ScenarioDetails( | ||
new Name('Errors in steps'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/errors-in-steps.feature'), | ||
3, | ||
3, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails)) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I have a step that throws an error'))) | ||
.next(TaskFinished, e => { | ||
expect(e.value.name).to.equal(new Name('Given I have a step that throws an error')); | ||
expect(e.outcome).to.be.instanceOf(ExecutionFailedWithError); | ||
expect((e.outcome as ExecutionFailedWithError).error).to.be.instanceOf(Error); | ||
expect((e.outcome as ExecutionFailedWithError).error.message).to.equal(`We're sorry, something happened`); | ||
}) | ||
.next(SceneFinished, e => { | ||
expect(e.value).to.equal(expectedScenarioDetails); | ||
expect(e.outcome).to.be.instanceOf(ExecutionFailedWithError); | ||
expect((e.outcome as ExecutionFailedWithError).error).to.be.instanceOf(Error); | ||
expect((e.outcome as ExecutionFailedWithError).error.message).to.equal(`We're sorry, something happened`); | ||
}) | ||
; | ||
}); | ||
|
||
it('correctly recognises scenario outlines', () => { | ||
|
||
emitAllFrom(require('./samples/scenario-outline.json')); | ||
|
||
const expectedScenarioDetails = (line: number) => new ScenarioDetails( | ||
new Name('The things I like'), | ||
new Category('Event Protocol'), | ||
new FileSystemLocation( | ||
new Path('features/outlines.feature'), | ||
line, | ||
7, | ||
), | ||
); | ||
|
||
PickEvent.from(recorder.events) | ||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails(10))) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I like programming'))) | ||
.next(TaskFinished, e => expect(e.value.name).to.equal(new Name('Given I like programming'))) | ||
.next(SceneFinished, e => expect(e.value).to.equal(expectedScenarioDetails(10))) | ||
|
||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails(11))) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I like to play guitar'))) | ||
.next(TaskFinished, e => expect(e.value.name).to.equal(new Name('Given I like to play guitar'))) | ||
.next(SceneFinished, e => expect(e.value).to.equal(expectedScenarioDetails(11))) | ||
|
||
.next(SceneStarts, e => expect(e.value).to.equal(expectedScenarioDetails(12))) | ||
.next(TestRunnerDetected, e => expect(e.value).to.equal(new Name('Cucumber'))) | ||
.next(SceneTagged, e => expect(e.tag).to.equal(new FeatureTag('Event Protocol'))) | ||
.next(TaskStarts, e => expect(e.value.name).to.equal(new Name('Given I like martial arts'))) | ||
.next(TaskFinished, e => expect(e.value.name).to.equal(new Name('Given I like martial arts'))) | ||
.next(SceneFinished, e => expect(e.value).to.equal(expectedScenarioDetails(12))) | ||
; | ||
}); | ||
|
||
function emitAllFrom(events: JSONObject[]): void { | ||
events.forEach(event => { | ||
// I can't use the convenient { type, ...body } construct because ESDoc/Babylon doesn't understand it; falling back to es5: | ||
const emitted = Object.assign({}, event); // tslint:disable-line:prefer-object-spread | ||
delete emitted.type; | ||
eventBroadcaster.emit(event.type as string, emitted); | ||
}); | ||
} | ||
}); |
Oops, something went wrong.