Skip to content
Permalink
Browse files
feat(serenity-bdd): Extracted the SerenityBDDReporter into a separate…
… module

SerenityBDDReporter lives in @serenity-js/serenity-bdd module now so that it can be bundled together
with the new CLI module responsible for downloading and executing the Serenity BDD CLI jar.
  • Loading branch information
jan-molak committed Aug 27, 2019
1 parent 6054f44 commit fe7cfca92357e65d465ee47657d916a941c1e9d3
Showing with 1,609 additions and 151 deletions.
  1. +1 −0 documentation/website/package.json
  2. +2 −1 examples/cucumber-domain-level-testing/features/support/configure_serenity.ts
  3. +2 −1 examples/cucumber-reporting/features/support/configure_serenity.ts
  4. +2 −1 examples/cucumber-rest-api-level-testing/features/support/configure_serenity.ts
  5. +1 −2 packages/core/package.json
  6. +56 −2 packages/core/spec/io/FileSystem.spec.ts
  7. +22 −0 packages/core/spec/io/Path.spec.ts
  8. +1 −3 packages/core/spec/{io → model}/Artifact.spec.ts
  9. +69 −18 packages/core/src/io/FileSystem.ts
  10. +11 −1 packages/core/src/io/Path.ts
  11. +0 −1 packages/core/src/stage/crew/index.ts
  12. +1 −0 packages/serenity-bdd/.esdoc.js
  13. +12 −0 packages/serenity-bdd/.gitignore
  14. +14 −0 packages/serenity-bdd/.npmignore
  15. +201 −0 packages/serenity-bdd/LICENSE.md
  16. +1 −0 packages/serenity-bdd/NOTICE.md
  17. +1 −0 packages/serenity-bdd/README.md
  18. +3 −0 packages/serenity-bdd/bin/serenity-bdd
  19. +89 −0 packages/serenity-bdd/package.json
  20. +15 −0 packages/serenity-bdd/spec/cli/bootstrap.spec.ts
  21. +62 −0 packages/serenity-bdd/spec/cli/model/GAV.spec.ts
  22. +10 −0 packages/serenity-bdd/spec/stage/given.ts
  23. +20 −0 packages/serenity-bdd/spec/stage/samples.ts
  24. +10 −16 ...core/spec/stage/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/SerenityBDDReporter.spec.ts
  25. +6 −13 ...rew → serenity-bdd/spec/stage}/serenity-bdd-reporter/SerenityBDDReporter/describing_scene.spec.ts
  26. +10 −10 ...→ serenity-bdd/spec/stage}/serenity-bdd-reporter/SerenityBDDReporter/reporting_activities.spec.ts
  27. +7 −22 ...enity-bdd/spec/stage}/serenity-bdd-reporter/SerenityBDDReporter/reporting_scene_sequences.spec.ts
  28. +6 −6 ...ew → serenity-bdd/spec/stage}/serenity-bdd-reporter/SerenityBDDReporter/tagging_scenarios.spec.ts
  29. +2 −2 packages/{core/spec/stage/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/create.ts
  30. 0 ...core/spec/stage/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/example-reports/failed.json
  31. 0 ...ge/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/example-reports/parsed-feature-file.json
  32. 0 ...c/stage/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/example-reports/sample-outcome.json
  33. 0 ...rew → serenity-bdd/spec/stage}/serenity-bdd-reporter/example-reports/sample-with-screenshots.json
  34. 0 .../spec/stage/crew → serenity-bdd/spec/stage}/serenity-bdd-reporter/example-reports/two-tables.json
  35. +6 −0 packages/serenity-bdd/src/cli/Argv.ts
  36. +21 −0 packages/serenity-bdd/src/cli/Printer.ts
  37. +50 −0 packages/serenity-bdd/src/cli/bootstrap.ts
  38. +70 −0 packages/serenity-bdd/src/cli/commands/run.ts
  39. +66 −0 packages/serenity-bdd/src/cli/commands/update.ts
  40. +23 −0 packages/serenity-bdd/src/cli/defaults.ts
  41. +3 −0 packages/serenity-bdd/src/cli/index.ts
  42. +13 −0 packages/serenity-bdd/src/cli/model/Complaint.ts
  43. +13 −0 packages/serenity-bdd/src/cli/model/DownloadProgressReport.ts
  44. +15 −0 packages/serenity-bdd/src/cli/model/ExecutionError.ts
  45. +50 −0 packages/serenity-bdd/src/cli/model/GAV.ts
  46. +13 −0 packages/serenity-bdd/src/cli/model/Notification.ts
  47. +5 −0 packages/serenity-bdd/src/cli/model/index.ts
  48. +39 −0 packages/serenity-bdd/src/cli/screenplay/abilities/UseFileSystem.ts
  49. +1 −0 packages/serenity-bdd/src/cli/screenplay/abilities/index.ts
  50. +4 −0 packages/serenity-bdd/src/cli/screenplay/index.ts
  51. +41 −0 packages/serenity-bdd/src/cli/screenplay/interactions/Complain.ts
  52. +11 −0 packages/serenity-bdd/src/cli/screenplay/interactions/CreateDirectory.ts
  53. +11 −0 packages/serenity-bdd/src/cli/screenplay/interactions/Notify.ts
  54. +13 −0 packages/serenity-bdd/src/cli/screenplay/interactions/RenameFile.ts
  55. +43 −0 packages/serenity-bdd/src/cli/screenplay/interactions/Spawn.ts
  56. +65 −0 packages/serenity-bdd/src/cli/screenplay/interactions/StreamResponse.ts
  57. +11 −0 packages/serenity-bdd/src/cli/screenplay/interactions/TerminateFlow.ts
  58. +5 −0 packages/serenity-bdd/src/cli/screenplay/interactions/index.ts
  59. +20 −0 packages/serenity-bdd/src/cli/screenplay/questions/FileExists.ts
  60. +30 −0 packages/serenity-bdd/src/cli/screenplay/questions/JavaExecutable.ts
  61. +40 −0 packages/serenity-bdd/src/cli/screenplay/questions/SerenityBDDArguments.ts
  62. +3 −0 packages/serenity-bdd/src/cli/screenplay/questions/index.ts
  63. +69 −0 packages/serenity-bdd/src/cli/screenplay/tasks/DownloadArtifact.ts
  64. +47 −0 packages/serenity-bdd/src/cli/screenplay/tasks/InvokeSerenityBDD.ts
  65. +2 −0 packages/serenity-bdd/src/cli/screenplay/tasks/index.ts
  66. +24 −0 packages/serenity-bdd/src/cli/stage/Actors.ts
  67. +39 −0 packages/serenity-bdd/src/cli/stage/NotificationReporter.ts
  68. +42 −0 packages/serenity-bdd/src/cli/stage/ProgressReporter.ts
  69. +3 −0 packages/serenity-bdd/src/cli/stage/index.ts
  70. +1 −0 packages/serenity-bdd/src/index.ts
  71. +1 −0 packages/serenity-bdd/src/stage/index.ts
  72. 0 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/Current.ts
  73. 0 ...ges/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/SerenityBDDJsonSchema.ts
  74. +3 −4 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/SerenityBDDReporter.ts
  75. 0 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/index.ts
  76. +2 −2 ...ges/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/ErrorRenderer.ts
  77. 0 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/IDGenerator.ts
  78. +2 −3 ...ges/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/OutcomeMapper.ts
  79. +11 −17 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/SceneReport.ts
  80. +1 −1 ...ages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/SceneReports.ts
  81. 0 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/reports/index.ts
  82. +4 −13 ...c/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/strategies/SceneReportingStrategy.ts
  83. +2 −10 ...crew → serenity-bdd/src/stage}/serenity-bdd-reporter/strategies/SceneSequenceReportingStrategy.ts
  84. +2 −2 ...e/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/strategies/SingleSceneReportingStrategy.ts
  85. 0 packages/{core/src/stage/crew → serenity-bdd/src/stage}/serenity-bdd-reporter/strategies/index.ts
  86. +10 −0 packages/serenity-bdd/tsconfig-lint.json
  87. +22 −0 packages/serenity-bdd/tsconfig.json
