Skip to content
Permalink
Browse files
feat(protractor): Click and DoubleClick interactions
  • Loading branch information
jan-molak committed Feb 6, 2019
1 parent 475a1aa commit 505e25d4cc0d75a00caf0d7f719fef5992576caa
@@ -59,6 +59,6 @@ describe('isClickable', function() {
/** @test {isClickable} */
it(`contributes to a human-readable description of a wait`, () => {
expect(Wait.until(Page.Enabled_Button, isClickable()).toString())
.to.equal(`#actor waits until the enabled button does become clickable`);
.to.equal(`#actor waits up to 5s until the enabled button does become clickable`);
});
});
@@ -50,6 +50,6 @@ describe('isEnabled', function() {
/** @test {isEnabled} */
it(`contributes to a human-readable description of a wait`, () => {
expect(Wait.until(Page.Enabled_Button, isEnabled()).toString())
.to.equal(`#actor waits until the enabled button does become enabled`);
.to.equal(`#actor waits up to 5s until the enabled button does become enabled`);
});
});
@@ -49,6 +49,6 @@ describe('isPresent', function() {
/** @test {isPresent} */
it(`contributes to a human-readable description of a wait`, () => {
expect(Wait.until(Page.Present_Header, isPresent()).toString())
.to.equal(`#actor waits until the header does become present`);
.to.equal(`#actor waits up to 5s until the header does become present`);
});
});
@@ -60,6 +60,6 @@ describe('isSelected', function() {
/** @test {isSelected} */
it(`contributes to a human-readable description of a wait`, () => {
expect(Wait.until(Languages.TypeScript, isSelected()).toString())
.to.equal(`#actor waits until the TypeScript option does become selected`);
.to.equal(`#actor waits up to 5s until the TypeScript option does become selected`);
});
});
@@ -51,6 +51,6 @@ describe('isVisible', function() {
/** @test {isVisible} */
it(`contributes to a human-readable description of a wait`, () => {
expect(Wait.until(Page.Visible_Header, isVisible()).toString())
.to.equal(`#actor waits until the header does become visible`);
.to.equal(`#actor waits up to 5s until the header does become visible`);
});
});
@@ -1,7 +1,8 @@
import { endsWith, Ensure, equals } from '@serenity-js/assertions';
import { expect } from '@integration/testing-tools';
import { Ensure, equals } from '@serenity-js/assertions';
import { Actor } from '@serenity-js/core';
import { by, protractor } from 'protractor';

import { by, protractor } from 'protractor';
import { BrowseTheWeb, Clear, Navigate, Target, Value } from '../../../src';
import { pageFromTemplate } from '../../fixtures';

@@ -32,4 +33,10 @@ describe('Clear', () => {

Ensure.that(Value.of(Form.Field), equals('')),
));

/** @test {Clear#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Clear.theValueOf(Form.Field).toString())
.to.equal('#actor clears the value of the name field');
});
});
@@ -0,0 +1,42 @@
import { expect } from '@integration/testing-tools';
import { Ensure, equals } from '@serenity-js/assertions';
import { Actor } from '@serenity-js/core';

import { by, protractor } from 'protractor';
import { Attribute, BrowseTheWeb, Click, Navigate, Target } from '../../../src';
import { pageFromTemplate } from '../../fixtures';

describe('Click', () => {

const Bernie = Actor.named('Bernie').whoCan(
BrowseTheWeb.using(protractor.browser),
);

const Form = {
Checkbox: Target.the('checkbox').located(by.id('no-spam-please')),
};

/** @test {Click} */
/** @test {Click.on} */
it('allows the actor to click on an element', () => Bernie.attemptsTo(
Navigate.to(pageFromTemplate(`
<html>
<body>
<form>
<input type="checkbox" id="no-spam-please" />
</form>
</body>
</html>
`)),

Click.on(Form.Checkbox),

Ensure.that(Attribute.of(Form.Checkbox).called('checked'), equals('true')),
));

