Skip to content
Permalink
Browse files
feat(protractor): switch task lets you switch between popup windows
affects: serenity-js
  • Loading branch information
jan-molak committed Jun 13, 2017
1 parent ead3374 commit fdedf8a85e597590ae4c46374a37cef0c72f0a48
Showing 8 changed files with 162 additions and 2 deletions.
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Non-Angular Popup</title>
</head>
<body>
<p>Yup, it's a pop-up!</p>
<input type="button" onclick="window.close()" value="Cool!" />
</body>
</html>
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<html>
<html ng-app="demo">
<head>
<script src="/js/angular.min.js"></script>
<title>demo app</title>
</head>
<body ng-app="demo">
<body>


<section ng-controller="timeouts" id="timeouts" ng-cloak>
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html ng-app="windows">
<head>
<script type="text/javascript" src="/js/angular.js"></script>
<title>Popups</title>
</head>
<body>

<section id="windows" ng-controller="popup-windows">
<a ng-click="openPopup()">open a popup</a>
</section>

<script type="text/javascript">
angular.module('windows', [])
.controller('popup-windows', ['$scope', '$window', function ($scope, $window) {
var index = 1;
$scope.openPopup = function() {
$window.open("/popup.html", "popup-" + index++, "width=300,height=200");
}
}]);
</script>

</body>
</html>
@@ -0,0 +1,68 @@
import expect = require('../expect');

import { by, protractor } from 'protractor';

import { Actor, BrowseTheWeb, See, Switch, Target, UseAngular } from '../../src/screenplay-protractor';
import { Click, Open, Text } from '../../src/serenity-protractor';

import { AppServer } from '../support/server';

class Popup {
static Trigger = Target.the('pop up trigger').located(by.linkText('open a popup'));
static Text = Target.the('text of the pop-up').located(by.tagName('body'));
static Dismiss = Target.the('dismiss popup button').located(by.tagName('input'));
}

describe ('When demonstrating how to work with popup windows, 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('popups'))));
after(app.stop());

it ('can switch to a newly opened popup window and back', () =>
james.attemptsTo(
Click.on(Popup.Trigger),

UseAngular.disableSynchronisation(),
Switch.toPopupWindow(),

See.if(Text.of(Popup.Text), text => expect(text).to.eventually.equal('Yup, it\'s a pop-up!')),
Click.on(Popup.Dismiss),

UseAngular.enableSynchronisation(),
Switch.toParentWindow(),

See.if(Text.of(Popup.Trigger), text => expect(text).to.eventually.equal('open a popup')),
));

it ('can switch to a specific window identified by its index', () =>
james.attemptsTo(
Click.on(Popup.Trigger),
Click.on(Popup.Trigger),

UseAngular.disableSynchronisation(),

Switch.toWindowNumber(2),
See.if(Text.of(Popup.Text), text => expect(text).to.eventually.equal('Yup, it\'s a pop-up!')),
Click.on(Popup.Dismiss),

Switch.toWindowNumber(1),
See.if(Text.of(Popup.Text), text => expect(text).to.eventually.equal('Yup, it\'s a pop-up!')),
Click.on(Popup.Dismiss),

UseAngular.enableSynchronisation(),
Switch.toParentWindow(),

See.if(Text.of(Popup.Trigger), text => expect(text).to.eventually.equal('open a popup')),
));

it ('complains when you try to switch to a popup that does not exist', () =>
expect(james.attemptsTo(
Switch.toPopupWindow(),
)).to.eventually.be.rejectedWith('No popup window was opened'));
});
@@ -12,6 +12,10 @@ var capabilities = {
build: process.env.BROWSERSTACK_AUTOMATE_BUILD,
project: process.env.BROWSERSTACK_AUTOMATE_PROJECT,

chromeOptions: {
args: ['--disable-infobars']
},

'browserstack.localIdentifier': process.env.BROWSERSTACK_LOCAL_IDENTIFIER,
'browserstack.local': true,
},
@@ -7,6 +7,8 @@ import { Target } from '../ui/target';

export class BrowseTheWeb implements Ability {

private parentWindow?: string;

/**
*
* Instantiates the Ability to BrowseTheWeb, allowing the Actor to interact with a Web UI
@@ -56,6 +58,23 @@ export class BrowseTheWeb implements Ability {
return this.browser.driver.manage();
}

switchToParentWindow(): PromiseLike<void> {
if (! this.parentWindow) {
throw new Error('This window does not have a parent');
}
return this.browser.switchTo().window(this.parentWindow);
}

switchToWindow(handle: (handles: string[]) => string): PromiseLike<void> {
return this.browser.getWindowHandle().then(currentWindow => {
this.parentWindow = currentWindow;

return this.browser.getAllWindowHandles().then(handles => {
return this.browser.switchTo().window(handle(handles));
});
});
}

sleep(millis: number): PromiseLike<void> {
return defer(() => this.browser.sleep(millis));
}
@@ -9,5 +9,6 @@ export * from './open';
export * from './resize_browser_window';
export * from './scroll';
export * from './select';
export * from './switch';
export * from './use_angular';
export * from './wait';
@@ -0,0 +1,34 @@
import { Interaction, UsesAbilities } from '@serenity-js/core/lib/screenplay';

import { BrowseTheWeb } from '../abilities/browse_the_web';

export class Switch {
static toParentWindow = (): Interaction => new SwitchToParentWindow();

static toWindowNumber = (index: number): Interaction => new SwitchToWindow((handles: string[]) => {
if (! handles[index]) {
throw new Error(`Can't switch to window ${index} as there are only ${handles.length}`);
}

return handles[index];
})

static toPopupWindow = (): Interaction => new SwitchToWindow((handles: string[]) => {
if (handles.length === 1) {
throw new Error('No popup window was opened');
}

return handles[handles.length - 1];
})
}

class SwitchToParentWindow implements Interaction {
performAs = (actor: UsesAbilities) => BrowseTheWeb.as(actor).switchToParentWindow();
}

class SwitchToWindow implements Interaction {
constructor(private handle: (handles: string[]) => string) {
}

performAs = (actor: UsesAbilities) => BrowseTheWeb.as(actor).switchToWindow(this.handle);
}

2 comments on commit fdedf8a

@marktigno
Copy link

@marktigno marktigno commented on fdedf8a Jun 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @jan-molak, this works well when I used this on testing pop-up windows on my end but I have a simple question here. Is there anyway to close the pop-up window before switching back to the parent? My implementation before this update came is using this code:

{
    protractor.browser.getAllWindowHandles().then(function(handles) {
    protractor.browser.switchTo().window(handles[handles.length - 1]).then(function() {
        //some implementing codes
    }),
    protractor.browser.close(), // <--this is what I mean
    protractor.browser.switchTo().window(handles[0])
}

@jan-molak
Copy link
Member Author

@jan-molak jan-molak commented on fdedf8a Jun 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @marktigno - we'll need to add a task for that. Would you mind raising a ticket (or a PR ;-) )

Please sign in to comment.