Permalink
Browse files

feat(serenity-protractor): SerenityProtractorFramework advises how to…

… install a missing test runner.

Instead of assuming that either of the optional test runners (`mocha` or `cucumber`) are available,

SerenityProtractorFramework performs a check first and advises the developer how to install the

missing module.
  • Loading branch information...
jan-molak committed Jan 28, 2017
1 parent 6cf0197 commit b6caf89375ab7d4c4258755e015f8de55515c52e
@@ -0,0 +1,24 @@
import expect = require('../../../expect');
import { attemptToRequire } from '../../../../src/serenity/io';
describe ('io', () => {
describe ('attemptRequire', () => {
it ('loads a module if it has been installed', () => {
let Mocha = attemptToRequire('mocha');
expect(Mocha).to.not.be.null;
expect(Mocha).to.be.instanceof(Function);
});
it ('loads a module if it has been installed', () => {
expect(() => attemptToRequire('non-existent')).
to.throw('This feature requires the \'non-existent\' module, which seems to be missing. ' +
'To install it, run: `npm install non-existent --save[-dev]`');
});
});
});
@@ -3,27 +3,24 @@ import { SerenityProtractorFramework, TestFramework } from '../serenity-protract
import _ = require('lodash');
import glob = require('glob');
import path = require('path');
// todo: maybe some conditional loading, checking if the module exists?
const Cucumber: any = require('cucumber'); // tslint:disable-line:no-var-requires
import { attemptToRequire } from '../serenity/io/attempt_require';
export class CucumberTestFramework implements TestFramework {
constructor(private serenity: SerenityProtractorFramework, private config: CucumberConfig) {
private args: string[] = [];
constructor(private serenity: SerenityProtractorFramework, config: CucumberConfig) {
this.args = ['node', 'cucumberjs'].
concat([ '--require', this.serenityCucumberModule() ]).
concat(this.argumentsFrom(config));
}
run(specs: string[]): PromiseLike<any> {
// so that it works with ts-node and with TypeScript transpiled to JavaScript
let serenityCucumber = glob.sync(path.resolve(__dirname, '../serenity-cucumber') + '/index.?s').pop();
let args = ['node', 'cucumberjs'].
concat([ '--require', serenityCucumber ]).
concat(this.argumentsFrom(this.config)).
concat(specs);
return new Promise((resolve, reject) => {
Cucumber.Cli(args).run(wasSuccessful => {
const Cucumber = attemptToRequire('cucumber');
Cucumber.Cli(this.args.concat(specs)).run(wasSuccessful => {
if (wasSuccessful) {
resolve(wasSuccessful);
} else {
@@ -33,6 +30,8 @@ export class CucumberTestFramework implements TestFramework {
});
}
private serenityCucumberModule = () => glob.sync(path.resolve(__dirname, '../serenity-cucumber') + '/index.?s').pop();
private argumentsFrom (config: CucumberConfig): string[]{
const resolveGlobs = (path: string) => glob.sync(path, { cwd: this.serenity.config.configDir });
const resolvePaths = (globPath: string[]) => _.chain(globPath).map(resolveGlobs).flatten().value();
@@ -6,14 +6,16 @@ import { endOf, ExecutedScenario, isPending, Scenario, startOf } from './model';
import _ = require('lodash');
import glob = require('glob');
import path = require('path');
import { attemptToRequire } from '../serenity/io';
import { MochaConfig } from './mocha_config';
export class MochaTestFramework implements TestFramework {
private mocha;
constructor(private config: MochaConfig) {
let Mocha = require('mocha');
const Mocha = attemptToRequire('mocha');
this.mocha = new Mocha(config);
@@ -35,16 +35,25 @@ export class SerenityProtractorFramework {
return this.runner.runTestPreparer().then(() => {
return framework.
run(specs).
// cucumber returns "false" when the run fails,
// but we don't care as it's Protractor's responsibility to return the correct error code
// based on the test results, hence `noop`
then(noop, console.warn).
then(noop, this.analyzeTheFailure).
then(() => serenity.waitForAnyOutstandingTasks()).
then(() => this.waitForOtherProtractorPlugins()).
then(() => this.reporter.finalResults());
});
}
private analyzeTheFailure = (issue: any) => new Promise((resolve, reject) => {
if (issue instanceof Error) {
return reject(issue);
}
else {
// Cucumber returns "false" when the run fails and Mocha returns the number of failed tests.
// both cases are handled by Protractor based on the final test results reported by Serenity/JS,
// so we don't need any additional error handling here.
return resolve(issue);
}
})
private testFrameworkBasedOn(config: SerenityFrameworkConfig): TestFramework {
switch (config.serenity.dialect.toLowerCase()) {
case 'cucumber':
@@ -0,0 +1,9 @@
export function attemptToRequire(module: string) {
try {
return require(module);
} catch (e) {
throw new Error(
`This feature requires the '${ module }' module, which seems to be missing. To install it, run: ` +
`\`npm install ${ module } --save[-dev]\``);
}
}
View
@@ -0,0 +1,3 @@
export * from './attempt_require';
export * from './file_system';
export * from './json';

0 comments on commit b6caf89

Please sign in to comment.