/** @test {Click#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Click.on(Form.Checkbox).toString())
.to.equal('#actor clicks on the checkbox');
});
});
@@ -0,0 +1,45 @@
import { expect } from '@integration/testing-tools';
import { Ensure, equals } from '@serenity-js/assertions';
import { Actor } from '@serenity-js/core';

import { by, protractor } from 'protractor';
import { BrowseTheWeb, DoubleClick, Navigate, Target, Text } from '../../../src';
import { pageFromTemplate } from '../../fixtures';

describe('DoubleClick', () => {

const Interactive_Element = Target.the('interactive element').located(by.id('double-click-me'));

const Bernie = Actor.named('Bernie').whoCan(
BrowseTheWeb.using(protractor.browser),
);

/** @test {DoubleClick} */
/** @test {DoubleClick.on} */
it('allows the actor to clear the value of a field', () => Bernie.attemptsTo(
Navigate.to(pageFromTemplate(`
<html>
<body>
<div id="double-click-me">double-click me!</div>
<script>
const el = document.getElementById('double-click-me');
el.addEventListener('dblclick', function (e) {
el.innerText = 'done!';
});
</script>
</body>
</html>
`)),

DoubleClick.on(Interactive_Element),

Ensure.that(Text.of(Interactive_Element), equals('done!')),
));

/** @test {DoubleClick#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(DoubleClick.on(Interactive_Element).toString())
.to.equal('#actor double-clicks on the interactive element');
});
});
@@ -1,3 +1,4 @@
import { expect } from '@integration/testing-tools';
import { Ensure, equals } from '@serenity-js/assertions';
import { Actor } from '@serenity-js/core';
import { by, protractor } from 'protractor';
@@ -17,6 +18,7 @@ describe('Enter', () => {
};

/** @test {Enter} */
/** @test {Enter.theValue} */
it('allows the actor to enter the value into a field', () => Bernie.attemptsTo(
Navigate.to(pageFromTemplate(`
<html>
@@ -38,4 +40,10 @@ describe('Enter', () => {

Ensure.that(Value.of(Form.Field), equals(Bernie.name)),
));

/** @test {Enter#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Enter.theValue(Bernie.name).into(Form.Field).toString())
.to.equal(`#actor enters 'Bernie' into the name field`);
});
});
@@ -1,3 +1,4 @@
import { expect } from '@integration/testing-tools';
import { endsWith, Ensure, equals } from '@serenity-js/assertions';
import { Actor } from '@serenity-js/core';
import { by, protractor } from 'protractor';
@@ -25,6 +26,12 @@ describe('Navigate', () => {

Ensure.that(Text.of(Target.the('heading').located(by.id('h'))), equals('Hello World')),
));

/** @test {Navigate#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Navigate.to(`https://serenity-js.org`).toString())
.to.equal(`#actor navigates to 'https://serenity-js.org'`);
});
});

describe('back', () => {
@@ -39,6 +46,11 @@ describe('Navigate', () => {
Ensure.that(Website.url(), endsWith('version/')),
));

/** @test {Navigate#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Navigate.back().toString())
.to.equal(`#actor navigates back in the browser history`);
});
});

describe('forward', () => {
@@ -54,5 +66,10 @@ describe('Navigate', () => {
Ensure.that(Website.url(), endsWith('accessibility/')),
));

/** @test {Navigate#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Navigate.forward().toString())
.to.equal(`#actor navigates forward in the browser history`);
});
});
});
@@ -30,34 +30,52 @@ describe('Wait', () => {
</html>
`);

/** @test {Wait} */
/** @test {Wait.for} */
it(`pauses the actor flow for the length of an explicitly-set duration`, () => Bernie.attemptsTo(
Navigate.to(Test_Page),
describe(`for`, () => {

Wait.for(Duration.ofMillis(300)),
/** @test {Wait} */
/** @test {Wait.for} */
it(`pauses the actor flow for the length of an explicitly-set duration`, () => Bernie.attemptsTo(
Navigate.to(Test_Page),

Ensure.that(Text.of(Status), equals('Ready!')),
));
Wait.for(Duration.ofMillis(300)),

/** @test {Wait} */
/** @test {Wait.until} */
it(`pauses the actor flow until the expectation is met`, () => Bernie.attemptsTo(
Navigate.to(Test_Page),
Ensure.that(Text.of(Status), equals('Ready!')),
));

Wait.until(Text.of(Status), equals('Ready!')),
/** @test {Wait#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Wait.for(Duration.ofMillis(300)).toString())
.to.equal(`#actor waits for 300ms`);
});
});

Ensure.that(Text.of(Status), equals('Ready!')),
));
describe(`until`, () => {

/** @test {Wait} */
/** @test {Wait.upTo} */
/** @test {Wait.until} */
it('pauses the actor flow until the timeout expires', () => expect(Bernie.attemptsTo(
Navigate.to(Test_Page),
/** @test {Wait} */
/** @test {Wait.until} */
it(`pauses the actor flow until the expectation is met`, () => Bernie.attemptsTo(
Navigate.to(Test_Page),

Wait.upTo(Duration.ofMillis(10)).until(Text.of(Status), equals('Ready!')),
)).to.be.rejected.then(error => {
expect(error.constructor.name).to.equal('TimeoutError');
}));
Wait.until(Text.of(Status), equals('Ready!')),

Ensure.that(Text.of(Status), equals('Ready!')),
));

/** @test {Wait} */
/** @test {Wait.upTo} */
/** @test {Wait.until} */
it('pauses the actor flow until the timeout expires', () => expect(Bernie.attemptsTo(
Navigate.to(Test_Page),

Wait.upTo(Duration.ofMillis(10)).until(Text.of(Status), equals('Ready!')),
)).to.be.rejected.then(error => {
expect(error.constructor.name).to.equal('TimeoutError');
}));

/** @test {Wait#toString} */
it(`provides a sensible description of the interaction being performed`, () => {
expect(Wait.upTo(Duration.ofMillis(10)).until(Text.of(Status), equals('Ready!')).toString())
.to.equal(`#actor waits up to 10ms until the text of the header does equal 'Ready!'`);
});
});
});
@@ -30,7 +30,7 @@ describe('Text', function() {
Ensure.that(Text.of(Header), equals('Hello World!')),
));