@@ -43,6 +43,7 @@
"@serenity-js/local-server": "2.0.1-alpha.75",
"@serenity-js/protractor": "2.0.1-alpha.75",
"@serenity-js/rest": "2.0.1-alpha.75",
"@serenity-js/serenity-bdd": "2.0.1-alpha.75",
"cheerio": "1.0.0-rc.2",
"clean-css": "4.2.1",
"glob": "7.1.3",
@@ -1,5 +1,6 @@
import { ArtifactArchiver, ConsoleReporter, serenity, SerenityBDDReporter, WithStage } from '@serenity-js/core';
import { ArtifactArchiver, ConsoleReporter, serenity, WithStage } from '@serenity-js/core';
import { FileSystem, Path } from '@serenity-js/core/lib/io';
import { SerenityBDDReporter } from '@serenity-js/serenity-bdd';

import { setDefaultTimeout, setWorldConstructor } from 'cucumber';
import { Actors } from './screenplay';
@@ -1,6 +1,7 @@
import { serenity } from '@serenity-js/core';
import { FileSystem, Path } from '@serenity-js/core/lib/io';
import { ArtifactArchiver, SerenityBDDReporter } from '@serenity-js/core/lib/stage';
import { ArtifactArchiver } from '@serenity-js/core/lib/stage';
import { SerenityBDDReporter } from '@serenity-js/serenity-bdd';

