Skip to content

Commit

Permalink
feat(screenplay): New Questions and Interactions
Browse files Browse the repository at this point in the history
`Hit` interaction allows for a key stroke to be sent into an element. There are several new

questions too: `Attribute` - to retrieve a value of an element attribute, `Value` - to get a value

of an input element, `Website` to retrieve properties of the website, such as a title.
  • Loading branch information
jan-molak committed Sep 10, 2016
1 parent df39a1a commit abf8611
Show file tree
Hide file tree
Showing 25 changed files with 649 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ before_install:
before_script:
- npm prune
script:
- npm run package
- npm run verify
after_success:
- npm run report-coverage
- npm run semantic-release
Expand Down
56 changes: 56 additions & 0 deletions behaviour/protractor/integration/interactions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { by, protractor } from 'protractor/globals';

import {
BrowseTheWeb,
Click,
Enter,
Open,
Target,
Text,
Value,
Website,
} from '../../../src/serenity-protractor/screenplay';
import { Actor } from '../../../src/serenity/screenplay';

import test = require('selenium-webdriver/testing');

import expect = require('../../expect');
import { Attribute } from '../../../src/serenity-protractor/screenplay/questions/attribute';

class DemoApp {
static Header = Target.the('header').located(by.css('header>h1'));
static Item_Field = Target.the('name field').located(by.model('item'));
static Items = Target.the('list of items').located(by.repeater('item in items'));
static Submit_Button = Target.the('submit button').located(by.css('input[type="submit"]'));
}

test.describe ('Interactions', () => {

let james = Actor.named('James').whoCan(BrowseTheWeb.using(protractor.browser));

test.beforeEach(() => james.attemptsTo(
Open.browserOn('resources/index.html')
));

test.it ('should allow to verify the title of a page', () =>
expect(james.toSee(Website.title())).eventually.equal('demo app') );

test.it ('should allow to verify text on the page', () =>
expect(james.toSee(Text.of(DemoApp.Header))).eventually.equal('Angular demo app') );

test.it ('should allow to interact with an input field', () =>
james.attemptsTo(
Enter.theValue('buy some milk').into(DemoApp.Item_Field),
Click.on(DemoApp.Submit_Button)
).then(() =>
expect(james.toSee(Text.ofAll(DemoApp.Items))).eventually.deep.equal([ 'buy some milk' ]) ));

test.it ('should allow to read the value of an input field', () =>
james.attemptsTo(
Enter.theValue('buy some milk').into(DemoApp.Item_Field)
).then(() =>
expect(james.toSee(Value.of(DemoApp.Item_Field))).eventually.equal('buy some milk') ));

test.it ('should allow to read the value of an arbitrary attribute', () =>
expect(james.toSee(Attribute.of(DemoApp.Item_Field).called('id'))).eventually.equal('item') );
});
39 changes: 39 additions & 0 deletions behaviour/protractor/protractor.conf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';
require('ts-node/register');

var path = require('path'),
protractor = require.resolve('protractor'),
node_modules = protractor.substring(0, protractor.lastIndexOf('node_modules') + 12);

exports.config = {
seleniumServerJar: path.resolve(node_modules, 'protractor/node_modules/webdriver-manager/selenium/selenium-server-standalone-2.53.1.jar'),

framework: 'mocha',
specs: [ 'integration/**/*.js' ],

capabilities: {
'browserName': 'phantomjs',
'phantomjs.binary.path': path.resolve(node_modules, 'phantomjs-prebuilt/lib/phantom/bin/phantomjs'),
},

// capabilities: {
// browserName: 'chrome',
// chromeOptions: {
// args: [
// 'incognito'
// // 'show-fps-counter=true'
// ]
// }
// },

baseUrl: 'file://' + __dirname + '/',

onPrepare: function () {

// By default, Protractor uses data:text/html,<html></html> as resetUrl, but
// location.replace from the data: to the file: protocol is not allowed
// (we'll get ‘not allowed local resource’ error), so we replace resetUrl with one
// that uses the file: protocol
browser.resetUrl = 'file://';
}
};
318 changes: 318 additions & 0 deletions behaviour/protractor/resources/angular.min.js

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions behaviour/protractor/resources/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html ng-app="demo">
<head>
<script src="angular.min.js"></script>
<title>demo app</title>
</head>
<body ng-controller="main">


<header>
<h1>{{ title }}</h1>
</header>

<form>
<label for="item">name</label>
<input id="item" type="text" ng-model="item" />
<input type="submit" ng-click="add(item)" value="add" />
</form>

<ul>
<li ng-repeat="item in items track by $index">{{ item }}</li>
</ul>

