|
What this is
Other links
The source code
/*
* Copyright 2004 ThoughtWorks, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* This script provides the Javascript API to drive the test application contained within
* a Browser Window.
* TODO:
* Add support for more events (keyboard and mouse)
* Allow to switch "user-entry" mode from mouse-based to keyboard-based, firing different
* events in different modes.
*/
// The window to which the commands will be sent. For example, to click on a
// popup window, first select that window, and then do a normal click command.
var browserName=navigator.appName;
var isIE = (browserName =="Microsoft Internet Explorer");
// Get the Gecko version as an 8 digit date.
var geckoResult = /^Mozilla\/5\.0 .*Gecko\/(\d{8}).*$/.exec(navigator.userAgent);
var geckoVersion = geckoResult == null ? null : geckoResult[1];
/*
* The 'invoke' method will call the required function, and then
* remove itself from the window object. This allows a calling app
* to provide a callback listener for the window load event, without the
* calling app needing to worry about cleaning up afterward.
* TODO: This could be more generic, but suffices for now.
*/
function SelfRemovingLoadListener(fn, frame) {
var self = this;
this.invoke=function () {
try {
// we've moved to a new page - clear the current one
browserbot.currentPage = null;
fn();
} finally {
removeLoadListener(frame, self.invoke);
}
}
}
BrowserBot = function(frame) {
this.frame = frame;
this.currentPage = null;
this.currentWindowName = null;
this.recordedAlerts = new Array();
}
BrowserBot.prototype.recordAlert = function(alert) {
this.recordedAlerts.push(alert);
}
/*
* Indicates any alerts have been generated
*/
BrowserBot.prototype.hasAlerts = function() {
return (this.recordedAlerts.length > 0) ;
}
/*
* Retreives the next alert
*/
BrowserBot.prototype.getNextAlert = function() {
return this.recordedAlerts.shift();
}
BrowserBot.prototype.getFrame = function() {
return this.frame;
}
BrowserBot.prototype.getContentWindow = function() {
return this.getFrame().contentWindow
}
BrowserBot.prototype.selectWindow = function(target) {
// we've moved to a new page - clear the current one
this.currentPage = null;
this.currentWindowName = null;
if (target != "null") {
// If window exists
if (this.getTargetWindow(target)) {
this.currentWindowName = target;
}
}
}
BrowserBot.prototype.openLocation = function(target, onloadCallback) {
// We're moving to a new page - clear the current one
this.currentPage = null;
this.callOnNextPageLoad(onloadCallback);
this.getFrame().src = target;
}
BrowserBot.prototype.getCurrentPage = function() {
if (this.currentPage == null) {
var testWindow = this.getContentWindow().window;
if (this.currentWindowName != null) {
testWindow = this.getTargetWindow(this.currentWindowName);
}
this.currentPage = new PageBot(testWindow, this)
}
return this.currentPage;
}
BrowserBot.prototype.getTargetWindow = function(windowName) {
var evalString = "this.getContentWindow().window." + windowName;
var targetWindow = eval(evalString);
if (!targetWindow) {
throw new Error("Window does not exist");
}
return targetWindow;
}
BrowserBot.prototype.callOnNextPageLoad = function(onloadCallback) {
if (onloadCallback) {
var el = new SelfRemovingLoadListener(onloadCallback, this.frame);
addLoadListener(this.getFrame(), el.invoke);
}
}
PageBot = function(pageWindow, browserBot) {
this.currentWindow = pageWindow;
this.browserBot = browserBot;
this.currentDocument = pageWindow.document;
this.location = pageWindow.location.pathname;
this.title = function() {return this.currentDocument.title};
modifyWindowToRecordAlerts(pageWindow, browserBot);
//SPIKE factor this better via TDD
function clearPageCache() {
browserbot.currentPage = null;
}
if (window.addEventListener) {
this.currentWindow.addEventListener("unload",clearPageCache, true);
}
else if (window.attachEvent) {
this.currentWindow.attachEvent("onunload",clearPageCache);
}
// End SPIKE
function modifyWindowToRecordAlerts(window, browserBot) {
window.alert = function(alert){browserBot.recordAlert(alert);};
}
this.locators = new Array();
this.locators.push(this.findIdentifiedElement);
this.locators.push(this.findElementByDomTraversal);
this.locators.push(this.findElementByXPath);
}
/*
* Finds an element on the current page, using various lookup protocols
*/
PageBot.prototype.findElement = function(locator) {
// Try the locators one at a time.
for (var i = 0; i < this.locators.length; i++) {
var locatorFunction = this.locators[i];
var element = locatorFunction.call(this, locator);
if (element != null) {
return element;
}
}
// Element was not found by any locator function.
throw new Error("Element " + locator + " not found");
}
/*
* In IE, getElementById() also searches by name.
* To provied consistent functionality with Firefox, we
* search by name attribute if an element with the id isn't found.
*/
PageBot.prototype.findIdentifiedElement = function(identifier) {
// Since we try to get an id with _any_ string, we need to handle
// cases where this causes an exception.
try {
var element = this.currentDocument.getElementById(identifier);
if (element == null
&& !isIE // IE checks this without asking
&& document.evaluate )// DOM3 XPath
{
var xpath = "//*[@name='" + identifier + "']";
element = document.evaluate(xpath, this.currentDocument, null, 0, null).iterateNext();
}
} catch (e) {
return null;
}
return element;
}
/**
* Finds an element using by evaluating the "document.*" string against the
* current document object. Dom expressions must begin with "document."
*/
PageBot.prototype.findElementByDomTraversal = function(domTraversal) {
if (domTraversal.indexOf("document.") != 0) {
return null;
}
// Trim the leading 'document'
domTraversal = domTraversal.substr(9);
var locatorScript = "this.currentDocument." + domTraversal;
var element = eval(locatorScript);
if (!element) {
return null;
}
return element;
}
/**
* Finds an element identified by the xpath expression. Expressions _must_
* begin with "//".
*/
PageBot.prototype.findElementByXPath = function(xpath) {
if (xpath.indexOf("//") != 0) {
return null;
}
// If the document doesn't support XPath, mod it with html-xpath.
// This only works for IE.
if (!this.currentDocument.evaluate) {
addXPathSupport(this.currentDocument);
}
// If we still don't have XPath bail.
// TODO implement subset of XPath for browsers without native support.
if (!this.currentDocument.evaluate) {
throw new Error("XPath not supported");
}
// Trim any trailing "/": not valid xpath, and remains from attribute locator.
if (xpath.charAt(xpath.length - 1) == '/') {
xpath = xpath.slice(0, xpath.length - 1);
}
return this.currentDocument.evaluate(xpath, this.currentDocument, null, 0, null).iterateNext();
}
/**
* Returns an attribute based on an attribute locator. This is made up of an element locator
* suffixed with @attribute-name.
*/
PageBot.prototype.findAttribute = function(locator) {
// Split into locator + attributeName
var attributePos = locator.lastIndexOf("@");
var elementLocator = locator.slice(0, attributePos);
var attributeName = locator.slice(attributePos + 1);
// Find the element.
var element = this.findElement(elementLocator);
// Handle missing "class" attribute in IE.
if (isIE && attributeName == "class") {
attributeName = "className";
}
// Get the attribute value.
var attributeValue = element.getAttribute(attributeName);
return attributeValue ? attributeValue.toString() : null;
}
/*
* Selects the first option with a matching label from the select box element
* provided. If no matching element is found, nothing happens.
*/
PageBot.prototype.selectOptionWithLabel = function(element, stringValue) {
triggerEvent(element, 'focus', false);
for (var i = 0; i < element.options.length; i++) {
var option = element.options[i];
if (option.text == stringValue) {
if (!option.selected) {
option.selected = true;
triggerEvent(element, 'change', true);
}
}
}
triggerEvent(element, 'blur', false);
}
PageBot.prototype.replaceText = function(element, stringValue) {
triggerEvent(element, 'focus', false);
triggerEvent(element, 'select', true);
element.value=stringValue;
if (isIE) {
triggerEvent(element, 'change', true);
}
triggerEvent(element, 'blur', false);
}
PageBot.prototype.clickElement = function(element) {
triggerEvent(element, 'focus', false);
var wasChecked = element.checked;
if (isIE) {
element.click();
}
else {
// Add an event listener that detects if the default action has been prevented.
var preventDefault = false;
element.addEventListener("click", function(evt) {preventDefault = evt.getPreventDefault()}, false);
// Trigger the click event.
triggerMouseEvent(element, 'click', true);
// In FireFox < 1.0 Final, and Mozilla <= 1.7.3, just sending the click event is enough.
// But in newer versions, we need to do it ourselves.
var needsProgrammaticClick = (geckoVersion > '20041025');
// Perform the link action if preventDefault was set.
if (needsProgrammaticClick && element.href && !preventDefault) {
this.currentWindow.location.href = element.href;
}
}
if (this.windowClosed()) {
return;
}
// Onchange event is not triggered automatically in IE.
if (isIE && isDefined(element.checked) && wasChecked != element.checked) {
triggerEvent(element, 'change', true);
}
triggerEvent(element, 'blur', false);
}
PageBot.prototype.windowClosed = function(element) {
return this.currentWindow.closed;
}
PageBot.prototype.bodyText = function() {
return getText(this.currentDocument.body)
}
function isDefined(value) {
return typeof(value) != undefined;
}
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.