Skip to content
Permalink
Browse files
fix(core): all Activity-related events can be correlated with the Sce…
…ne they originate from
  • Loading branch information
jan-molak committed Oct 10, 2020
1 parent 943c016 commit 6cf0eca7670db01ac317587996fc3ea5984de059
Show file tree
Hide file tree
Showing 24 changed files with 223 additions and 177 deletions.

Large diffs are not rendered by default.

@@ -14,7 +14,11 @@ describe('Task', () => {
beforeEach(() => {
stage = sinon.createStubInstance(Stage);

const activityId = CorrelationId.create();
const
sceneId = new CorrelationId('some-scene-id'),
activityId = new CorrelationId('some-activity-id');

stage.currentSceneId.returns(sceneId);
stage.assignNewActivityId.returns(activityId);
stage.currentActivityId.returns(activityId);
});
@@ -14,6 +14,10 @@ const equals = (expected: number) => (actual: PromiseLike<number>) => expect(act

describe('Actor', () => {

const
sceneId = new CorrelationId('some-scene-id'),
activityId = new CorrelationId('some-activity-id');

let
guitar: sinon.SinonStubbedInstance<Guitar>,
stage: sinon.SinonStubbedInstance<Stage>;
@@ -22,8 +26,8 @@ describe('Actor', () => {
guitar = sinon.createStubInstance(AcousticGuitar);
stage = sinon.createStubInstance(Stage);

const activityId = CorrelationId.create();
stage.assignNewActivityId.returns(activityId);
stage.currentSceneId.returns(sceneId);
stage.currentActivityId.returns(activityId);
});

@@ -48,7 +52,7 @@ describe('Actor', () => {
});

/** @test {Actor} */
it('has Abilities allowing them to perform Activities and interact with a given Interface of the system under test', () =>
it('has Abilities allowing them to perform Activities and interact with a given interface of the system under test', () =>

actor('Chris').whoCan(PlayAGuitar.suchAs(guitar)).attemptsTo(
PlayAChord.of(Chords.AMajor),
@@ -125,7 +129,7 @@ describe('Actor', () => {
stage = sinon.createStubInstance(Stage);
stage.currentTime.returns(now);

const activityId = CorrelationId.create();
stage.currentSceneId.returns(sceneId);
stage.assignNewActivityId.returns(activityId);
stage.currentActivityId.returns(activityId);

@@ -13,13 +13,17 @@ describe('Note', () => {
expectedHobby = 'DYI',
NameOfAHobby = () => Question.about(`the name of a hobby`, someActor => Promise.resolve(expectedHobby));

const
sceneId = new CorrelationId('some-scene-id'),
activityId = new CorrelationId('some-activity-id');

let stage: sinon.SinonStubbedInstance<Stage>,
Noah: Actor;

beforeEach(() => {
stage = sinon.createStubInstance(Stage);

const activityId = CorrelationId.create();
stage.currentSceneId.returns(sceneId);
stage.assignNewActivityId.returns(activityId);
stage.currentActivityId.returns(activityId);

@@ -131,7 +131,7 @@ describe('Stage', () => {

const retrieved = stage.currentSceneId();

expect(retrieved.value).to.equal('unknown-scene');
expect(retrieved.value).to.equal('unknown');
});

it('assigns activityIds', () => {
@@ -184,6 +184,10 @@ export class Serenity {
return this.stage.assignNewSceneId();
}

currentSceneId(): CorrelationId {
return this.stage.currentSceneId();
}

assignNewActivityId(): CorrelationId {
return this.stage.assignNewActivityId();
}
@@ -5,12 +5,14 @@ import { DomainEvent } from './DomainEvent';

export abstract class ActivityFinished extends DomainEvent {
constructor(
public readonly sceneId: CorrelationId,
public readonly activityId: CorrelationId,
public readonly details: ActivityDetails,
public readonly outcome: Outcome,
timestamp?: Timestamp,
) {
super(timestamp);
ensure('sceneId', sceneId, isDefined());
ensure('activityId', activityId, isDefined());
ensure('details', details, isDefined());
ensure('outcome', outcome, isDefined());
@@ -7,6 +7,7 @@ import { ArtifactArchived } from './ArtifactArchived';
export class ActivityRelatedArtifactArchived extends ArtifactArchived {
static fromJSON<E>(o: JSONObject) {
return new ActivityRelatedArtifactArchived(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
Name.fromJSON(o.name as string),
Artifact.ofType(o.type as string),
@@ -16,18 +17,21 @@ export class ActivityRelatedArtifactArchived extends ArtifactArchived {
}

constructor(
public readonly sceneId: CorrelationId,
public readonly activityId: CorrelationId,
name: Name,
type: ArtifactType,
path: Path,
timestamp?: Timestamp,
) {
super(name, type, path, timestamp);
ensure('sceneId', sceneId, isDefined());
ensure('activityId', activityId, isDefined());
}

toJSON(): JSONObject {
return {
sceneId: this.sceneId.toJSON(),
activityId: this.activityId.toJSON(),
name: this.name.toJSON(),
type: this.type.name,
@@ -6,6 +6,7 @@ import { ArtifactGenerated } from './ArtifactGenerated';
export class ActivityRelatedArtifactGenerated extends ArtifactGenerated {
static fromJSON(o: JSONObject) {
return new ActivityRelatedArtifactGenerated(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
Name.fromJSON(o.name as string),
Artifact.fromJSON(o.artifact as SerialisedArtifact),
@@ -14,12 +15,14 @@ export class ActivityRelatedArtifactGenerated extends ArtifactGenerated {
}

constructor(
public readonly sceneId: CorrelationId,
public readonly activityId: CorrelationId,
name: Name,
artifact: Artifact,
timestamp?: Timestamp,
) {
super(name, artifact, timestamp);
ensure('sceneId', sceneId, isDefined());
ensure('activityId', activityId, isDefined());
}
}
@@ -5,11 +5,13 @@ import { DomainEvent } from './DomainEvent';

export abstract class ActivityStarts extends DomainEvent {
constructor(
public readonly sceneId: CorrelationId,
public readonly activityId: CorrelationId,
public readonly details: ActivityDetails,
timestamp?: Timestamp,
) {
super(timestamp);
ensure('sceneId', sceneId, isDefined());
ensure('activityId', activityId, isDefined());
ensure('details', details, isDefined());
}
@@ -6,6 +6,7 @@ import { ActivityFinished } from './ActivityFinished';
export class InteractionFinished extends ActivityFinished {
static fromJSON(o: JSONObject) {
return new InteractionFinished(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
ActivityDetails.fromJSON(o.details as JSONObject),
Outcome.fromJSON(o.outcome as SerialisedOutcome),
@@ -6,6 +6,7 @@ import { ActivityStarts } from './ActivityStarts';
export class InteractionStarts extends ActivityStarts {
static fromJSON(o: JSONObject) {
return new InteractionStarts(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
ActivityDetails.fromJSON(o.details as JSONObject),
Timestamp.fromJSON(o.timestamp as string),
@@ -6,6 +6,7 @@ import { ActivityFinished } from './ActivityFinished';
export class TaskFinished extends ActivityFinished {
static fromJSON(o: JSONObject) {
return new TaskFinished(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
ActivityDetails.fromJSON(o.details as JSONObject),
Outcome.fromJSON(o.outcome as SerialisedOutcome),
@@ -6,6 +6,7 @@ import { ActivityStarts } from './ActivityStarts';
export class TaskStarts extends ActivityStarts {
static fromJSON(o: JSONObject) {
return new TaskStarts(
CorrelationId.fromJSON(o.sceneId as string),
CorrelationId.fromJSON(o.activityId as string),
ActivityDetails.fromJSON(o.details as JSONObject),
Timestamp.fromJSON(o.timestamp as string),
@@ -20,23 +20,25 @@ export class TrackedActivity implements Activity {
}

performAs(actor: (PerformsActivities | UsesAbilities | AnswersQuestions) & { name: string }): PromiseLike<void> {
const activityId = this.stage.assignNewActivityId();
const details = new ActivityDetails(TrackedActivity.describer.describe(this.activity, actor));
const
sceneId = this.stage.currentSceneId(),
activityId = this.stage.assignNewActivityId(),
details = new ActivityDetails(TrackedActivity.describer.describe(this.activity, actor));

const [ activityStarts, activityFinished] = this.activity instanceof Interaction
? [ InteractionStarts, InteractionFinished ]
: [ TaskStarts, TaskFinished ];

return Promise.resolve()
.then(() => this.stage.announce(new activityStarts(activityId, details, this.stage.currentTime())))
.then(() => this.stage.announce(new activityStarts(sceneId, activityId, details, this.stage.currentTime())))
.then(() => this.activity.performAs(actor))
.then(() => {
const outcome = new ExecutionSuccessful();
this.stage.announce(new activityFinished(activityId, details, outcome, this.stage.currentTime()));
this.stage.announce(new activityFinished(sceneId, activityId, details, outcome, this.stage.currentTime()));
})
.catch(error => {
const outcome = TrackedActivity.outcomes.outcomeFor(error);
this.stage.announce(new activityFinished(activityId, details, outcome, this.stage.currentTime()));
this.stage.announce(new activityFinished(sceneId, activityId, details, outcome, this.stage.currentTime()));

throw error;
});
@@ -134,6 +134,7 @@ export class Actor implements
*/
collect(artifact: Artifact, name?: string | Name) {
this.stage.announce(new ActivityRelatedArtifactGenerated(
this.stage.currentSceneId(),
this.stage.currentActivityId(),
this.nameFrom(name || new Name(artifact.constructor.name)),
artifact,
@@ -15,7 +15,7 @@ export class Stage {
private actorInTheSpotlight: Actor = null;

private currentActivity: CorrelationId = null;
private currentScene: CorrelationId = new CorrelationId('unknown-scene');
private currentScene: CorrelationId = new CorrelationId('unknown');

constructor(
private cast: Cast,
@@ -108,6 +108,7 @@ export class ArtifactArchiver implements StageCrewMember {
return (absolutePath: Path) => {
if (evt instanceof ActivityRelatedArtifactGenerated) {
this.stage.announce(new ActivityRelatedArtifactArchived(
evt.sceneId,
evt.activityId,
evt.name,
evt.artifact.constructor as ArtifactType,
@@ -74,12 +74,20 @@ export class Notifier {
stepStarts(step: Step): void {
this.currentStepActivityId = this.serenity.assignNewActivityId();

this.emit(new TaskStarts(this.currentStepActivityId, new ActivityDetails(step.name), this.serenity.currentTime()));
this.emit(
new TaskStarts(
this.currentSceneId,
this.currentStepActivityId,
new ActivityDetails(step.name),
this.serenity.currentTime()
),
);
}

stepFinished(step: Step, outcome: Outcome): void {
this.emit(
new TaskFinished(
this.currentSceneId,
this.currentStepActivityId,
new ActivityDetails(step.name),
outcome,
@@ -82,12 +82,14 @@ export class SerenityReporterForJasmine {
*/
if (result.failedExpectations.length > 1) {
result.failedExpectations.forEach(failedExpectation => {
const activityId = this.serenity.assignNewActivityId();
const
sceneId = this.serenity.currentSceneId(),
activityId = this.serenity.assignNewActivityId();
const details = new ActivityDetails(new Name('Expectation'));

this.emit(
new TaskStarts(activityId, details, this.serenity.currentTime()),
new TaskFinished(activityId, details, this.failureOutcomeFrom(failedExpectation), this.serenity.currentTime()),
new TaskStarts(sceneId, activityId, details, this.serenity.currentTime()),
new TaskFinished(sceneId, activityId, details, this.failureOutcomeFrom(failedExpectation), this.serenity.currentTime()),
);
});
}
@@ -57,8 +57,8 @@ describe('Photographer', () => {

givenFollowingEvents(
new SceneStarts(sceneId, defaultCardScenario),
new TaskStarts(activityId, pickACard),
new TaskFinished(activityId, pickACard, outcome),
new TaskStarts(sceneId, activityId, pickACard),
new TaskFinished(sceneId, activityId, pickACard, outcome),
new SceneFinished(sceneId, defaultCardScenario, outcome),
new TestRunFinished(),
).areSentTo(photographer);
@@ -61,6 +61,7 @@ export abstract class PhotoTakingStrategy {
photoName = this.combinedNameFrom(...context, nameSuffix);

stage.announce(new ActivityRelatedArtifactGenerated(
event.sceneId,
event.activityId,
photoName,
Photo.fromBase64(screenshot),

0 comments on commit 6cf0eca

Please sign in to comment.