PageRenderTime 36ms CodeModel.GetById 18ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/testing/selenium-core/scripts/selenium-commandhandlers.js

http://datanucleus-appengine.googlecode.com/
JavaScript | 377 lines | 257 code | 40 blank | 80 comment | 38 complexity | 08074b10965a1ee2b2513041d7558e8d MD5 | raw file
  1/*
  2* Copyright 2004 ThoughtWorks, Inc
  3*
  4*  Licensed under the Apache License, Version 2.0 (the "License");
  5*  you may not use this file except in compliance with the License.
  6*  You may obtain a copy of the License at
  7*
  8*      http://www.apache.org/licenses/LICENSE-2.0
  9*
 10*  Unless required by applicable law or agreed to in writing, software
 11*  distributed under the License is distributed on an "AS IS" BASIS,
 12*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13*  See the License for the specific language governing permissions and
 14*  limitations under the License.
 15*/
 16
 17// A naming convention used in this file:
 18//
 19//
 20//   - a "seleniumApi" is an instance of the Selenium object, defined in selenium-api.js.
 21//
 22//   - a "Method" is an unbound function whose target must be supplied when it's called, ie.
 23//     it should be invoked using Function.call() or Function.apply()
 24//
 25//   - a "Block" is a function that has been bound to a target object, so can be called invoked directly
 26//     (or with a null target)
 27//
 28//   - "CommandHandler" is effectively an abstract base for
 29//     various handlers including ActionHandler, AccessorHandler and AssertHandler.
 30//     Subclasses need to implement an execute(seleniumApi, command) function,
 31//     where seleniumApi is the Selenium object, and command a SeleniumCommand object.
 32//
 33//   - Handlers will return a "result" object (ActionResult, AccessorResult, AssertResult).
 34//     ActionResults may contain a .terminationCondition function which is run by 
 35//     -executionloop.js after the command is run; we'll run it over and over again
 36//     until it returns true or the .terminationCondition throws an exception.
 37//     AccessorResults will contain the results of running getter (e.g. getTitle returns
 38//     the title as a string).
 39
 40var CommandHandlerFactory = classCreate();
 41objectExtend(CommandHandlerFactory.prototype, {
 42
 43    initialize: function() {
 44        this.handlers = {};
 45    },
 46
 47    registerAction: function(name, actionBlock, wait, dontCheckAlertsAndConfirms) {
 48        this.handlers[name] = new ActionHandler(actionBlock, wait, dontCheckAlertsAndConfirms);
 49    },
 50
 51    registerAccessor: function(name, accessBlock) {
 52        this.handlers[name] = new AccessorHandler(accessBlock);
 53    },
 54
 55    registerAssert: function(name, assertBlock, haltOnFailure) {
 56        this.handlers[name] = new AssertHandler(assertBlock, haltOnFailure);
 57    },
 58
 59    getCommandHandler: function(name) {
 60        return this.handlers[name];
 61    },
 62
 63    _registerAllAccessors: function(seleniumApi) {
 64        // Methods of the form getFoo(target) result in commands:
 65        // getFoo, assertFoo, verifyFoo, assertNotFoo, verifyNotFoo
 66        // storeFoo, waitForFoo, and waitForNotFoo.
 67        for (var functionName in seleniumApi) {
 68            var match = /^(get|is)([A-Z].+)$/.exec(functionName);
 69            if (match) {
 70                var accessMethod = seleniumApi[functionName];
 71                var accessBlock = fnBind(accessMethod, seleniumApi);
 72                var baseName = match[2];
 73                var isBoolean = (match[1] == "is");
 74                var requiresTarget = (accessMethod.length == 1);
 75
 76                this.registerAccessor(functionName, accessBlock);
 77                this._registerStoreCommandForAccessor(baseName, accessBlock, requiresTarget);
 78
 79                var predicateBlock = this._predicateForAccessor(accessBlock, requiresTarget, isBoolean);
 80                this._registerAssertionsForPredicate(baseName, predicateBlock);
 81                this._registerWaitForCommandsForPredicate(seleniumApi, baseName, predicateBlock);
 82            }
 83        }
 84    },
 85
 86    _registerAllActions: function(seleniumApi) {
 87        for (var functionName in seleniumApi) {
 88            var match = /^do([A-Z].+)$/.exec(functionName);
 89            if (match) {
 90                var actionName = match[1].lcfirst();
 91                var actionMethod = seleniumApi[functionName];
 92                var dontCheckPopups = actionMethod.dontCheckAlertsAndConfirms;
 93                var actionBlock = fnBind(actionMethod, seleniumApi);
 94                this.registerAction(actionName, actionBlock, false, dontCheckPopups);
 95                this.registerAction(actionName + "AndWait", actionBlock, true, dontCheckPopups);
 96            }
 97        }
 98    },
 99
100    _registerAllAsserts: function(seleniumApi) {
101        for (var functionName in seleniumApi) {
102            var match = /^assert([A-Z].+)$/.exec(functionName);
103            if (match) {
104                var assertBlock = fnBind(seleniumApi[functionName], seleniumApi);
105
106                // Register the assert with the "assert" prefix, and halt on failure.
107                var assertName = functionName;
108                this.registerAssert(assertName, assertBlock, true);
109
110                // Register the assert with the "verify" prefix, and do not halt on failure.
111                var verifyName = "verify" + match[1];
112                this.registerAssert(verifyName, assertBlock, false);
113            }
114        }
115    },
116
117    registerAll: function(seleniumApi) {
118        this._registerAllAccessors(seleniumApi);
119        this._registerAllActions(seleniumApi);
120        this._registerAllAsserts(seleniumApi);
121    },
122
123    _predicateForAccessor: function(accessBlock, requiresTarget, isBoolean) {
124        if (isBoolean) {
125            return this._predicateForBooleanAccessor(accessBlock);
126        }
127        if (requiresTarget) {
128            return this._predicateForSingleArgAccessor(accessBlock);
129        }
130        return this._predicateForNoArgAccessor(accessBlock);
131    },
132
133    _predicateForSingleArgAccessor: function(accessBlock) {
134        // Given an accessor function getBlah(target),
135        // return a "predicate" equivalient to isBlah(target, value) that
136        // is true when the value returned by the accessor matches the specified value.
137        return function(target, value) {
138            var accessorResult = accessBlock(target);
139            accessorResult = selArrayToString(accessorResult);
140            if (PatternMatcher.matches(value, accessorResult)) {
141                return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
142            } else {
143                return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
144            }
145        };
146    },
147
148    _predicateForNoArgAccessor: function(accessBlock) {
149        // Given a (no-arg) accessor function getBlah(),
150        // return a "predicate" equivalient to isBlah(value) that
151        // is true when the value returned by the accessor matches the specified value.
152        return function(value) {
153            var accessorResult = accessBlock();
154            accessorResult = selArrayToString(accessorResult);
155            if (PatternMatcher.matches(value, accessorResult)) {
156                return new PredicateResult(true, "Actual value '" + accessorResult + "' did match '" + value + "'");
157            } else {
158                return new PredicateResult(false, "Actual value '" + accessorResult + "' did not match '" + value + "'");
159            }
160        };
161    },
162
163    _predicateForBooleanAccessor: function(accessBlock) {
164        // Given a boolean accessor function isBlah(),
165        // return a "predicate" equivalient to isBlah() that
166        // returns an appropriate PredicateResult value.
167        return function() {
168            var accessorResult;
169            if (arguments.length > 2) throw new SeleniumError("Too many arguments! " + arguments.length);
170            if (arguments.length == 2) {
171                accessorResult = accessBlock(arguments[0], arguments[1]);
172            } else if (arguments.length == 1) {
173                accessorResult = accessBlock(arguments[0]);
174            } else {
175                accessorResult = accessBlock();
176            }
177            if (accessorResult) {
178                return new PredicateResult(true, "true");
179            } else {
180                return new PredicateResult(false, "false");
181            }
182        };
183    },
184
185    _invertPredicate: function(predicateBlock) {
186        // Given a predicate, return the negation of that predicate.
187        // Leaves the message unchanged.
188        // Used to create assertNot, verifyNot, and waitForNot commands.
189        return function(target, value) {
190            var result = predicateBlock(target, value);
191            result.isTrue = !result.isTrue;
192            return result;
193        };
194    },
195
196    createAssertionFromPredicate: function(predicateBlock) {
197        // Convert an isBlahBlah(target, value) function into an assertBlahBlah(target, value) function.
198        return function(target, value) {
199            var result = predicateBlock(target, value);
200            if (!result.isTrue) {
201                Assert.fail(result.message);
202            }
203        };
204    },
205
206    _invertPredicateName: function(baseName) {
207        var matchResult = /^(.*)Present$/.exec(baseName);
208        if (matchResult != null) {
209            return matchResult[1] + "NotPresent";
210        }
211        return "Not" + baseName;
212    },
213
214    _registerAssertionsForPredicate: function(baseName, predicateBlock) {
215        // Register an assertion, a verification, a negative assertion,
216        // and a negative verification based on the specified accessor.
217        var assertBlock = this.createAssertionFromPredicate(predicateBlock);
218        this.registerAssert("assert" + baseName, assertBlock, true);
219        this.registerAssert("verify" + baseName, assertBlock, false);
220
221        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
222        var negativeassertBlock = this.createAssertionFromPredicate(invertedPredicateBlock);
223        this.registerAssert("assert" + this._invertPredicateName(baseName), negativeassertBlock, true);
224        this.registerAssert("verify" + this._invertPredicateName(baseName), negativeassertBlock, false);
225    },
226
227    _waitForActionForPredicate: function(predicateBlock) {
228        // Convert an isBlahBlah(target, value) function into a waitForBlahBlah(target, value) function.
229        return function(target, value) {
230            var terminationCondition = function () {
231                try {
232                    return predicateBlock(target, value).isTrue;
233                } catch (e) {
234                    // Treat exceptions as meaning the condition is not yet met.
235                    // Useful, for example, for waitForValue when the element has
236                    // not even been created yet.
237                    // TODO: possibly should rethrow some types of exception.
238                    return false;
239                }
240            };
241            return Selenium.decorateFunctionWithTimeout(terminationCondition, this.defaultTimeout);
242        };
243    },
244
245    _registerWaitForCommandsForPredicate: function(seleniumApi, baseName, predicateBlock) {
246        // Register a waitForBlahBlah and waitForNotBlahBlah based on the specified accessor.
247        var waitForActionMethod = this._waitForActionForPredicate(predicateBlock);
248        var waitForActionBlock = fnBind(waitForActionMethod, seleniumApi);
249        
250        var invertedPredicateBlock = this._invertPredicate(predicateBlock);
251        var waitForNotActionMethod = this._waitForActionForPredicate(invertedPredicateBlock);
252        var waitForNotActionBlock = fnBind(waitForNotActionMethod, seleniumApi);
253        
254        this.registerAction("waitFor" + baseName, waitForActionBlock, false, true);
255        this.registerAction("waitFor" + this._invertPredicateName(baseName), waitForNotActionBlock, false, true);
256        //TODO decide remove "waitForNot.*Present" action name or not
257        //for the back compatiblity issues we still make waitForNot.*Present availble
258        this.registerAction("waitForNot" + baseName, waitForNotActionBlock, false, true);
259    },
260
261    _registerStoreCommandForAccessor: function(baseName, accessBlock, requiresTarget) {
262        var action;
263        if (requiresTarget) {
264            action = function(target, varName) {
265                storedVars[varName] = accessBlock(target);
266            };
267        } else {
268            action = function(varName) {
269                storedVars[varName] = accessBlock();
270            };
271        }
272        this.registerAction("store" + baseName, action, false, true);
273    }
274
275});
276
277function PredicateResult(isTrue, message) {
278    this.isTrue = isTrue;
279    this.message = message;
280}
281
282// NOTE: The CommandHandler is effectively an abstract base for
283// various handlers including ActionHandler, AccessorHandler and AssertHandler.
284// Subclasses need to implement an execute(seleniumApi, command) function,
285// where seleniumApi is the Selenium object, and command a SeleniumCommand object.
286function CommandHandler(type, haltOnFailure) {
287    this.type = type;
288    this.haltOnFailure = haltOnFailure;
289}
290
291// An ActionHandler is a command handler that executes the sepcified action,
292// possibly checking for alerts and confirmations (if checkAlerts is set), and
293// possibly waiting for a page load if wait is set.
294function ActionHandler(actionBlock, wait, dontCheckAlerts) {
295    this.actionBlock = actionBlock;
296    CommandHandler.call(this, "action", true);
297    if (wait) {
298        this.wait = true;
299    }
300    // note that dontCheckAlerts could be undefined!!!
301    this.checkAlerts = (dontCheckAlerts) ? false : true;
302}
303ActionHandler.prototype = new CommandHandler;
304ActionHandler.prototype.execute = function(seleniumApi, command) {
305    if (this.checkAlerts && (null == /(Alert|Confirmation)(Not)?Present/.exec(command.command))) {
306        // todo: this conditional logic is ugly
307        seleniumApi.ensureNoUnhandledPopups();
308    }
309    var terminationCondition = this.actionBlock(command.target, command.value);
310    // If the handler didn't return a wait flag, check to see if the
311    // handler was registered with the wait flag.
312    if (terminationCondition == undefined && this.wait) {
313        terminationCondition = seleniumApi.makePageLoadCondition();
314    }
315    return new ActionResult(terminationCondition);
316};
317
318function ActionResult(terminationCondition) {
319    this.terminationCondition = terminationCondition;
320}
321
322function AccessorHandler(accessBlock) {
323    this.accessBlock = accessBlock;
324    CommandHandler.call(this, "accessor", true);
325}
326AccessorHandler.prototype = new CommandHandler;
327AccessorHandler.prototype.execute = function(seleniumApi, command) {
328    var returnValue = this.accessBlock(command.target, command.value);
329    return new AccessorResult(returnValue);
330};
331
332function AccessorResult(result) {
333    this.result = result;
334}
335
336/**
337 * Handler for assertions and verifications.
338 */
339function AssertHandler(assertBlock, haltOnFailure) {
340    this.assertBlock = assertBlock;
341    CommandHandler.call(this, "assert", haltOnFailure || false);
342}
343AssertHandler.prototype = new CommandHandler;
344AssertHandler.prototype.execute = function(seleniumApi, command) {
345    var result = new AssertResult();
346    try {
347        this.assertBlock(command.target, command.value);
348    } catch (e) {
349        // If this is not a AssertionFailedError, or we should haltOnFailure, rethrow.
350        if (!e.isAssertionFailedError) {
351            throw e;
352        }
353        if (this.haltOnFailure) {
354            var error = new SeleniumError(e.failureMessage);
355            throw error;
356        }
357        result.setFailed(e.failureMessage);
358    }
359    return result;
360};
361
362function AssertResult() {
363    this.passed = true;
364}
365AssertResult.prototype.setFailed = function(message) {
366    this.passed = null;
367    this.failed = true;
368    this.failureMessage = message;
369}
370
371function SeleniumCommand(command, target, value, isBreakpoint) {
372    this.command = command;
373    this.target = target;
374    this.value = value;
375    this.isBreakpoint = isBreakpoint;
376}
377