Blame | Letzte Änderung | Log anzeigen | RSS feed
/** 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.*/// A naming convention used in this file:////// - a "seleniumApi" is an instance of the Selenium object, defined in selenium-api.js.//// - a "Method" is an unbound function whose target must be supplied when it's called, ie.// it should be invoked using Function.call() or Function.apply()//// - a "Block" is a function that has been bound to a target object, so can be called invoked directly// (or with a null target)//// - "CommandHandler" is effectively an abstract base for// various handlers including ActionHandler, AccessorHandler and AssertHandler.// Subclasses need to implement an execute(seleniumApi, command) function,// where seleniumApi is the Selenium object, and command a SeleniumCommand object.//// - Handlers will return a "result" object (ActionResult, AccessorResult, AssertResult).// ActionResults may contain a .terminationCondition function which is run by// -executionloop.js after the command is run; we'll run it over and over again// until it returns true or the .terminationCondition throws an exception.// AccessorResults will contain the results of running getter (e.g. getTitle returns// the title as a string).var CommandHandlerFactory = classCreate();objectExtend(CommandHandlerFactory.prototype, {initialize: function() {this.handlers = {};},registerAction: function(name, actionBlock, wait, dontCheckAlertsAndConfirms) {this.handlers[name] = new ActionHandler(actionBlock, wait, dontCheckAlertsAndConfirms);},registerAccessor: function(name, accessBlock) {this.handlers[name] = new AccessorHandler(accessBlock);},registerAssert: function(name, assertBlock, haltOnFailure) {this.handlers[name] = new AssertHandler(assertBlock, haltOnFailure);},getCommandHandler: function(name) {return this.handlers[name];},_registerAllAccessors: function(seleniumApi) {// Methods of the form getFoo(target) result in commands:// getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo// storeFoo, waitForFoo, and waitForNotFoo.for (var functionName in seleniumApi) {var match = /^(get|is)([A-Z].+)$/.exec(functionName);if (match) {var accessMethod = seleniumApi[functionName];var accessBlock = fnBind(accessMethod, seleniumApi);var baseName = match[2];var isBoolean = (match[1] == "is");var requiresTarget = (accessMethod.length == 1);this.registerAccessor(functionName, accessBlock);this._registerStoreCommandForAccessor(baseName, accessBlock, requiresTarget);var predicateBlock = this._predicateForAccessor(accessBlock, requiresTarget, isBoolean);this._registerAssertionsForPredicate(baseName, predicateBlock);this._registerWaitForCommandsForPredicate(seleniumApi, baseName, predicateBlock);}}},_registerAllActions: function(seleniumApi) {for (var functionName in seleniumApi) {var match = /^do([A-Z].+)$/.exec(functionName);if (match) {var actionName = match[1].lcfirst();var actionMethod = seleniumApi[functionName];var dontCheckPopups = actionMethod.dontCheckAlertsAndConfirms;var actionBlock = fnBind(actionMethod, seleniumApi);this.registerAction(actionName, actionBlock, false, dontCheckPopups);this.registerAction(actionName + "AndWait", actionBlock, true, dontCheckPopups);}}},_registerAllAsserts: function(seleniumApi) {for (var functionName in seleniumApi) {var match = /^assert([A-Z].+)$/.exec(functionName);if (match) {var assertBlock = fnBind(seleniumApi[functionName], seleniumApi);// Register the assert with the "assert" prefix, and halt on failure.var assertName = functionName;this.registerAssert(assertName, assertBlock, true);// Register the assert with the "verify" prefix, and do not halt on failure.var verifyName = "verify" + match[1];this.registerAssert(verifyName, assertBlock, false);}}},registerAll: function(seleniumApi) {this._registerAllAccessors(seleniumApi);this._registerAllActions(seleniumApi);this._registerAllAsserts(seleniumApi);},_predicateForAccessor: function(accessBlock, requiresTarget, isBoolean) {if (isBoolean) {return this._predicateForBooleanAccessor(accessBlock);}if (requiresTarget) {return this._predicateForSingleArgAccessor(accessBlock);}return this._predicateForNoArgAccessor(accessBlock);},_predicateForSingleArgAccessor: function(accessBlock) {// Given an accessor function getBlah(target),// return a "predicate" equivalient to isBlah(target, value) that// is true when the value returned by the accessor matches the specified value.return function(target, value) {var accessorResult = accessBlock(target);if (PatternMatcher.matches(value, accessorResult)) {return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");} else {return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");}};},_predicateForNoArgAccessor: function(accessBlock) {// Given a (no-arg) accessor function getBlah(),// return a "predicate" equivalient to isBlah(value) that// is true when the value returned by the accessor matches the specified value.return function(value) {var accessorResult = accessBlock();if (PatternMatcher.matches(value, accessorResult)) {return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");} else {return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");}};},_predicateForBooleanAccessor: function(accessBlock) {// Given a boolean accessor function isBlah(),// return a "predicate" equivalient to isBlah() that// returns an appropriate PredicateResult value.return function() {var accessorResult;if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length);if (arguments.length == 2) {accessorResult = accessBlock(arguments[0], arguments[1]);} else if (arguments.length == 1) {accessorResult = accessBlock(arguments[0]);} else {accessorResult = accessBlock();}if (accessorResult) {return new PredicateResult(true, "true");} else {return new PredicateResult(false, "false");}};},_invertPredicate: function(predicateBlock) {// Given a predicate, return the negation of that predicate.// Leaves the message unchanged.// Used to create assertNot, verifyNot, and waitForNot commands.return function(target, value) {var result = predicateBlock(target, value);result.isTrue = !result.isTrue;return result;};},createAssertionFromPredicate: function(predicateBlock) {// Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.return function(target, value) {var result = predicateBlock(target, value);if (!result.isTrue) {Assert.fail(result.message);}};},_invertPredicateName: function(baseName) {var matchResult = /^(.*)Present$/.exec(baseName);if (matchResult != null) {return matchResult[1] + "NotPresent";}return "Not" + baseName;},_registerAssertionsForPredicate: function(baseName, predicateBlock) {// Register an assertion, a verification, a negative assertion,// and a negative verification based on the specified accessor.var assertBlock = this.createAssertionFromPredicate(predicateBlock);this.registerAssert("assert" + baseName, assertBlock, true);this.registerAssert("verify" + baseName, assertBlock, false);var invertedPredicateBlock = this._invertPredicate(predicateBlock);var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);this.registerAssert("assert" + this._invertPredicateName(baseName), negativeassertBlock, true);this.registerAssert("verify" + this._invertPredicateName(baseName), negativeassertBlock, false);},_waitForActionForPredicate: function(predicateBlock) {// Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.return function(target, value) {var terminationCondition = function () {try {return predicateBlock(target, value).isTrue;} catch (e) {// Treat exceptions as meaning the condition is not yet met.// Useful, for example, for waitForValue when the element has// not even been created yet.// TODO: possibly should rethrow some types of exception.return false;}};return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);};},_registerWaitForCommandsForPredicate: function(seleniumApi, baseName, predicateBlock) {// Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.var waitForActionMethod = this._waitForActionForPredicate(predicateBlock);var waitForActionBlock = fnBind(waitForActionMethod, seleniumApi);var invertedPredicateBlock = this._invertPredicate(predicateBlock);var waitForNotActionMethod = this._waitForActionForPredicate(invertedPredicateBlock);var waitForNotActionBlock = fnBind(waitForNotActionMethod, seleniumApi);this.registerAction("waitFor" + baseName, waitForActionBlock, false, true);this.registerAction("waitFor" + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);//TODO decide remove "waitForNot.*Present" action name or not//for the back compatiblity issues we still make waitForNot.*Present availblethis.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);},_registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {var action;if (requiresTarget) {action = function(target, varName) {storedVars[varName] = accessBlock(target);};} else {action = function(varName) {storedVars[varName] = accessBlock();};}this.registerAction("store" + baseName, action, false, true);}});function PredicateResult(isTrue, message) {this.isTrue = isTrue;this.message = message;}// NOTE: The CommandHandler is effectively an abstract base for// various handlers including ActionHandler, AccessorHandler and AssertHandler.// Subclasses need to implement an execute(seleniumApi, command) function,// where seleniumApi is the Selenium object, and command a SeleniumCommand object.function CommandHandler(type, haltOnFailure) {this.type = type;this.haltOnFailure = haltOnFailure;}// An ActionHandler is a command handler that executes the sepcified action,// possibly checking for alerts and confirmations (if checkAlerts is set), and// possibly waiting for a page load if wait is set.function ActionHandler(actionBlock, wait, dontCheckAlerts) {this.actionBlock = actionBlock;CommandHandler.call(this, "action", true);if (wait) {this.wait = true;}// note that dontCheckAlerts could be undefined!!!this.checkAlerts = (dontCheckAlerts) ? false : true;}ActionHandler.prototype = new CommandHandler;ActionHandler.prototype.execute = function(seleniumApi, command) {if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {// todo: this conditional logic is uglyseleniumApi.ensureNoUnhandledPopups();}var terminationCondition = this.actionBlock(command.target, command.value);// If the handler didn't return a wait flag, check to see if the// handler was registered with the wait flag.if (terminationCondition == undefined && this.wait) {terminationCondition = seleniumApi.makePageLoadCondition();}return new ActionResult(terminationCondition);};function ActionResult(terminationCondition) {this.terminationCondition = terminationCondition;}function AccessorHandler(accessBlock) {this.accessBlock = accessBlock;CommandHandler.call(this, "accessor", true);}AccessorHandler.prototype = new CommandHandler;AccessorHandler.prototype.execute = function(seleniumApi, command) {var returnValue = this.accessBlock(command.target, command.value);return new AccessorResult(returnValue);};function AccessorResult(result) {this.result = result;}/*** Handler for assertions and verifications.*/function AssertHandler(assertBlock, haltOnFailure) {this.assertBlock = assertBlock;CommandHandler.call(this, "assert", haltOnFailure || false);}AssertHandler.prototype = new CommandHandler;AssertHandler.prototype.execute = function(seleniumApi, command) {var result = new AssertResult();try {this.assertBlock(command.target, command.value);} catch (e) {// If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.if (!e.isAssertionFailedError) {throw e;}if (this.haltOnFailure) {var error = new SeleniumError(e.failureMessage);throw error;}result.setFailed(e.failureMessage);}return result;};function AssertResult() {this.passed = true;}AssertResult.prototype.setFailed = function(message) {this.passed = null;this.failed = true;this.failureMessage = message;}function SeleniumCommand(command, target, value, isBreakpoint) {this.command = command;this.target = target;this.value = value;this.isBreakpoint = isBreakpoint;}