<script>
angular.module('demo', []).
controller('main', ['$scope', function ($scope) {
$scope.title = 'Angular demo app';
$scope.items = [];

$scope.add = function (item) {
$scope.items.push(item);
}
}]);
</script>
</body>
</html>
4 changes: 1 addition & 3 deletions behaviour/spawner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ export class Spawned {

constructor(pathToScript: string, args: string[], options: ForkOptions) {

// let spawned = childProcess.fork(pathToScript, args, options);

let spawned = childProcess.fork(istanbul, ['cover',
'--dir', `${ process.cwd() }/${ dirs.staging.reports.coverage.behaviour }`,
'--dir', `${ process.cwd() }/${ dirs.staging.reports.coverage.behaviour.cucumber }`,
'--root', src,
'--report', 'json',
'--include-pid',
Expand Down
25 changes: 18 additions & 7 deletions gulpfile.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ module.exports = {
coverage: {
all: 'staging/reports/coverage',
spec: 'staging/reports/coverage/spec',
behaviour: 'staging/reports/coverage/behaviour',
behaviour: {
all: 'staging/reports/coverage/behaviour',
cucumber: 'staging/reports/coverage/behaviour/cucumber',
protractor: 'staging/reports/coverage/behaviour/protractor'
},
}
},
traspiled: {
all: 'staging/transpiled',
src: 'staging/transpiled/src/**/*.js',
spec: 'staging/transpiled/spec/**/*.js',
behaviour: 'staging/transpiled/behaviour/**/*.js',
export: 'staging/transpiled/src/**/*'
all: 'staging/transpiled',
src: 'staging/transpiled/src/**/*.js',
spec: 'staging/transpiled/spec/**/*.js',
behaviour: {
cucumber: 'staging/transpiled/behaviour/cucumber/*.js',
protractor: 'staging/transpiled/behaviour/protractor/protractor.conf.js'
},
export: 'staging/transpiled/src/**/*'
}
},
export: 'lib',
Expand All @@ -23,7 +30,11 @@ module.exports = {
spec: 'spec/**/*.ts',
behaviour: {
spec: 'behaviour/**/*.ts',
examples: [ 'behaviour/**/cucumber/features/**/*', 'behaviour/**/cucumber/**/*.js' ]
examples: [
'behaviour/**/cucumber/features/**/*',
'behaviour/**/protractor.conf.js',
'behaviour/**/resources/**/*'
]
},

typings: 'typings/index.d.ts'
Expand Down
22 changes: 10 additions & 12 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use strict';

const gulp = require('gulp'),
clean = require('gulp-clean'),
tslint = require("gulp-tslint"),
ts = require('gulp-typescript'),
merge = require('merge2'),
Expand All @@ -16,10 +15,7 @@ const gulp = require('gulp'),
project = ts.createProject('tsconfig.json'),
dirs = require('./gulpfile.config');


gulp.task('clean', () => gulp.src([dirs.staging.all, dirs.export], { read: false }).pipe(clean()));

gulp.task("lint", () =>
gulp.task('lint', () =>
gulp.src([ dirs.src, dirs.spec, dirs.behaviour.spec, '!**/*.d.ts' ])
.pipe(tslint({
formatter: "verbose"
Expand Down Expand Up @@ -61,8 +57,8 @@ gulp.task('test', ['pre-test'], () =>
// .pipe(istanbul.enforceThresholds({ thresholds: { global: 70 } }))
);

gulp.task('verify', ['pre-test', 'prepare-examples'], () =>
gulp.src(dirs.staging.traspiled.behaviour)
gulp.task('verify:cucumber', ['pre-test', 'prepare-examples'], () =>
gulp.src(dirs.staging.traspiled.behaviour.cucumber)
.pipe(mocha())
);

Expand All @@ -79,8 +75,12 @@ gulp.task('aggregate', () => {

return merge([
remapCoverageToTypescript(dirs.staging.reports.coverage.spec),
remapCoverageToTypescript(dirs.staging.reports.coverage.behaviour)
]).pipe(report({
remapCoverageToTypescript(dirs.staging.reports.coverage.behaviour.protractor),
remapCoverageToTypescript(dirs.staging.reports.coverage.behaviour.cucumber)
])
// .pipe(gulp.dest(dirs.staging.reports.coverage.all));

.pipe(report({
dir: dirs.staging.reports.coverage.all,
reporters: [
'text-summary',
Expand All @@ -97,6 +97,4 @@ gulp.task('export', () =>
.pipe(gulp.dest(dirs.export))
);

gulp.task('package', (done) => runSequence('clean', 'lint', 'test', 'verify', 'aggregate', 'export'));

gulp.task('default', [ 'package' ], () => {});
gulp.task('package', (done) => runSequence('lint', 'test', 'export'));
7 changes: 1 addition & 6 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 13 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": {
"clean": "gulp clean",
"clean": "rimraf staging lib",
"pretest": "npm run webdriver:update -- --standalone",
"test": "gulp test",
"package": "gulp package",
"behaviour:cucumber": "gulp verify:cucumber",
"behaviour:protractor": "istanbul cover --dir staging/reports/coverage/behaviour/protractor --root staging/transpiled/src --report json --include-pid --include-all-sources protractor -- staging/transpiled/behaviour/protractor/protractor.conf.js",
"behaviour": "npm run behaviour:cucumber && npm run behaviour:protractor",
"aggregate": "gulp aggregate",
"verify": "npm run clean && npm run package && npm run behaviour && npm run aggregate",
"verify:protractor": "npm run clean && npm run package && gulp pre-test prepare-examples && npm run behaviour:protractor && npm run aggregate",
"setup:typings": "npm run typings -- install",
"setup:webdriver": "npm run webdriver:update -- --standalone",
"setup": "npm run setup:typings && npm run setup:webdriver",
"prepublish": "npm run setup",
"package": "gulp package",
"pretest": "npm run webdriver:update -- --standalone",
"test": "gulp test",
"commit": "git-cz",
"typings": "typings",
"webdriver-manager": "webdriver-manager",
Expand All @@ -41,7 +47,6 @@
"cucumber": "1.2.2",
"cz-conventional-changelog": "1.1.7",
"gulp": "3.9.1",
"gulp-clean": "0.3.2",
"gulp-concat": "2.6.0",
"gulp-istanbul": "1.0.0",
"gulp-istanbul-report": "0.0.1",
Expand All @@ -57,17 +62,18 @@
"phantomjs-prebuilt": "2.1.7",
"protractor-cucumber-framework": "0.6.0",
"remap-istanbul": "0.6.4",
"rimraf": "2.5.4",
"run-sequence": "1.2.2",
"selenium-webdriver": "2.53.3",
"semantic-release": "^4.3.5",
"sinon": "1.17.5",
"sinon-chai": "2.8.0",
"split": "1.0.0",
"ts-helper": "0.0.1",
"ts-node": "1.2.2",
"tslint": "3.14.0",
"typescript": "1.8.10",
"typings": "1.3.2",
"semantic-release": "^4.3.5"
"typings": "1.3.2"
},
"repository": {
"type": "git",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ describe('Interactions', () => {
let promise = Enter.theValue('Jan').into(target).thenHit(webdriver.Key.ENTER).performAs(actor);

return expect(promise).to.be.eventually.fulfilled.then(() => {
expect(element.sendKeys).to.have.been.calledWithExactly('Jan', webdriver.Key.ENTER);
expect(element.sendKeys).to.have.been.calledWith('Jan');
expect(element.sendKeys).to.have.been.calledWith(webdriver.Key.ENTER);
});
});
});
Expand Down
18 changes: 18 additions & 0 deletions spec/serenity-protractor/keys.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { keyNameOf } from '../../src/serenity-protractor/keys';

import expect = require('../expect');
import * as webdriver from 'selenium-webdriver';

describe('Protractor', () => {

describe ('keyNameOf', () => {

it ('should determine the name of the key pressed', () => {
expect(keyNameOf(webdriver.Key.ENTER)).to.equal('ENTER');
});

it ('should return the name of the searched for key, if it could not be found', () => {
expect(keyNameOf('unknown-key')).to.equal('unknown-key');
});
});
});
2 changes: 1 addition & 1 deletion spec/serenity/stage/console_reporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ConsoleReporter, Journal, Stage, StageManager } from '../../../src/sere
import sinon = require('sinon');

import expect = require('../../expect');
import { consoleReporter } from '../../../src/serenity/stage/console_reporter';
import { consoleReporter } from '../../../src/stage_crew';

describe ('When reporting on what happened during the rehearsal', () => {

Expand Down
3 changes: 2 additions & 1 deletion spec/serenity/stage/json_reporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
Tag,
} from '../../../src/serenity/domain';
import { FileSystem } from '../../../src/serenity/io/file_system';
import { Journal, JsonReporter, Stage, StageManager, jsonReporter } from '../../../src/serenity/stage';
import { Journal, JsonReporter, Stage, StageManager } from '../../../src/serenity/stage';
import { jsonReporter } from '../../../src/stage_crew';

import expect = require('../../expect');

Expand Down
18 changes: 18 additions & 0 deletions src/serenity-protractor/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { protractor } from 'protractor';
import * as webdriver from 'selenium-webdriver';

export function keyNameOf(key: string) {
let keys = definitionsFrom(protractor, webdriver);

for ( let candidate in keys ) {
if ( keys.hasOwnProperty(candidate) && keys[ candidate ] === key ) {
return candidate;
}
}

return key;
}

function definitionsFrom(...potentialSources) {
return potentialSources.concat({ Key: [] }).find(source => source && source.Key).Key;
}
Loading

0 comments on commit abf8611

Please sign in to comment.