Skip to content

Commit

Permalink
feat(jasmine): support for nested requirements reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-molak committed Jan 27, 2024
1 parent 37ef679 commit 137fef7
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 13 deletions.
@@ -0,0 +1,9 @@
describe('My feature', () => {

describe('A scenario', () => {

it('passes', () => {
// no-op, passing
});
});
});
4 changes: 4 additions & 0 deletions integration/jasmine/examples/programmatic-reporter.js
@@ -0,0 +1,4 @@
const path = require('path');
const { default: reporter } = require('@serenity-js/jasmine');

jasmine.getEnv().addReporter(reporter(jasmine, { specDirectory: path.resolve(__dirname, '../examples') }));
12 changes: 11 additions & 1 deletion integration/jasmine/spec/custom_reporter.spec.ts
@@ -1,6 +1,6 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected, TestSuiteFinished, TestSuiteStarts } from '@serenity-js/core/lib/events';
import { ExecutionFailedWithError, ExecutionSkipped, ExecutionSuccessful, FeatureTag, ImplementationPending, Name, ProblemIndication } from '@serenity-js/core/lib/model';
import { CapabilityTag, ExecutionFailedWithError, ExecutionSkipped, ExecutionSuccessful, FeatureTag, ImplementationPending, Name, ProblemIndication, ThemeTag } from '@serenity-js/core/lib/model';
import { describe, it } from 'mocha';

import { jasmine } from '../src/jasmine';
Expand Down Expand Up @@ -28,6 +28,8 @@ describe('@serenity-js/jasmine', function () {
PickEvent.from(result.events)
.next(TestSuiteStarts, event => expect(event.details.name).to.equal(new Name(`a suite`)))
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name(`a spec`)))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Custom-reporter-requirements')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('a suite')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand Down Expand Up @@ -60,18 +62,26 @@ describe('@serenity-js/jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name(`pending suite will be pending`)))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Custom-reporter-requirements')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag(`focused suite, excludes other suites and specs`)))
.next(SceneFinished, event => expect(event.outcome).to.be.instanceof(ImplementationPending))

.next(SceneStarts, event => expect(event.details.name).to.equal(new Name(`pending spec`)))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Custom-reporter-requirements')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag(`focused suite, excludes other suites and specs`)))
.next(SceneFinished, event => expect(event.outcome).to.be.instanceof(ImplementationPending))

.next(SceneStarts, event => expect(event.details.name).to.equal(new Name(`spec`)))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Custom-reporter-requirements')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag(`focused suite, excludes other suites and specs`)))
.next(SceneFinished, event => expect(event.outcome).to.be.instanceof(ExecutionSuccessful))

.next(SceneStarts, event => expect(event.details.name).to.equal(new Name(`will be excluded`)))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Custom-reporter-requirements')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag(`excluded suite`)))
.next(SceneFinished, event => expect(event.outcome).to.be.instanceof(ExecutionSkipped))
;
Expand Down
10 changes: 9 additions & 1 deletion integration/jasmine/spec/failing_scenarios.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integratio
import { AssertionError } from '@serenity-js/core';
import { ActivityFinished, ActivityStarts, SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected } from '@serenity-js/core/lib/events';
import { trimmed } from '@serenity-js/core/lib/io';
import { ExecutionFailedWithAssertionError, ExecutionFailedWithError, FeatureTag, Name, ProblemIndication } from '@serenity-js/core/lib/model';
import { CapabilityTag, ExecutionFailedWithAssertionError, ExecutionFailedWithError, FeatureTag, Name, ProblemIndication, ThemeTag } from '@serenity-js/core/lib/model';
import { describe, it } from 'mocha';

import { jasmine } from '../src/jasmine';
Expand All @@ -19,6 +19,8 @@ describe('@serenity-js/jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A scenario fails when marked as failed')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Failing')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand All @@ -38,6 +40,8 @@ describe('@serenity-js/jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A scenario fails when an error is thrown')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Failing')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand All @@ -57,6 +61,8 @@ describe('@serenity-js/jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A scenario fails when the assertion fails')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Failing')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand Down Expand Up @@ -85,6 +91,8 @@ describe('@serenity-js/jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A scenario can fail with multiple failures')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Failing')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(ActivityStarts, event => expect(event.details.name).to.equal(new Name('Expectation')))
Expand Down
5 changes: 4 additions & 1 deletion integration/jasmine/spec/no_describe_blocks.spec.ts
@@ -1,7 +1,7 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { Timestamp } from '@serenity-js/core';
import { SceneStarts, SceneTagged, TestRunFinished, TestRunFinishes, TestRunnerDetected, TestRunStarts } from '@serenity-js/core/lib/events';
import { Category, FeatureTag, Name } from '@serenity-js/core/lib/model';
import { CapabilityTag, Category, FeatureTag, Name, ThemeTag } from '@serenity-js/core/lib/model';
import { describe, it } from 'mocha';

