Skip to content

Commit

Permalink
Merge pull request #25 from dennisverspuij/windowing
Browse files Browse the repository at this point in the history
Correctly track and expose windows opened from secondary windows, and so on
  • Loading branch information
jcalderonzumba authored Mar 31, 2017
2 parents 5197032 + 5208a99 commit 575a9c1
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 14 deletions.
46 changes: 35 additions & 11 deletions src/Client/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Poltergeist.Browser = (function () {

/**
* Resets the browser to a clean slate
* @return {Function}
*/
Browser.prototype.resetPage = function () {
var _ref;
Expand All @@ -56,20 +55,44 @@ Poltergeist.Browser = (function () {
phantom.clearCookies();
}

this.page = this.currentPage = new Poltergeist.WebPage;
this.page = this.currentPage = new Poltergeist.WebPage(null, this);
this.page.setViewportSize({
width: this.width,
height: this.height
});
this.page.handle = "" + (this._counter++);
this.pages.push(this.page);

return this.page.onPageCreated = function (newPage) {
var page;
page = new Poltergeist.WebPage(newPage);
page.handle = "" + (self._counter++);
return self.pages.push(page);
};
};

/**
* Adds given newly opened Poltergeist.WebPage to the list of available windows/frames
* consulted by methods getPageByHandle, window_handle, window_handles, switch_to_window and close_window.
*
* @param {WebPage} page
*/
Browser.prototype.registerPage = function (page) {
if (!('handle' in page))
{
page.handle = "" + (this._counter++);
this.pages.push(page);
}
};

/**
* Removes given closed Poltergeist.WebPage from the list of available windows/frames
* consulted by methods getPageByHandle, window_handle, window_handles, switch_to_window and close_window.
*
* @param {Poltergeist.WebPage} page
*/
Browser.prototype.unregisterPage = function (page) {
if (('handle' in page) && (page.handle !== null))
{
for (var i = this.pages.length; i--;) {
if (page === this.pages[i]) {
this.pages.splice(i,1);
break;
}
}
page.handle = null;
}
};

/**
Expand Down Expand Up @@ -696,6 +719,7 @@ Poltergeist.Browser = (function () {

/**
* Closes the window given by handle name if possible
* NOTE: Closing a page in PhantomJS also closes new windows/frames opened from that page (QWebPage behaviour)
* @param serverResponse
* @param handle
* @return {*}
Expand Down
23 changes: 20 additions & 3 deletions src/Client/web_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Poltergeist.WebPage = (function () {

WebPage.EXTENSIONS = [];

function WebPage(nativeWebPage) {
function WebPage(nativeWebPage, browser) {
var callback, i, callBacksLength, callBacksRef;

//Lets create the native phantomjs webpage
Expand All @@ -32,6 +32,11 @@ Poltergeist.WebPage = (function () {
this._native = nativeWebPage;
}

this.browser = browser;
if (this.browser) {
this.browser.registerPage(this); // Make the browser aware of opened windows/frames
}

this.id = 0;
this.source = null;
this.closed = false;
Expand Down Expand Up @@ -98,8 +103,11 @@ Poltergeist.WebPage = (function () {
* @return {boolean}
*/
WebPage.prototype.onClosingNative = function () {
this.handle = null;
return this.closed = true;
this.closed = true;
if (this.browser) {
this.browser.unregisterPage(this); // Make the browser aware of closed windows/frames
}
return true;
};

/**
Expand Down Expand Up @@ -147,6 +155,15 @@ Poltergeist.WebPage = (function () {
return this.source;
};

/**
* Make the browser aware of new windows/frames
*
* @param {webPage} newPage
*/
WebPage.prototype.onPageCreatedNative = function (newPage) {
new WebPage(newPage, this.browser);
};

/**
* This callback is invoked when there is a JavaScript execution error.
* It is a good way to catch problems when evaluating a script in the web page context.
Expand Down
71 changes: 71 additions & 0 deletions tests/unit/BrowserWindowTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,75 @@ public function testSwitchToWindow() {
$this->assertTrue($this->browser->switchToWindow("1"));
$this->assertEquals(1, $this->browser->windowHandle());
}

public function testWindowTree() {
// Open a stack of windows like following array of window names by handles:
$expectedStack = array(
0 => array('', 'windowing.html'),
1 => array('', 'windowing.html'),
2 => array('second', 'windowing.html'),
3 => array('', 'windowing.html'),
4 => array('third', 'windowing.html'),
6 => array('', 'windowing.html'),
5 => array('BASIC_WINDOW', 'basic.html'),
);
$this->visitUrl($this->getTestPageBaseUrl() . "/static/windowing.html"); // Open 0
$element = $this->browser->find('xpath', '//*[@id="to_new"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 1 from 0
$element = $this->browser->find('xpath', '//*[@id="to_second"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 2 from 0
$this->browser->switchToWindow('2');
$element = $this->browser->find('xpath', '//*[@id="to_new"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 3 from 2
$element = $this->browser->find('xpath', '//*[@id="to_third"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 4 from 2
$this->browser->switchToWindow('0');
$element = $this->browser->find('xpath', '//*[@id="to_new_basic"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 5 from 0
$this->browser->switchToWindow('4');
$element = $this->browser->find('xpath', '//*[@id="to_new"]');
$this->browser->click($element['page_id'], $element['ids'][0]); // Open 6 from 4

// Validate stack
$validateStack = function() use (&$expectedStack) {
$currentHandle = $this->browser->windowHandle();
$actual = array();
foreach($this->browser->windowHandles() as $handle) {
$this->browser->switchToWindow($handle);
$actual[$handle] = array($this->browser->windowName(), basename($this->browser->currentUrl()));
}
$this->assertEquals($expectedStack, $actual); // assertEquals ignores key order
$this->browser->switchToWindow($currentHandle);
};
$validateStack();

// Open auth_ok.html from 1 into window 'third' (4), then revalidate stack and url of 'third'
$this->browser->switchToWindow('1');
$element = $this->browser->find('xpath', '//*[@id="to_third_auth_ok"]');
$this->browser->click($element['page_id'], $element['ids'][0]);
$expectedStack[$this->browser->windowHandle('third')][1] = 'auth_ok.html';
$validateStack();

// Close 3 and revalidate stack
$this->browser->closeWindow('3');
unset($expectedStack[3]);
$validateStack();

// Close 2, and thereby also 4 and 6, and revalidate stack
$this->browser->closeWindow('2');
unset($expectedStack[2], $expectedStack[4], $expectedStack[6]);
$validateStack();

// Close all others
$this->browser->closeWindow('0');
$this->assertEquals(null, $this->browser->windowHandles());
$this->assertEquals(null, $this->browser->windowHandle());
try {
$this->fail($this->browser->windowName());
} catch(\Exception $e) {
$this->assertInstanceOf('Zumba\GastonJS\Exception\NoSuchWindowError', $e);
}
$this->browser->reset();
$this->assertEquals(0, $this->browser->windowHandle());
}
}
14 changes: 14 additions & 0 deletions tests/unit/Server/www/web/static/windowing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Windowing</title>
</head>
<body>
<a id="to_new" href="windowing.html" target="_blank">clone into new window</a>
<input id="to_new_basic" type="button" onclick="window.open('basic.html', '_blank');" value="open basic.html into new window">

<a id="to_second" href="windowing.html" target="second">clone into 'second'</a><br>
<button id="to_third" type="button" onclick="window.open('windowing.html', 'third');">clone into 'third'</button>
<a id="to_third_auth_ok" href="auth_ok.html" target="third">open auh_ok.html into 'third'</a><br>
</body>
</html>

0 comments on commit 575a9c1

Please sign in to comment.