Skip to content
Permalink
Browse files
feat(protractor): Photo taking strategies for the Photographer
  • Loading branch information
jan-molak committed Mar 25, 2019
1 parent 200a213 commit 8f6d149c4db85ecf0f1a6c469935c0ca9ab1048e
Show file tree
Hide file tree
Showing 17 changed files with 537 additions and 229 deletions.
@@ -44,7 +44,7 @@ export class ArtifactArchiver implements StageCrewMember {
this.archive(
photo.constructor as ArtifactType,
name,
new Path(`${ sanitise(name.value) }-${ this.hashOf(photo) }.png`),
new Path(`${ this.sanitised(name.value) }-${ this.hashOf(photo) }.png`),
photo.base64EncodedValue,
'base64',
);
@@ -54,7 +54,7 @@ export class ArtifactArchiver implements StageCrewMember {
this.archive(
report.constructor as ArtifactType,
name,
new Path(`${ sanitise(name.value) }-${ this.hashOf(report) }.json`),
new Path(`${ this.sanitised(name.value) }-${ this.hashOf(report) }.json`),
report.map(JSON.stringify),
'utf8',
);
@@ -89,4 +89,10 @@ export class ArtifactArchiver implements StageCrewMember {
this.stage.manager.notifyOf(new AsyncOperationFailed(error, id));
});
}
}

private sanitised(text: string): string {
return sanitise(text).toLowerCase()
.trim()
.replace(/\s+/, '-');
}
}

This file was deleted.

@@ -0,0 +1,106 @@
import { EventRecorder, expect, givenFollowingEvents, PickEvent } from '@integration/testing-tools';
import {
Activity,
AssertionError,
ImplementationPendingError,
Interaction,
LogicError,
Task,
} from '@serenity-js/core';
import {
ArtifactGenerated,
SceneFinished,
SceneStarts,
TaskFinished,
TaskStarts,
TestRunFinished,
} from '@serenity-js/core/lib/events';
import { FileSystemLocation, Path } from '@serenity-js/core/lib/io';
import {
ActivityDetails,
Category,
ExecutionCompromised,
ExecutionFailedWithAssertionError,
ExecutionFailedWithError,
ExecutionIgnored,
ExecutionSkipped,
ExecutionSuccessful,
ImplementationPending,
Name,
Outcome,
Photo,
ScenarioDetails,
} from '@serenity-js/core/lib/model';
import { Stage } from '@serenity-js/core/lib/stage';
import { given } from 'mocha-testdata';
import { Photographer, TakePhotosOfFailures } from '../../../src/stage';
import { create } from './create';

describe('Photographer', () => {

const
defaultCardScenario = new ScenarioDetails(
new Name('Paying with a default card'),
new Category('Online Checkout'),
new FileSystemLocation(
new Path(`payments/checkout.feature`),
),
),
pickACard = new ActivityDetails(new Name('Pick the default credit card'));

it(`complains when sent DomainEvents before getting assigned to a Stage`, () => {
const photographer = new Photographer(new TakePhotosOfFailures());
expect(() => photographer.notifyOf(new SceneStarts(defaultCardScenario)))
.to.throw(LogicError, `Photographer needs to be assigned to the Stage before it can be notified of any DomainEvents`);
});

describe(`when there's no actor in the spotlight`, () => {

given(
new ExecutionSkipped(),
new ExecutionIgnored(),
new ExecutionSuccessful(),
).
it(`doesn't take a picture if everything goes well`, (outcome: Outcome) => {
const { stage, recorder } = create();

const photographer = new Photographer(new TakePhotosOfFailures(), stage);
stage.manager.register(photographer);

givenFollowingEvents(
new SceneStarts(defaultCardScenario),
new TaskStarts(pickACard),
new TaskFinished(pickACard, outcome),
new SceneFinished(defaultCardScenario, outcome),
new TestRunFinished(),
).areSentTo(photographer);

return stage.manager.waitForNextCue().then(() => {
expect(recorder.events).to.have.lengthOf(0);
});
});

given(
{ description: 'compromised', outcome: new ExecutionCompromised(new Error('Database is down')) },
{ description: 'error', outcome: new ExecutionFailedWithError(new TypeError()) },
{ description: 'assertion error', outcome: new ExecutionFailedWithAssertionError(new AssertionError(`expected false to equal true`, false, true)) },
{ description: 'implementation pending', outcome: new ImplementationPending(new ImplementationPendingError('method missing')) },
).
it(`does nothing, even when a problem occurs`, ({ outcome }) => {
const { stage, recorder } = create();

const photographer = new Photographer(new TakePhotosOfFailures(), stage);
stage.manager.register(photographer);

givenFollowingEvents(
new SceneStarts(defaultCardScenario),
new SceneFinished(defaultCardScenario, outcome),
new TestRunFinished(),
).areSentTo(photographer);

return stage.manager.waitForNextCue().then(() => {
expect(recorder.events).to.have.lengthOf(0);
});
});
});
});
@@ -0,0 +1,22 @@
import { EventRecorder } from '@integration/testing-tools';
import { Actor, Duration } from '@serenity-js/core';
import { Clock, Stage, StageManager } from '@serenity-js/core/lib/stage';
import { protractor } from 'protractor';
import { BrowseTheWeb } from '../../../src/screenplay/abilities';

export function create(timeout: Duration = Duration.ofSeconds(1)) {
const clock = new Clock();

const
stageManager = new StageManager(timeout, clock),
actors = { actor: (name: string) => {
return new Actor(name, stageManager, clock)
.whoCan(BrowseTheWeb.using(protractor.browser));
}},
stage = new Stage(actors, stageManager),
recorder = new EventRecorder();

stage.assign(recorder);

return { stage, recorder };
}
@@ -0,0 +1,11 @@
import { Activity, Interaction, Task } from '@serenity-js/core';

export class Perform {
static interactionThatSucceeds = (id: number = 1) =>
Interaction.where(`#actor succeeds (#${id})`, actor => void 0)

static interactionThatFailsWith = (errorType: new (message: string) => Error) =>
Interaction.where(`#actor fails due to ${ errorType.name }`, actor => { throw new errorType('failure'); })

static taskWith = (...activities: Activity[]) => Task.where(`#actor performs activities`, ...activities);
}

0 comments on commit 8f6d149

Please sign in to comment.