import { jasmine } from '../src/jasmine';
Expand All @@ -22,6 +22,7 @@ describe('@serenity-js/jasmine', function () {
expect(event.details.name).to.equal(new Name('has no describe blocks'));
expect(event.details.category).to.equal(new Category('examples/no-describe-blocks.spec.js'));
})
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('examples/no-describe-blocks.spec.js')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))

Expand All @@ -43,6 +44,8 @@ describe('@serenity-js/jasmine', function () {
expect(event.details.name).to.equal(new Name('has no describe blocks'));
expect(event.details.category).to.equal(new Category('examples/nested/another-no-describe-blocks.spec.js'));
})
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Nested')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('examples/nested/another-no-describe-blocks.spec.js')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))

Expand Down
3 changes: 2 additions & 1 deletion integration/jasmine/spec/passing_scenario.spec.ts
@@ -1,7 +1,7 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { Timestamp } from '@serenity-js/core';
import { SceneFinished, SceneStarts, SceneTagged, TestRunFinished, TestRunFinishes, TestRunnerDetected, TestRunStarts } from '@serenity-js/core/lib/events';
import { ExecutionSuccessful, FeatureTag, Name } from '@serenity-js/core/lib/model';
import { CapabilityTag, ExecutionSuccessful, FeatureTag, Name } from '@serenity-js/core/lib/model';
import { describe, it } from 'mocha';

import { jasmine } from '../src/jasmine';
Expand All @@ -17,6 +17,7 @@ describe('@serenity-js/jasmine', function () {
PickEvent.from(result.events)
.next(TestRunStarts, event => expect(event.timestamp).to.be.instanceof(Timestamp))
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A scenario passes')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))
Expand Down
53 changes: 53 additions & 0 deletions integration/jasmine/spec/requirement_tag_reporting.spec.ts
@@ -0,0 +1,53 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent, SpawnResult } from '@integration/testing-tools';
import { Timestamp } from '@serenity-js/core';
import { SceneFinished, SceneFinishes, SceneStarts, SceneTagged, TestRunFinished, TestRunFinishes, TestRunnerDetected, TestRunStarts } from '@serenity-js/core/lib/events';
import { CapabilityTag, CorrelationId, ExecutionSuccessful, FeatureTag, Name,ThemeTag } from '@serenity-js/core/lib/model';
import { describe, it } from 'mocha';
import path from 'path';

import { jasmineSpawner } from '../src/jasmine';

describe('@serenity-js/jasmine', function () {

this.timeout(30000);

it('recognises a passing scenario', () => jasmine('examples/my_super_theme/my_theme/my_capability/my_feature.spec.js')
.then(ifExitCodeIsOtherThan(0, logOutput))
.then(result => {

expect(result.exitCode).to.equal(0);

let currentSceneId: CorrelationId;

PickEvent.from(result.events)
.next(TestRunStarts, event => expect(event.timestamp).to.be.instanceof(Timestamp))
.next(SceneStarts, event => {
expect(event.details.name).to.equal(new Name('A scenario passes'));
currentSceneId = event.sceneId;
})
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('My super theme')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('My theme')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('My capability')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('My feature')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinishes, event => {
expect(event.sceneId).to.equal(currentSceneId);
})
.next(SceneFinished, event => {
expect(event.sceneId).to.equal(currentSceneId);
expect(event.outcome).to.equal(new ExecutionSuccessful());
})
.next(TestRunFinishes, event => expect(event.timestamp).to.be.instanceof(Timestamp))
.next(TestRunFinished, event => expect(event.timestamp).to.be.instanceof(Timestamp))
;
}));
});

