Permalink
Browse files

feat(integration): Stand-ins enable taking screenshots when there are…

… no Actors on the Stage.

The Photographer relies on Actors being present on the Stage to recognise whose activities should
be captured.

A Stand-in substitutes for the Actor if they're missing, allowing you to capture rich, illustrated reports
of your acceptance test scenarios even if you haven't (yet) adopted the Screenplay Pattern, nor have defined
the actors.

This functionality is useful when Serenity/JS is introduced to an existing code base as it allows
for gradual and safe adoption of the library.

Thanks to @awashbrook for the "Stand-in" metaphor!
  • Loading branch information...
jan-molak committed Feb 1, 2017
1 parent f025ed2 commit 786b3373b69cea62ba0c898a397d998b8ef32394
@@ -2,7 +2,9 @@ import { Runner } from 'protractor';
import { serenity, Serenity } from '../..';
import { serenityBDDReporter } from '../../serenity/reporting';
import { ProtractorReport, ProtractorReporter } from '../reporting';
import { photographer } from '../stage/photographer';
import { SerenityFrameworkConfig } from './serenity_framework_config';
import { StandIns } from './stand_ins';
import { TestFramework } from './test_framework';
import { TestFrameworkDetector } from './test_framework_detector';
@@ -26,7 +28,10 @@ export class SerenityProtractorFramework {
this.reporter = new ProtractorReporter(runner);
this.framework = new TestFrameworkDetector().frameworkFor(this.config);
this.serenity.assignCrewMembers(...this.config.serenity.crew, this.reporter);
this.serenity.assignCrewMembers(...this.config.serenity.crew.concat([
this.reporter,
new StandIns(),
]));
}
run = (specs: string[]): PromiseLike<ProtractorReport> => this.runner.runTestPreparer().then(() =>
@@ -48,7 +53,10 @@ export class SerenityProtractorFramework {
private defaultConfig = (): SerenityFrameworkConfig => ({
serenity: {
crew: [ serenityBDDReporter() ],
crew: [
serenityBDDReporter(),
photographer(),
],
},
})
}
@@ -0,0 +1,43 @@
import { protractor } from 'protractor';
import { DomainEvent, SceneStarts } from '../../serenity/domain';
import { Actor } from '../../serenity/screenplay/actor';
import { Stage, StageCrewMember } from '../../serenity/stage';
import { BrowseTheWeb } from '../screenplay/abilities/browse_the_web';
/**
* Provides generic stand-in actors for the purpose of taking screenshots.
* This is useful when integrating Serenity/JS with a legacy code base that does not (yet) use the Screenplay Pattern,
* nor invoke `serenity.callToStageFor(customActors)`.
*/
export class StandIns implements StageCrewMember {
private static Events_of_Interest = [ SceneStarts ];
private stage: Stage;
assignTo(stage: Stage) {
this.stage = stage;
this.stage.manager.registerInterestIn(StandIns.Events_of_Interest, this);
}
notifyOf(event: DomainEvent<any>): void {
switch (event.constructor.name) { // tslint:disable-line:switch-default - ignore other events
case SceneStarts.name:
this.provideStandInActorsIfNeeded();
this.shineSpotlightOn('a stand-in actor');
}
}
private provideStandInActorsIfNeeded() {
if (! this.stage.actorsAreReady()) {
this.stage.enter({
actor: (name: string) => {
return Actor.named(name).whoCan(BrowseTheWeb.using(protractor.browser));
},
});
}
}
private shineSpotlightOn(name: string) {
this.stage.theActorCalled(name);
}
}
@@ -20,7 +20,7 @@ export class Stage {
}
theActorCalled(actorName: string): Actor {
if (! this.castIsReady()) {
if (! this.actorsAreReady()) {
throw new Error('The cast has not entered the stage yet.');
}
@@ -41,7 +41,7 @@ export class Stage {
return this.actorInTheSpotlight;
}
private castIsReady(): boolean {
actorsAreReady(): boolean {
return !! this.cast;
}
}

0 comments on commit 786b337

Please sign in to comment.