Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(serenity-bdd): support for reporting business rules
Business rules are a new feature introduced in Cucumber 7
  • Loading branch information
jan-molak committed Jan 5, 2021
1 parent 02efcee commit 3920852
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 27 deletions.
@@ -0,0 +1,117 @@
import { expect } from '@integration/testing-tools';
import { StageManager } from '@serenity-js/core';
import {
BusinessRuleDetected,
SceneFinished,
SceneParametersDetected,
SceneSequenceDetected,
SceneStarts,
SceneTemplateDetected,
TestRunFinishes,
} from '@serenity-js/core/lib/events';
import { FileSystemLocation, Path } from '@serenity-js/core/lib/io';
import { BusinessRule, Category, CorrelationId, Description, ExecutionSuccessful, Name, ScenarioDetails, ScenarioParameters } from '@serenity-js/core/lib/model';
import 'mocha';
import * as sinon from 'sinon';

import { SerenityBDDReporter } from '../../../../../src';
import { SerenityBDDReport } from '../../../../../src/stage/crew/serenity-bdd-reporter/SerenityBDDJsonSchema';
import { given } from '../../given';
import { create } from '../create';

describe('SerenityBDDReporter', () => {

let stageManager: sinon.SinonStubbedInstance<StageManager>,
reporter: SerenityBDDReporter;

beforeEach(() => {
const env = create();

stageManager = env.stageManager;
reporter = env.reporter;
});

// see examples/cucumber/features/reporting_results/reports_scenario_outlines.feature for more context
const
sceneIds = [ new CorrelationId('scene-0'), new CorrelationId('scene-1') ],
category = new Category('Reporting results'),
name = new Name('Reports scenario outlines'),
path = new Path(`reporting_results/reports_scenario_outlines.feature`),
template = new Description(`
When <Developer> makes a contribution of:
| value |
| time |
| talent |
| great code |
Then they help bring serenity to fellow devs
`),
sequence = new ScenarioDetails(name, category, new FileSystemLocation(
path,
7,
)),
scenarios = [
new ScenarioDetails(name, category, new FileSystemLocation(path, 25)),
new ScenarioDetails(name, category, new FileSystemLocation(path, 26)),
]
;

/**
* @test {SerenityBDDReporter}
* @test {SceneStarts}
* @test {SceneBackgroundDetected}
* @test {SceneFinished}
* @test {ExecutionSuccessful}
* @test {TestRunFinishes}
*/
it('captures information about the business rule for single-scene scenarios', () => {
given(reporter).isNotifiedOfFollowingEvents(
new SceneStarts(sceneIds[0], scenarios[0]),
new BusinessRuleDetected(sceneIds[0], scenarios[0], new BusinessRule(new Name('my rule name'), new Description('my rule description'))),
new SceneFinished(sceneIds[0], scenarios[0], new ExecutionSuccessful()),
new TestRunFinishes(),
);

const report: SerenityBDDReport = stageManager.notifyOf.firstCall.lastArg.artifact.map(_ => _);

expect(report.rule).to.equal('my rule name');
});

it('captures information about the business rule for scene sequences', () => {
given(reporter).isNotifiedOfFollowingEvents(
new SceneSequenceDetected(sceneIds[0], sequence),
new SceneTemplateDetected(sceneIds[0], template),
new SceneParametersDetected(
sceneIds[0],
scenarios[0],
new ScenarioParameters(
new Name('Serenity/JS contributors'),
new Description(`Some of the people who have contributed their time and talent to the Serenity/JS project`),
{ Developer: 'jan-molak', Twitter_Handle: '@JanMolak' },
),
),
new SceneStarts(sceneIds[0], scenarios[0]),
new BusinessRuleDetected(sceneIds[0], scenarios[0], new BusinessRule(new Name('my rule name'), new Description('my rule description'))),
new SceneFinished(sceneIds[0], scenarios[0], new ExecutionSuccessful()),

new SceneSequenceDetected(sceneIds[1], sequence),
new SceneTemplateDetected(sceneIds[1], template),
new SceneParametersDetected(
sceneIds[1],
scenarios[1],
new ScenarioParameters(
new Name('Serenity/JS contributors'),
new Description(`Some of the people who have contributed their time and talent to the Serenity/JS project`),
{ Developer: 'wakaleo', Twitter_Handle: '@wakaleo' },
),
),
new SceneStarts(sceneIds[1], scenarios[1]),
new BusinessRuleDetected(sceneIds[1], scenarios[1], new BusinessRule(new Name('my rule name'), new Description('my rule description'))),
new SceneFinished(sceneIds[1], scenarios[1], new ExecutionSuccessful()),
new TestRunFinishes(),
);

const report: SerenityBDDReport = stageManager.notifyOf.firstCall.lastArg.artifact.map(_ => _);

expect(report.rule).to.equal('my rule name');
});
});
Expand Up @@ -19,6 +19,7 @@ export interface SerenityBDDReport extends JSONObject {
tags: Tag[]; // done [x]
startTime: number; // done [x]
duration: number; // done [x]
rule: string;
projectKey: string; // todo [ ] protractor <- this typically comes from an env variable
sessionId?: string; // todo [ ] protractor <- how is this used?
driver?: string; // todo [ ] protractor 'chrome:jill' <- how is this used?
Expand Down
@@ -1,6 +1,7 @@
import {
ActivityRelatedArtifactArchived,
ActivityRelatedArtifactGenerated,
BusinessRuleDetected,
FeatureNarrativeDetected,
SceneBackgroundDetected,
SceneDescriptionDetected,
Expand All @@ -9,9 +10,18 @@ import {
} from '@serenity-js/core/lib/events';

import { SerenityBDDReport } from '../SerenityBDDJsonSchema';
import { SerenityBDDReportContext } from './SerenityBDDReportContext';
import { EventQueue } from './EventQueue';
import { activityRelatedArtifact, archivedActivityRelatedArtifact, backgroundOf, descriptionOf, featureNarrativeOf, tagOf, testRunnerCalled } from './transformations';
import { SerenityBDDReportContext } from './SerenityBDDReportContext';
import {
activityRelatedArtifact,
archivedActivityRelatedArtifact,
backgroundOf,
businessRuleOf,
descriptionOf,
featureNarrativeOf,
tagOf,
testRunnerCalled,
} from './transformations';

/**
* @package
Expand Down Expand Up @@ -50,6 +60,12 @@ export abstract class EventQueueProcessor {
.with(tagOf(event.tag))
}

protected onBusinessRuleDetected<Context extends SerenityBDDReportContext>(report: Context) {
return (event: BusinessRuleDetected): Context =>
report
.with(businessRuleOf(event.rule))
}

protected onActivityRelatedArtifactGenerated<Context extends SerenityBDDReportContext>(report: Context) {
return (event: ActivityRelatedArtifactGenerated): Context =>
report
Expand Down
@@ -1,8 +1,10 @@
import { match } from 'tiny-types';
import {
ActivityFinished,
ActivityRelatedArtifactArchived,
ActivityRelatedArtifactGenerated,
ActivityStarts,
BusinessRuleDetected,
DomainEvent,
FeatureNarrativeDetected,
SceneBackgroundDetected,
Expand All @@ -15,20 +17,12 @@ import {
SceneTemplateDetected,
TestRunnerDetected,
} from '@serenity-js/core/lib/events';
import { match } from 'tiny-types';

import { SerenityBDDReport } from '../../SerenityBDDJsonSchema';
import { EventQueue } from '../EventQueue';
import { EventQueueProcessor } from '../EventQueueProcessor';
import { SerenityBDDReportContext } from '../SerenityBDDReportContext';
import {
activityFinished,
activityStarted,
executionFinishedAt,
executionStartedAt,
reportIdIncluding,
scenarioDetailsOf,
} from '../transformations';
import { activityFinished, activityStarted, executionFinishedAt, executionStartedAt, reportIdIncluding, scenarioDetailsOf } from '../transformations';
import { SceneSequenceReportContext } from './SceneSequenceReportContext';
import { scenarioOutlineOf, sceneSequenceOverallResult } from './transformations';
import { scenarioParameterResult } from './transformations/scenarioParameterResult';
Expand All @@ -54,6 +48,7 @@ export class SceneSequenceEventQueueProcessor extends EventQueueProcessor {
.when(FeatureNarrativeDetected, this.onFeatureNarrativeDetected(context))
.when(SceneBackgroundDetected, this.onSceneBackgroundDetected(context))
.when(SceneDescriptionDetected, this.onSceneDescriptionDetected(context))
.when(BusinessRuleDetected, this.onBusinessRuleDetected(context))
.when(TestRunnerDetected, this.onTestRunnerDetected(context))
.when(SceneTagged, this.onSceneTagged(context))
.when(ActivityStarts, this.onActivityStarts(context))
Expand Down Expand Up @@ -114,4 +109,3 @@ export class SceneSequenceEventQueueProcessor extends EventQueueProcessor {
.with(executionFinishedAt(event.timestamp))
}
}

@@ -1,8 +1,10 @@
import { match } from 'tiny-types';
import {
ActivityFinished,
ActivityRelatedArtifactArchived,
ActivityRelatedArtifactGenerated,
ActivityStarts,
BusinessRuleDetected,
DomainEvent,
FeatureNarrativeDetected,
SceneBackgroundDetected,
Expand All @@ -12,7 +14,6 @@ import {
SceneTagged,
TestRunnerDetected,
} from '@serenity-js/core/lib/events';
import { match } from 'tiny-types';

import { SerenityBDDReport } from '../../SerenityBDDJsonSchema';
import { EventQueue } from '../EventQueue';
Expand All @@ -32,20 +33,21 @@ export class SingleSceneEventQueueProcessor extends EventQueueProcessor {
}

process(queue: EventQueue): SerenityBDDReport {
return queue.reduce((report, event) =>
return queue.reduce((context, event) =>
match<DomainEvent, SingleSceneReportContext>(event)
.when(SceneStarts, this.onSceneStarts(report))
.when(FeatureNarrativeDetected, this.onFeatureNarrativeDetected(report))
.when(SceneBackgroundDetected, this.onSceneBackgroundDetected(report))
.when(SceneDescriptionDetected, this.onSceneDescriptionDetected(report))
.when(TestRunnerDetected, this.onTestRunnerDetected(report))
.when(SceneTagged, this.onSceneTagged(report))
.when(ActivityStarts, this.onActivityStarts(report))
.when(ActivityFinished, this.onActivityFinished(report))
.when(ActivityRelatedArtifactGenerated, this.onActivityRelatedArtifactGenerated(report))
.when(ActivityRelatedArtifactArchived, this.onActivityRelatedArtifactArchived(report))
.when(SceneFinished, this.onSceneFinished(report))
.else(() => report),
.when(SceneStarts, this.onSceneStarts(context))
.when(FeatureNarrativeDetected, this.onFeatureNarrativeDetected(context))
.when(SceneBackgroundDetected, this.onSceneBackgroundDetected(context))
.when(SceneDescriptionDetected, this.onSceneDescriptionDetected(context))
.when(BusinessRuleDetected, this.onBusinessRuleDetected(context))
.when(TestRunnerDetected, this.onTestRunnerDetected(context))
.when(SceneTagged, this.onSceneTagged(context))
.when(ActivityStarts, this.onActivityStarts(context))
.when(ActivityFinished, this.onActivityFinished(context))
.when(ActivityRelatedArtifactGenerated, this.onActivityRelatedArtifactGenerated(context))
.when(ActivityRelatedArtifactArchived, this.onActivityRelatedArtifactArchived(context))
.when(SceneFinished, this.onSceneFinished(context))
.else(() => context),
new SingleSceneReportContext()
).build();
}
Expand Down Expand Up @@ -77,4 +79,3 @@ export class SingleSceneEventQueueProcessor extends EventQueueProcessor {
.with(executionFinishedAt(event.timestamp))
}
}

@@ -0,0 +1,17 @@
import { BusinessRule, Description, Name } from '@serenity-js/core/lib/model';
import { SerenityBDDReportContext } from '../SerenityBDDReportContext';

/**
* @package
*/
export function businessRuleOf<Context extends SerenityBDDReportContext>(rule: BusinessRule) {
return (context: Context): Context => {

context.report.rule = rule.name.value;

// todo: Serenity BDD v. 2.3.10 doesn't support business rule descriptions yet
// context.report.?? = rule.description.value;

return context;
}
}
Expand Up @@ -3,6 +3,7 @@ export * from './activityRelatedArtifact';
export * from './archivedActivityRelatedArtifact';
export * from './activityStarted';
export * from './backgroundOf';
export * from './businessRuleOf';
export * from './descriptionOf';
export * from './executionFinishedAt';
export * from './executionFinishedWith';
Expand Down

0 comments on commit 3920852

Please sign in to comment.