function jasmine(...params: string[]): Promise<SpawnResult> {
return jasmineSpawner(
...params,
'--random=false',
`--require=${ path.resolve(__dirname, '../examples/programmatic-reporter.js') }`,
`--require=${ path.resolve(__dirname, '../examples/setup.js') }`,
);
}
17 changes: 16 additions & 1 deletion integration/jasmine/spec/screenplay.spec.ts
@@ -1,7 +1,16 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected } from '@serenity-js/core/lib/events';
import { trimmed } from '@serenity-js/core/lib/io';
import { ExecutionFailedWithAssertionError, ExecutionFailedWithError, ExecutionSuccessful, FeatureTag, Name, ProblemIndication } from '@serenity-js/core/lib/model';
import {
CapabilityTag,
ExecutionFailedWithAssertionError,
ExecutionFailedWithError,
ExecutionSuccessful,
FeatureTag,
Name,
ProblemIndication
} from '@serenity-js/core/lib/model';
import { ThemeTag } from '@serenity-js/core/lib/model/index.js';
import { describe, it } from 'mocha';

import { jasmine } from '../src/jasmine';
Expand All @@ -21,6 +30,8 @@ describe('@serenity-js/Jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A screenplay scenario correctly reports assertion errors')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Screenplay')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine reporting')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand All @@ -47,6 +58,8 @@ describe('@serenity-js/Jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A screenplay scenario fails when discarding an ability fails')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Screenplay')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine reporting')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand All @@ -71,6 +84,8 @@ describe('@serenity-js/Jasmine', function () {

PickEvent.from(result.events)
.next(SceneStarts, event => expect(event.details.name).to.equal(new Name('A screenplay scenario fails when discarding an ability fails')))
.next(SceneTagged, event => expect(event.tag).to.equal(new ThemeTag('Examples')))
.next(SceneTagged, event => expect(event.tag).to.equal(new CapabilityTag('Screenplay')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Jasmine reporting')))
.next(TestRunnerDetected, event => expect(event.name).to.equal(new Name('Jasmine')))
.next(SceneFinished, event => {
Expand Down
2 changes: 1 addition & 1 deletion integration/jasmine/src/jasmine.ts
Expand Up @@ -9,7 +9,7 @@ const jasmineExecutable = path.resolve(
'jasmine.js',
);

const jasmineSpawner = spawner(
export const jasmineSpawner = spawner(
jasmineExecutable,
{ cwd: path.resolve(__dirname, '..') },
);
Expand Down
21 changes: 16 additions & 5 deletions packages/jasmine/src/SerenityReporterForJasmine.ts
@@ -1,4 +1,10 @@
import { AssertionError, ErrorSerialiser, ImplementationPendingError, type Serenity, TestCompromisedError } from '@serenity-js/core';
import {
AssertionError,
ErrorSerialiser,
ImplementationPendingError,
type Serenity,
TestCompromisedError
} from '@serenity-js/core';
import {
type DomainEvent,
SceneFinished,
Expand All @@ -14,6 +20,7 @@ import {
TestSuiteFinished,
TestSuiteStarts,
} from '@serenity-js/core/lib/events/index.js';
import type { RequirementsHierarchy } from '@serenity-js/core/lib/io/index.js';
import { FileSystemLocation, Path } from '@serenity-js/core/lib/io/index.js';
import {
ActivityDetails,
Expand All @@ -24,7 +31,6 @@ import {
ExecutionFailedWithError,
ExecutionSkipped,
ExecutionSuccessful,
FeatureTag,
ImplementationPending,
Name,
type Outcome,
Expand Down Expand Up @@ -56,7 +62,10 @@ export class SerenityReporterForJasmine implements JasmineReporter {
/**
* @param {Serenity} serenity
*/
constructor(private readonly serenity: Serenity) {
constructor(
private readonly serenity: Serenity,
private readonly requirementsHierachy: RequirementsHierarchy
) {
}

jasmineStarted(info: JasmineStartedInfo): void {
Expand All @@ -82,8 +91,10 @@ export class SerenityReporterForJasmine implements JasmineReporter {

this.emit(
new SceneStarts(this.currentSceneId, details, this.serenity.currentTime()),
new SceneTagged(this.currentSceneId, new FeatureTag(this.currentFeatureNameFor(result)), this.serenity.currentTime()),
// todo: emit capabilities and themes

...this.requirementsHierachy.requirementTagsFor(details.location.path, this.currentFeatureNameFor(result))
.map(tag => new SceneTagged(this.currentSceneId, tag, this.serenity.currentTime())),

new TestRunnerDetected(this.currentSceneId, new Name('Jasmine'), this.serenity.currentTime()),
);
}
Expand Down

0 comments on commit 137fef7

Please sign in to comment.