import { setDefaultTimeout } from 'cucumber';

@@ -1,5 +1,6 @@
import { ArtifactArchiver, ConsoleReporter, serenity, SerenityBDDReporter, WithStage } from '@serenity-js/core';
import { ArtifactArchiver, ConsoleReporter, serenity, WithStage } from '@serenity-js/core';
import { FileSystem, Path } from '@serenity-js/core/lib/io';
import { SerenityBDDReporter } from '@serenity-js/serenity-bdd';

import { setDefaultTimeout, setWorldConstructor } from 'cucumber';
import { Actors } from './screenplay';
@@ -33,7 +33,6 @@
"cuid": "2.1.1",
"error-stack-parser": "2.0.2",
"graceful-fs": "4.1.11",
"mkdirp": "0.5.1",
"moment": "2.22.2",
"semver": "^6.0.0",
"tiny-types": "1.12.1",
@@ -45,7 +44,7 @@
"@types/mkdirp": "0.5.2",
"@types/sanitize-filename": "1.1.28",
"@types/semver": "^6.0.0",
"memfs": "2.15.0"
"memfs": "2.15.5"
},
"repository": {
"type": "git",
@@ -11,7 +11,7 @@ describe ('FileSystem', () => {
originalJSON = { name: 'jan' },
processCWD = new Path('/Users/jan/projects/serenityjs');

describe ('storing JSON files', () => {
describe ('when storing JSON files', () => {

it ('stores a JSON file in a desired location', () => {
const
@@ -53,7 +53,7 @@ describe ('FileSystem', () => {
});
});

describe ('storing pictures', () => {
describe ('when storing pictures', () => {

it ('stores a base64-encoded picture at a desired location', () => {
const
@@ -82,6 +82,60 @@ describe ('FileSystem', () => {
});
});

describe ('when removing', () => {

describe('individual files', () => {

it('removes the file', () => {
const
fs = FakeFS.with({
[processCWD.value]: {
outlet: {
subdir: {
'file-to-be-deleted.json': '{}',
'file-not-to-be-deleted.json': '{}',
},
},
},
}),
out = new FileSystem(processCWD, fs);

return out.remove(new Path('outlet/subdir/file-to-be-deleted.json')).then(() => {

expect(fs.existsSync(processCWD.join(new Path('outlet/subdir/file-to-be-deleted.json')).value)).to.equal(false);
expect(fs.existsSync(processCWD.join(new Path('outlet/subdir/file-not-to-be-deleted.json')).value)).to.equal(true);
});
});
});

describe('directories', () => {

it('removes the directory recursively', () => {
const
fs = FakeFS.with({
[processCWD.value]: {
outlet: {
subdir: {
'file-to-be-deleted.json': '{}',
},
another: {
'file-not-to-be-deleted.json': '{}',
},
},
},
}),
out = new FileSystem(processCWD, fs);

return out.remove(new Path('outlet/subdir')).then(() => {

expect(fs.existsSync(processCWD.join(new Path('outlet/subdir/file-to-be-deleted.json')).value)).to.equal(false);
expect(fs.existsSync(processCWD.join(new Path('outlet/subdir')).value)).to.equal(false);
expect(fs.existsSync(processCWD.join(new Path('outlet/another/file-not-to-be-deleted.json')).value)).to.equal(true);
});
});
});
});

function jsonFrom(file: Buffer) {
return JSON.parse(file.toString('ascii'));
}
@@ -44,6 +44,19 @@ describe ('Path', () => {
expect(p1.join(p2)).to.equal(new Path('/home/jan/file.json'));
});

it('can split an absolute path', () => {
const
p = new Path('/home/jan/directory/file.json');

expect(p.split()).to.deep.equal([ 'home', 'jan', 'directory', 'file.json' ]);
});

it('can split a relative path', () => {
const p = new Path('directory/file.json');

expect(p.split()).to.deep.equal([ 'directory', 'file.json' ]);
});

it('can resolve two paths', () => {
const
p1 = new Path('/home/jan/documents'),
@@ -52,6 +65,15 @@ describe ('Path', () => {
expect(p1.resolve(p2)).to.equal(new Path('/home/jan/projects'));
});

it('knows the root directory', () => {
expect(new Path('/home/jan/documents').root()).to.equal(new Path('/'));
});

it(`knows if it's absolute or relative`, () => {
expect(new Path('/home/jan/documents').isAbsolute()).to.equal(true);
expect(new Path('documents').isAbsolute()).to.equal(false);
});

given(
{ description: 'file in a sub-directory', path: new Path('/home/jan/file.json'), expected: new Path('/home/jan') },
{ description: 'sub-directory', path: new Path('/home/jan'), expected: new Path('/home') },
@@ -1,6 +1,4 @@
import { Serialised } from 'tiny-types';

import { Artifact, JSONData, Name, Photo } from '../../src/model';
import { Artifact, JSONData, Photo } from '../../src/model';
import { expect } from '../expect';

describe ('Artifact', () => {
@@ -1,35 +1,86 @@
import * as gracefulFs from 'graceful-fs';
import * as mkdirp from 'mkdirp';
import * as nodeFS from 'fs';
import * as gracefulFS from 'graceful-fs';
import { promisify } from 'util';

import { Path } from './Path';

export class FileSystem {

constructor(
private readonly root: Path,
private readonly fs = gracefulFs,
private readonly fs: typeof nodeFS = gracefulFS,
private readonly directoryMode = parseInt('0777', 8) & (~process.umask()),
) {
}

public store(relativePathToFile: Path, data: any, encoding?: string): Promise<Path> {
return Promise.resolve(relativePathToFile)
.then(relativePath => this.prepareDirectory(relativePath))
.then(absolutePath => this.write(absolutePath, data, encoding));
public store(relativeOrAbsolutePathToFile: Path, data: any, encoding?: string): Promise<Path> {
return Promise.resolve()
.then(() => this.ensureDirectoryExistsAt(relativeOrAbsolutePathToFile.directory()))
.then(() => this.write(this.root.resolve(relativeOrAbsolutePathToFile), data, encoding));
}

private prepareDirectory(relativePathToFile: Path): PromiseLike<Path> {
const
absolutePath = this.root.resolve(relativePathToFile),
parent = absolutePath.directory();
public createWriteStreamTo(relativeOrAbsolutePathToFile: Path): nodeFS.WriteStream {
return this.fs.createWriteStream(this.root.resolve(relativeOrAbsolutePathToFile).value);
}

return new Promise((resolve, reject) => {
public stat(relativeOrAbsolutePathToFile: Path): Promise<nodeFS.Stats> {
const stat = promisify(this.fs.stat);

mkdirp(parent.value, { fs: this.fs }, error => {
return error
? reject(error)
: resolve(absolutePath);
});
});
return stat(this.root.resolve(relativeOrAbsolutePathToFile).value);
}

public remove(relativeOrAbsolutePathToFileOrDirectory: Path): Promise<void> {
const stat = promisify(this.fs.stat),
unlink = promisify(this.fs.unlink),
readdir = promisify(this.fs.readdir),
rmdir = promisify(this.fs.rmdir);

const absolutePath = this.root.resolve(relativeOrAbsolutePathToFileOrDirectory);

return stat(absolutePath.value)
.then(result =>
result.isFile()
? unlink(absolutePath.value)
: readdir(absolutePath.value).then(entries =>
Promise.all(entries.map(entry =>
this.remove(absolutePath.join(new Path(entry)))),
).then(() => rmdir(absolutePath.value)),
),
)
.then(() => void 0);
}

public ensureDirectoryExistsAt(relativeOrAbsolutePathToDirectory: Path): Promise<Path> {

const absolutePath = this.root.resolve(relativeOrAbsolutePathToDirectory);

return absolutePath.split().reduce((promisedParent, child) => {
return promisedParent.then(parent => new Promise((resolve, reject) => {
const current = parent.resolve(new Path(child));

this.fs.mkdir(current.value, this.directoryMode, error => {
if (! error || error.code === 'EEXIST') {
return resolve(current);
}

// To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
if (error.code === 'ENOENT') { // Throw the original parentDir error on `current` `ENOENT` failure.
throw new Error(`EACCES: permission denied, mkdir '${ parent.value }'`);
}

const caughtErr = !! ~['EACCES', 'EPERM', 'EISDIR'].indexOf(error.code);
if (! caughtErr || caughtErr && current.equals(relativeOrAbsolutePathToDirectory)) {
throw error; // Throw if it's just the last created dir.
}
});
}));
}, Promise.resolve(absolutePath.root()));
}

public rename(source: Path, destination: Path): Promise<void> {
const rename = promisify(this.fs.rename);

return rename(source.value, destination.value);
}

private write(path: Path, data: any, encoding?: string): Promise<Path> {
@@ -20,7 +20,9 @@ export class Path extends TinyType {
}

split(): string[] {
return this.value.split(Path.Separator);
return this.value
.split(Path.Separator)
.filter(segment => !! segment); // so that we ignore the trailing path separator in absolute paths
}

resolve(another: Path) {
@@ -34,4 +36,12 @@ export class Path extends TinyType {
basename(): string {
return path.basename(this.value);
}

isAbsolute(): boolean {
return path.isAbsolute(this.value);
}

root(): Path {
return new Path(path.parse(this.value).root);
}
}
@@ -1,4 +1,3 @@
export * from './artifact-archiver';
export * from './console-reporter';
export * from './stream-reporter';
export * from './serenity-bdd-reporter';
@@ -0,0 +1 @@
module.exports = require('../../.esdoc');
@@ -0,0 +1,12 @@
# Node
node_modules
target/
*.log

# Build artifacts
.nyc_output
lib
staging

# Serenity BDD jars
cache
@@ -0,0 +1,14 @@
# Node
node_modules
*.log

# Build artifacts
target

# Specs
spec

# Config
.*
tsconfig.json
tsconfig-*.json

0 comments on commit fe7cfca

Please sign in to comment.