it(`produces sensible description of the question being asked`, () => {
it(`produces a sensible description of the question being asked`, () => {
expect(Text.of(Target.the('header').located(by.tagName('h1'))).toString())
.to.equal('the text of the header');
});
@@ -1,5 +1,5 @@
import { Ability, UsesAbilities } from '@serenity-js/core';
import { ElementArrayFinder, ElementFinder, Locator, protractor, ProtractorBrowser } from 'protractor';
import { ActionSequence, ElementArrayFinder, ElementFinder, Locator, protractor, ProtractorBrowser } from 'protractor';
import { Navigation } from 'selenium-webdriver';
import { promiseOf } from '../promiseOf';

@@ -87,6 +87,17 @@ export class BrowseTheWeb implements Ability {
return this.browser.navigate();
}

/**
* @desc
* Interface for defining sequences of complex user interactions.
* Each sequence will not be executed until `perform` is called.
*
* @returns {ActionSequence}
*/
actions(): ActionSequence {
return this.browser.actions();
}

/**
* @desc
* Locates a single element identified by the locator
@@ -17,6 +17,6 @@ export class Clear implements Interaction {
}

toString(): string {
return formatted `#actor clear the value of ${ this.field }`;
return formatted `#actor clears the value of ${ this.field }`;
}
}
@@ -0,0 +1,22 @@
import { AnswersQuestions, Interaction, UsesAbilities } from '@serenity-js/core';
import { formatted } from '@serenity-js/core/lib/io';
import { ElementFinder } from 'protractor';
import { promiseOf } from '../promiseOf';
import { Target } from '../questions';

export class Click implements Interaction {
static on(target: Target<ElementFinder>) {
return new Click(target);
}

constructor(private readonly target: Target<ElementFinder>) {
}

performAs(actor: UsesAbilities & AnswersQuestions): PromiseLike<void> {
return promiseOf(this.target.answeredBy(actor).click());
}

toString(): string {
return formatted `#actor clicks on ${ this.target }`;
}
}
@@ -0,0 +1,26 @@
import { AnswersQuestions, Interaction, UsesAbilities } from '@serenity-js/core';
import { formatted } from '@serenity-js/core/lib/io';
import { ElementFinder } from 'protractor';
import { BrowseTheWeb } from '../abilities';
import { promiseOf } from '../promiseOf';
import { Target } from '../questions';

export class DoubleClick implements Interaction {
static on(target: Target<ElementFinder>) {
return new DoubleClick(target);
}

constructor(private readonly target: Target<ElementFinder>) {
}

performAs(actor: UsesAbilities & AnswersQuestions): PromiseLike<void> {
return promiseOf(BrowseTheWeb.as(actor).actions()
.doubleClick(this.target.answeredBy(actor))
.perform(),
);
}

toString(): string {
return formatted `#actor double-clicks on ${ this.target }`;
}
}
@@ -61,6 +61,6 @@ class WaitUntil<Actual> implements Interaction {
}

toString(): string {
return formatted`#actor waits until ${ this.actual } does ${ this.expectation }`;
return formatted`#actor waits up to ${ this.timeout } until ${ this.actual } does ${ this.expectation }`;
}
}
@@ -1,4 +1,6 @@
export * from './Clear';
export * from './Click';
export * from './DoubleClick';
export * from './Enter';
export * from './Navigate';
export * from './Wait';

0 comments on commit 505e25d

Please sign in to comment.