Skip to content

Commit

Permalink
feat(protractor): support for using Mocha with Protractor
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-molak committed Jun 18, 2020
1 parent a685afc commit ae5bd7e
Show file tree
Hide file tree
Showing 33 changed files with 904 additions and 12 deletions.
7 changes: 7 additions & 0 deletions integration/protractor-mocha/.gitignore
@@ -0,0 +1,7 @@
# Node
node_modules
*.log

# Build artifacts
.nyc_output
lib
9 changes: 9 additions & 0 deletions integration/protractor-mocha/examples/Actors.js
@@ -0,0 +1,9 @@
const { BrowseTheWeb } = require('@serenity-js/protractor');

module.exports.Actors = class Actors {
prepare(actor) {
return actor.whoCan(
BrowseTheWeb.using(require('protractor').browser),
);
}
};
11 changes: 11 additions & 0 deletions integration/protractor-mocha/examples/failing.spec.js
@@ -0,0 +1,11 @@
const assert = require('assert');

describe('Mocha', () => {

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

it('fails', () => {
assert.equal(false, true, 'Expected false to be true.');
});
});
});
@@ -0,0 +1,24 @@
const
{ actorCalled } = require('@serenity-js/core'),
{ UseAngular, Navigate } = require('@serenity-js/protractor');

describe('Mocha', () => {

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

it('passes the first time', () => actorCalled('Mocha').attemptsTo(
UseAngular.disableSynchronisation(),
Navigate.to('chrome://version/'),
));

it('passes the second time', () => actorCalled('Mocha').attemptsTo(
UseAngular.disableSynchronisation(),
Navigate.to('chrome://accessibility/'),
));

it('passes the third time', () => actorCalled('Mocha').attemptsTo(
UseAngular.disableSynchronisation(),
Navigate.to('chrome://chrome-urls/'),
));
});
});
9 changes: 9 additions & 0 deletions integration/protractor-mocha/examples/passing.spec.js
@@ -0,0 +1,9 @@
describe('Mocha', () => {

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

it('passes', () => {
// no-op
});
});
});
50 changes: 50 additions & 0 deletions integration/protractor-mocha/examples/protractor.conf.js
@@ -0,0 +1,50 @@
const
path = require('path'),
{ StreamReporter } = require('@serenity-js/core'),
{ ChildProcessReporter } = require('@integration/testing-tools'),
{ Actors } = require('./Actors');

exports.config = {
chromeDriver: require('chromedriver/lib/chromedriver').path,
SELENIUM_PROMISE_MANAGER: false,

directConnect: true,

allScriptsTimeout: 11000,

specs: [ '*.spec.js', ],

framework: 'custom',
frameworkPath: require.resolve('@serenity-js/protractor/adapter'),

serenity: {
runner: 'mocha',
actors: new Actors(),
crew: [
new ChildProcessReporter(),
new StreamReporter(),
]
},

mochaOpts: {

},

onPrepare: function() {
return browser.waitForAngularEnabled(false);
},

capabilities: {
browserName: 'chrome',

chromeOptions: {
args: [
'--disable-infobars',
'--no-sandbox',
'--disable-gpu',
'--window-size=1024x768',
'--headless',
],
},
},
};
@@ -0,0 +1,17 @@
describe('Mocha', () => {

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

let counter = 0;

it('passes the third time', () => {

if (counter++ < 3) {
throw new Error(`Something's happened`);
}

// third time lucky, isn't it?

});
});
});
22 changes: 22 additions & 0 deletions integration/protractor-mocha/examples/screenplay.spec.js
@@ -0,0 +1,22 @@
const
{ actorCalled } = require('@serenity-js/core'),
{ Navigate, UseAngular } = require('@serenity-js/protractor');

describe('Mocha', () => {

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

beforeEach(() => actorCalled('Mocha').attemptsTo(
UseAngular.disableSynchronisation(),
Navigate.to('chrome://version/'),
));

it('passes', () => actorCalled('Mocha').attemptsTo(
Navigate.to('chrome://accessibility/'),
));

afterEach(() => actorCalled('Mocha').attemptsTo(
Navigate.to('chrome://chrome-urls/'),
));
});
});
43 changes: 43 additions & 0 deletions integration/protractor-mocha/package.json
@@ -0,0 +1,43 @@
{
"name": "@integration/protractor-mocha",
"version": "2.10.2",
"description": "Protractor/Mocha integration tests",
"author": {
"name": "Jan Molak",
"email": "jan.molak@smartcodeltd.co.uk",
"url": "https://janmolak.com"
},
"homepage": "https://serenity-js.org",
"license": "Apache-2.0",
"private": true,
"config": {
"access": "private"
},
"scripts": {
"clean": "rimraf lib",
"lint": "tslint --project tsconfig-lint.json --config ../../tslint.json --format stylish",
"test": "mocha --config ../../.mocharc.yml 'spec/**/*.spec.*'"
},
"repository": {
"type": "git",
"url": "https://github.com/serenity-js/serenity-js.git"
},
"bugs": {
"url": "https://github.com/serenity-js/serenity-js/issues"
},
"engines": {
"node": ">= 10",
"npm": ">= 6"
},
"dependencies": {
"@integration/testing-tools": "2.10.2",
"@serenity-js/core": "2.10.2",
"@serenity-js/mocha": "2.10.2",
"@serenity-js/protractor": "2.10.2",
"chromedriver": "^83.0.0",
"protractor": "^7.0.0"
},
"devDependencies": {
"@types/mocha": "^7.0.2"
}
}
41 changes: 41 additions & 0 deletions integration/protractor-mocha/spec/failing_scenario.spec.ts
@@ -0,0 +1,41 @@
import 'mocha';

