Permalink
Browse files

feat(serenity-protractor): Serenity can auto-detect the appropriate t…

…est dialect

You can specify whether you'd like to use cucumber or mocha by specifying the `serenity.dialect:

...` property, but if you don't, Serenity will try to guess what you're after based on the presence

of `cucumberOpts` or `mochaOpts` property in your protractor.conf.js
  • Loading branch information...
jan-molak committed Jan 29, 2017
1 parent b6caf89 commit c50a4bf70d58f291b6ba4695f2f4497c4d84d99a
@@ -2,9 +2,6 @@ import sinon = require('sinon');
import expect = require('../../../expect');
import { given } from 'mocha-testdata';
import { Runner } from 'protractor/built/runner';
import { ProtractorReportExporter } from '../../../../src/serenity-protractor/framework';
import { ProtractorReporter } from '../../../../src/serenity-protractor/framework/protractor_reporter';
import {
Activity,
ActivityFinished,
@@ -16,6 +13,9 @@ import {
SceneFinished,
SceneStarts,
} from '../../../../src/serenity/domain';
import { ProtractorReporter } from '../../../../src/serenity-protractor/reporting';
import { ProtractorReportExporter } from '../../../../src/serenity-protractor/reporting/protractor_reporter';
import { RehearsalReport } from '../../../../src/serenity/reporting';
describe('serenity-protractor', () => {
@@ -0,0 +1,128 @@
import sinon = require('sinon');
import expect = require('../../../expect');
import { CucumberTestFramework } from '../../../../src/serenity-cucumber/cucumber_test_framework';
import { MochaTestFramework } from '../../../../src/serenity-mocha/mocha_test_framework';
import { TestFrameworkDetector } from '../../../../src/serenity-protractor/framework/test_framework_detector';
describe('serenity-protractor', () => {
describe('framework', () => {
describe('TestFrameworkDetector', () => {
const detect = new TestFrameworkDetector();
describe('Correctly instantiates the required framework', () => {
describe('when the preferred `dialect`', () => {
it('is set to `cucumber`', () => {
let framework = detect.frameworkFor({
serenity: {
dialect: 'cucumber',
},
configDir: '/path/to/protractor/configuration',
});
expect(framework).to.be.an.instanceOf(CucumberTestFramework);
});
it('is set to `mocha`', () => {
let framework = detect.frameworkFor({
serenity: {
dialect: 'mocha',
},
configDir: '/path/to/protractor/configuration',
});
expect(framework).to.be.an.instanceOf(MochaTestFramework);
});
});
describe('when the `dialect` is not set, but the config', () => {
it('seems like `cucumber` because of the `cucumberOpts`', () => {
let framework = detect.frameworkFor({
cucumberOpts: {
require: [ 'features/**/*.ts' ],
format: 'pretty',
},
configDir: '/path/to/protractor/configuration',
});
expect(framework).to.be.an.instanceOf(CucumberTestFramework);
});
it('seems like `cucumber` because of the `cucumberOpts` defined in capabilities', () => {
let framework = detect.frameworkFor({
capabilities: {
cucumberOpts: {
require: [ 'features/**/*.ts' ],
format: 'pretty',
},
},
configDir: '/path/to/protractor/configuration',
});
expect(framework).to.be.an.instanceOf(CucumberTestFramework);
});
it('seems like `mocha` because of the `mochaOpts`', () => {
let framework = detect.frameworkFor({
mochaOpts: {
ui: 'bdd',
},
});
expect(framework).to.be.an.instanceOf(MochaTestFramework);
});
it('seems like `mocha` because of the `mochaOpts` defined in capabilities', () => {
let framework = detect.frameworkFor({
capabilities: {
mochaOpts: {
ui: 'bdd',
},
},
});
expect(framework).to.be.an.instanceOf(MochaTestFramework);
});
});
});
describe('When the framework could not be detected', () => {
it('advises how it can be fixed', () => {
expect(() => detect.frameworkFor({
// no relevant config
})).to.throw(
'Serenity/JS could not determine the test dialect you wish to use. ' +
'Please add `serenity: { dialect: \'...\' }` to your Protractor configuration file ' +
'and choose one of the following options: cucumber, mocha.',
);
});
});
describe('When the framework is not (yet) supported', () => {
it('advises how to contact the Serenity/JS team', () => {
expect(() => detect.frameworkFor({
serenity: {
dialect: 'something-new',
},
} as any)).to.throw(
'Serenity/JS does not (yet) support the \'something-new\' test framework. ' +
'Please let us know on github if you\'d like to see it added!',
);
});
});
});
});
});
@@ -1,4 +1,4 @@
import { SerenityProtractorFramework, TestFramework } from '../serenity-protractor/framework';
import { TestFramework } from '../serenity-protractor/framework';
import _ = require('lodash');
import glob = require('glob');
@@ -9,7 +9,7 @@ export class CucumberTestFramework implements TestFramework {
private args: string[] = [];
constructor(private serenity: SerenityProtractorFramework, config: CucumberConfig) {
constructor(private requireRoot: string, config: CucumberConfig) {
this.args = ['node', 'cucumberjs'].
concat([ '--require', this.serenityCucumberModule() ]).
concat(this.argumentsFrom(config));
@@ -33,7 +33,7 @@ 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 resolveGlobs = (path: string) => glob.sync(path, { cwd: this.requireRoot });
const resolvePaths = (globPath: string[]) => _.chain(globPath).map(resolveGlobs).flatten().value();
const resolvedConfig = Object.assign({}, config, { require: resolvePaths(config.require || []) } );
@@ -54,12 +54,12 @@ export interface CucumberConfig {
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#formats
*/
format?: string[];
format?: string;
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#format-options
*/
formatOptions: string;
formatOptions?: string;
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#running-specific-features
@@ -69,7 +69,7 @@ export interface CucumberConfig {
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#profiles
*/
profile: string[];
profile?: string[];
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#requiring-support-files
@@ -79,10 +79,10 @@ export interface CucumberConfig {
/**
* @link https://docs.cucumber.io/tag-expressions/
*/
tags: string;
tags?: string;
/**
* @link https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#world-parameters
*/
worldParameters: string;
worldParameters?: string;
}
@@ -32,7 +32,7 @@ export interface MochaConfig {
/**
* String or regexp to filter tests with
*/
grep: string | RegExp;
grep?: string | RegExp;
/**
* Number of times to retry failed tests
@@ -1,5 +1,4 @@
import { SerenityProtractorFramework } from './serenity_protractor_framework';
export { ProtractorReportExporter } from './protractor_report_exporter'
export { run, SerenityProtractorFramework } from './serenity_protractor_framework';
export { TestFramework } from './test_framework';

This file was deleted.

Oops, something went wrong.
@@ -5,7 +5,7 @@ import { MochaConfig } from '../../serenity-mocha';
import { StageCrewMember } from '../../serenity/stage';
export interface SerenityFrameworkConfig extends Config {
serenity: {
serenity?: {
crew?: StageCrewMember[];
dialect?: 'cucumber' | 'mocha';
};
@@ -1,12 +1,10 @@
import { Runner } from 'protractor';
import { serenity, Serenity } from '../..';
import { CucumberTestFramework } from '../../serenity-cucumber/cucumber_test_framework';
import { MochaTestFramework } from '../../serenity-mocha';
import { jsonReporter } from '../../serenity/stage/json_reporter';
import { ProtractorReport } from './protractor_report';
import { ProtractorReporter } from './protractor_reporter';
import { ProtractorReport, ProtractorReporter } from '../reporting';
import { SerenityFrameworkConfig } from './serenity_framework_config';
import { TestFramework } from './test_framework';
import { TestFrameworkDetector } from './test_framework_detector';
// spec: https://github.com/angular/protractor/blob/master/lib/frameworks/README.md
@@ -19,58 +17,33 @@ export function run(runner: Runner, specs: string[]): PromiseLike<ProtractorRepo
export class SerenityProtractorFramework {
public config: SerenityFrameworkConfig;
private framework: TestFramework;
private reporter: ProtractorReporter;
constructor(private serenity: Serenity, private runner: Runner) {
this.config = Object.assign({}, this.defaultConfig(), runner.getConfig());
this.reporter = new ProtractorReporter(runner);
this.config = Object.assign({}, this.defaultConfig(), runner.getConfig());
this.reporter = new ProtractorReporter(runner);
this.framework = new TestFrameworkDetector().frameworkFor(this.config);
this.serenity.assignCrewMembers(...this.config.serenity.crew, this.reporter);
}
run(specs: string[]): PromiseLike<ProtractorReport> {
const framework = this.testFrameworkBasedOn(this.config);
return this.runner.runTestPreparer().then(() => {
return framework.
run(specs).
then(noop, this.analyzeTheFailure).
then(() => serenity.waitForAnyOutstandingTasks()).
then(() => this.waitForOtherProtractorPlugins()).
then(() => this.reporter.finalResults());
});
}
run = (specs: string[]): PromiseLike<ProtractorReport> => this.runner.runTestPreparer().then(() =>
this.framework.run(specs).
then(noop, this.analyzeTheFailure).
then(() => this.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);
}
return issue instanceof Error
? reject(issue)
: resolve(issue); // Cucumber returns "false" when the run fails and Mocha returns the number of failed tests.
// Both those 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.
})
private testFrameworkBasedOn(config: SerenityFrameworkConfig): TestFramework {
switch (config.serenity.dialect.toLowerCase()) {
case 'cucumber':
// tslint:disable-next-line:no-string-literal
const cucumberConfig = Object.assign({}, config.cucumberOpts, config.capabilities[ 'cucumberOpts' ]);
return new CucumberTestFramework(this, cucumberConfig);
case 'mocha':
// tslint:disable-next-line:no-string-literal
const mochaConfig = Object.assign({}, config.mochaOpts, config.capabilities[ 'mochaOpts' ]);
return new MochaTestFramework(mochaConfig);
default:
throw new Error('Handle this better');
}
}
private waitForOtherProtractorPlugins = () => Promise.resolve(this.config.onComplete || noop);
private defaultConfig = (): SerenityFrameworkConfig => ({
Oops, something went wrong.

0 comments on commit c50a4bf

Please sign in to comment.