Permalink
Browse files

feat(serenity-protractor): Interactions: Execute.asyncScript(), Execu…

…te.script() and Evaluate.script()

New interactions help to apply changes to the internal state of an Angular application.

This is not something you should probably be using when writing a regular acceptance test,
but can come in handy when you need to create a "fast-track" equivalent of a slow UI-based task.

More about the "fast-track" pattern shortly at https://janmolak.com
  • Loading branch information...
jan-molak committed Feb 19, 2017
1 parent 74c1ab2 commit 0a4b08e263fd52798decff34539e4602f61ccb0f
@@ -0,0 +1,63 @@
import { by, protractor } from 'protractor';
import expect = require('../expect');
import { Actor, BrowseTheWeb, Evaluate, Execute, Open, Target, Text, Value } from '../../src/screenplay-protractor';
import { AppServer } from '../support/server';
class Username {
static Field = Target.the('username field').located(by.css('[name="text-input"] label[for="text"] input'));
static Result = Target.the('username value').located(by.css('[name="text-input"] label[for="text"] pre'));
}
class Newsletter {
static Checkbox = Target.the('newsletter checkbox').located(by.css('[name="checkboxes"] label[for="checkbox"] input'));
static Result = Target.the('newsletter result').located(by.css('[name="checkboxes"] label[for="checkbox"] pre'));
}
describe ('When working with an Angular app, a test scenario', function() {
this.timeout(10000);
const app = new AppServer();
const james = Actor.named('James').whoCan(BrowseTheWeb.using(protractor.browser));
before(app.start());
before(() => james.attemptsTo(Open.browserOn(app.demonstrating('angular_internals'))));
after(app.stop());
it ('can execute a script within its context', () =>
james.attemptsTo(
Execute.script('arguments[0].click();').on(Newsletter.Checkbox),
).then(() => Promise.all([
expect(james.toSee(Value.of(Newsletter.Checkbox))).eventually.equal('on'),
])));
it ('can execute an asynchronous script within its context', () =>
james.attemptsTo(
Execute.asyncScript(
'var callback = arguments[arguments.length - 1];',
'arguments[0].click();',
'callback();',
).on(Newsletter.Checkbox),
).then(() => Promise.all([
expect(james.toSee(Value.of(Newsletter.Checkbox))).eventually.equal('on'),
])));
it ('can execute an asynchronous script with arguments', () =>
james.attemptsTo(
Execute.asyncScript(
'var callback = arguments[arguments.length - 1];',
'arguments[0].text(arguments[1]);',
'callback();',
).on(Username.Field).withArguments('James'),
).then(() => Promise.all([
expect(james.toSee(Text.of(Username.Result))).eventually.equal('James'),
])));
it.only ('can evaluate an expression in the context of an Angular $scope', () =>
james.attemptsTo(
Evaluate.script('text.username = "James"; $apply();').on(Username.Field),
).then(() => Promise.all([
expect(james.toSee(Text.of(Username.Result))).eventually.equal('James'),
])));
});
@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<title>Basic Forms</title>
<style>
fieldset {
margin-bottom: 10px;
}
label {
display: block;
padding:10px;
margin:10px;
border:1px solid grey;
border-radius: 5px;
}
pre {
border: 1px dashed lightgrey;
padding:10px;
}
</style>
<script type="text/javascript" src="/js/angular.js"></script>
<script type="text/javascript">
angular.module('demo', [])
.controller('text-input', ['$scope', function ($scope) {
$scope.checkboxes = {
weekly_newsletter: false
};
}]);
</script>
</head>
<body ng-app="demo">
<form>
<fieldset name="text-input" ng-controller="text-input">
<legend>Working with Angular internals</legend>
<label for="text">
Username:
<input type="text" ng-model="text.username" id="text" />
<pre>{{ text.username }}</pre>
</label>
<label for="checkbox">
<input type="checkbox" ng-model="checkboxes.weekly_newsletter" id="checkbox" />
Select to receive the weekly newsletter
<pre>{{ checkboxes.weekly_newsletter }}</pre>
</label>
</fieldset>
</form>
</body>
</html>
@@ -74,7 +74,7 @@ describe ('When demonstrating the usage of an HTML form, a test scenario', funct
it ('can execute script to click a checkbox, as an example for script execution..', () =>
james.attemptsTo(
Execute.theScript('arguments[0].click();').on(Newsletter.Checkbox),
Execute.script('arguments[0].click();').on(Newsletter.Checkbox),
).then(() => Promise.all([
expect(james.toSee(Value.of(Newsletter.Checkbox))).eventually.equal('on'),
])));
@@ -76,6 +76,10 @@ export class BrowseTheWeb implements Ability {
return this.browser.executeScript(script, target.resolveUsing(this.browser.element));
}
executeAsyncScript(script: string | Function, target: Target, ...args: any[]): PromiseLike<any> {
return this.browser.executeAsyncScript(script, target.resolveUsing(this.browser.element), ...args);
}
constructor(private browser: ProtractorBrowser) {
}
}
@@ -0,0 +1,16 @@
import { Interaction, UsesAbilities } from '../../../serenity/screenplay';
import { BrowseTheWeb } from '../abilities/browse_the_web';
import { Target } from '../ui/target';
export class Evaluate implements Interaction {
static script = (script: string) => ({
on: (target: Target) => new Evaluate(script, target),
})
performAs(actor: UsesAbilities): PromiseLike<any> {
return (BrowseTheWeb.as(actor).locate(this.target).evaluate(this.script) as PromiseLike<any>);
}
constructor(private script: string, private target: Target) {
}
}
@@ -3,16 +3,31 @@ import { BrowseTheWeb } from '../abilities/browse_the_web';
import { Target } from '../ui/target';
export class Execute {
static theScript(script: string) {
return { on: (target: Target): Interaction => new ExecuteScript(script, target) };
}
static script = (...lines: string[]) => ({
on: (target: Target): Interaction => new ExecuteScript(lines, target),
})
static asyncScript = (...lines: string[]) => ({
on: (target: Target): ExecuteAsyncScript => new ExecuteAsyncScript(lines, target),
})
}
class ExecuteScript implements Interaction {
performAs(actor: UsesAbilities): PromiseLike<any> {
return BrowseTheWeb.as(actor).executeScript(this.script, this.target);
return BrowseTheWeb.as(actor).executeScript(this.lines.join('\n'), this.target);
}
constructor(private script: string, private target: Target) {
constructor(private lines: string[], private target: Target) {
}
}
export class ExecuteAsyncScript implements Interaction {
performAs(actor: UsesAbilities): PromiseLike<any> {
return BrowseTheWeb.as(actor).executeAsyncScript(this.lines.join('\n'), this.target, ...this.args);
}
withArguments = (...args: any[]): Interaction => new ExecuteAsyncScript(this.lines, this.target, args);
constructor(private lines: string[], private target: Target, private args: any[] = []) {
}
}
@@ -3,6 +3,7 @@ export * from './click';
export * from './double_click';
export * from './enter';
export * from './execute';
export * from './evaluate';
export * from './hit';
export * from './open';
export * from './resize_browser_window';

0 comments on commit 0a4b08e

Please sign in to comment.