import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { AssertionError } from '@serenity-js/core';
import { SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected } from '@serenity-js/core/lib/events';
import { ExecutionFailedWithAssertionError, FeatureTag, Name, ProblemIndication } from '@serenity-js/core/lib/model';
import { protractor } from '../src/protractor';

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

this.timeout(30000);

it('recognises a failing scenario', () =>
protractor(
'./examples/protractor.conf.js',
'--specs=examples/failing.spec.js',
)
.then(ifExitCodeIsOtherThan(1, logOutput))
.then(res => {

expect(res.exitCode).to.equal(1);

PickEvent.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario fails')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Mocha')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Mocha')))
.next(SceneFinished, event => {
const outcome = event.outcome as ProblemIndication;

expect(outcome).to.be.instanceOf(ExecutionFailedWithAssertionError);

const error = outcome.error as AssertionError;

expect(error.constructor.name).to.match(/^AssertionError/);
expect(error.message).to.equal('Expected false to be true.');
expect(error.expected).to.equal('true');
expect(error.actual).to.equal('false');
})
;
}));
});
29 changes: 29 additions & 0 deletions integration/protractor-mocha/spec/passing_scenario.spec.ts
@@ -0,0 +1,29 @@
import 'mocha';

import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { SceneFinished, SceneStarts, SceneTagged, TestRunnerDetected } from '@serenity-js/core/lib/events';
import { ExecutionSuccessful, FeatureTag, Name } from '@serenity-js/core/lib/model';
import { protractor } from '../src/protractor';

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

this.timeout(30000);

it('recognises a passing scenario', () =>
protractor(
'./examples/protractor.conf.js',
'--specs=examples/passing.spec.js',
)
.then(ifExitCodeIsOtherThan(0, logOutput))
.then(res => {

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

PickEvent.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes')))
.next(SceneTagged, event => expect(event.tag).to.equal(new FeatureTag('Mocha')))
.next(TestRunnerDetected, event => expect(event.value).to.equal(new Name('Mocha')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))
;
}));
});
@@ -0,0 +1,107 @@
import { expect, ifExitCodeIsOtherThan, logOutput, PickEvent } from '@integration/testing-tools';
import { AsyncOperationAttempted, AsyncOperationCompleted, InteractionStarts, SceneFinished, SceneFinishes, SceneStarts, TestRunFinished } from '@serenity-js/core/lib/events';
import { Description, ExecutionSuccessful, Name } from '@serenity-js/core/lib/model';
import 'mocha';
import { protractor } from '../src/protractor';

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

/*
* See:
* - https://github.com/angular/protractor/issues/3234
* - https://github.com/jan-molak/serenity-js/issues/56
*/

this.timeout(30000);

it('supports restarting the browser between test scenarios', () =>
protractor(
'./examples/protractor.conf.js',
'--specs=examples/multiple_passing_scenarios.spec.js',
'--restartBrowserBetweenTests',
)
.then(ifExitCodeIsOtherThan(0, logOutput))
.then(res => {

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

PickEvent.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the first time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://version/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the first time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the second time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://accessibility/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the second time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the third time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://chrome-urls/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the third time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(TestRunFinished, event => expect(event.timestamp).to.not.be.undefined)
;
}));

it('produces the same result when the browser is not restarted between the tests', () =>
protractor(
'./examples/protractor.conf.js',
'--specs=examples/multiple_passing_scenarios.spec.js',
)
.then(ifExitCodeIsOtherThan(0, logOutput))
.then(res => {

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

PickEvent.from(res.events)
.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the first time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://version/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the first time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the second time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://accessibility/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the second time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(SceneStarts, event => expect(event.value.name).to.equal(new Name('A scenario passes the third time')))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha disables synchronisation with Angular`)))
.next(InteractionStarts, event => expect(event.value.name).to.equal(new Name(`Mocha navigates to 'chrome://chrome-urls/'`)))
.next(SceneFinishes, event => expect(event.value.name).to.equal(new Name('A scenario passes the third time')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] Invoking ProtractorRunner.afterEach...')))
.next(AsyncOperationAttempted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discards abilities...')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[Actor] Mocha discarded abilities successfully')))
.next(AsyncOperationCompleted, event => expect(event.taskDescription).to.equal(new Description('[ProtractorReporter] ProtractorRunner.afterEach succeeded')))
.next(SceneFinished, event => expect(event.outcome).to.equal(new ExecutionSuccessful()))

.next(TestRunFinished, event => expect(event.timestamp).to.not.be.undefined)
;
}));
});

0 comments on commit ae5bd7e

Please sign in to comment.