| // Copyright 2012 Selenium comitters |
| // Copyright 2012 Software Freedom Conservancy |
| // |
| // 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. |
| |
| goog.provide('webdriver.ActionSequence'); |
| |
| goog.require('goog.array'); |
| goog.require('webdriver.Button'); |
| goog.require('webdriver.Command'); |
| goog.require('webdriver.CommandName'); |
| goog.require('webdriver.Key'); |
| |
| |
| |
| /** |
| * Class for defining sequences of complex user interactions. Each sequence |
| * will not be executed until {@link #perform} is called. |
| * |
| * <p>Example:<pre><code> |
| * new webdriver.ActionSequence(driver). |
| * keyDown(webdriver.Key.SHIFT). |
| * click(element1). |
| * click(element2). |
| * dragAndDrop(element3, element4). |
| * keyUp(webdriver.Key.SHIFT). |
| * perform(); |
| * </pre></code> |
| * |
| * @param {!webdriver.WebDriver} driver The driver instance to use. |
| * @constructor |
| */ |
| webdriver.ActionSequence = function(driver) { |
| |
| /** |
| * @type {!webdriver.WebDriver} |
| * @private |
| */ |
| this.driver_ = driver; |
| |
| /** |
| * @type {!Array.<{description: string, command: !webdriver.Command}>} |
| * @private |
| */ |
| this.actions_ = []; |
| }; |
| |
| |
| /** |
| * Schedules an action to be executed each time {@link #perform} is called on |
| * this instance. |
| * @param {string} description A description of the command. |
| * @param {!webdriver.Command} command The command. |
| * @private |
| */ |
| webdriver.ActionSequence.prototype.schedule_ = function(description, command) { |
| this.actions_.push({ |
| description: description, |
| command: command |
| }); |
| }; |
| |
| |
| /** |
| * Executes this action sequence. |
| * @return {!webdriver.promise.Promise} A promise that will be resolved once |
| * this sequence has completed. |
| */ |
| webdriver.ActionSequence.prototype.perform = function() { |
| // Make a protected copy of the scheduled actions. This will protect against |
| // users defining additional commands before this sequence is actually |
| // executed. |
| var actions = goog.array.clone(this.actions_); |
| var driver = this.driver_; |
| return webdriver.promise.Application.getInstance().schedule( |
| 'ActionSequence.perform', function() { |
| goog.array.forEach(actions, function(action) { |
| driver.schedule(action.command, action.description); |
| }); |
| }); |
| }; |
| |
| |
| /** |
| * Moves the mouse. The location to move to may be specified in terms of the |
| * mouse's current location, an offset relative to the top-left corner of an |
| * element, or an element (in which case the middle of the element is used). |
| * @param {(!webdriver.WebElement|{x: number, y: number})} location The |
| * location to drag to, as either another WebElement or an offset in pixels. |
| * @param {{x: number, y: number}=} opt_offset An optional offset, in pixels. |
| * Defaults to (0, 0). |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.mouseMove = function(location, opt_offset) { |
| var command = new webdriver.Command(webdriver.CommandName.MOVE_TO); |
| |
| var offset = opt_offset || {x: 0, y: 0}; |
| |
| if (goog.isNumber(location.x)) { |
| offset.x += location.x; |
| offset.y += location.y; |
| } else { |
| // The interactions API expect the element ID to be encoded as a simple |
| // string, not the usual JSON object. |
| var id = (/** @type {!webdriver.WebElement} */location).toWireValue(). |
| then(function(value) { |
| return value['ELEMENT']; |
| }); |
| command.setParameter('element', id); |
| } |
| |
| command.setParameter('xoffset', offset.x); |
| command.setParameter('yoffset', offset.y); |
| |
| this.schedule_('mouseMove', command); |
| return this; |
| }; |
| |
| |
| /** |
| * Schedules a mouse action. |
| * @param {string} description A simple descriptive label for the scheduled |
| * action. |
| * @param {!webdriver.CommandName} commandName The name of the command. |
| * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either |
| * the element to interact with or the button to click with. |
| * Defaults to {@link webdriver.Button.LEFT} if neither an element nor |
| * button is specified. |
| * @param {webdriver.Button=} opt_button The button to use. Defaults to |
| * {@link webdriver.Button.LEFT}. Ignored if the previous argument is |
| * provided as a button. |
| * @return {!webdriver.ActionSequence} A self reference. |
| * @private |
| */ |
| webdriver.ActionSequence.prototype.scheduleMouseAction_ = function( |
| description, commandName, opt_elementOrButton, opt_button) { |
| var button; |
| if (goog.isNumber(opt_elementOrButton)) { |
| button = opt_elementOrButton; |
| } else { |
| if (opt_elementOrButton) { |
| this.mouseMove((/** @type {!webdriver.WebElement} */opt_elementOrButton)); |
| } |
| button = goog.isDef(opt_button) ? opt_button : webdriver.Button.LEFT; |
| } |
| |
| var command = new webdriver.Command(commandName). |
| setParameter('button', button); |
| this.schedule_(description, command); |
| return this; |
| }; |
| |
| |
| /** |
| * Presses a mouse button. The mouse button will not be released until |
| * {@link #mouseUp} is called, regardless of whether that call is made in this |
| * sequence or another. The behavior for out-of-order events (e.g. mouseDown, |
| * click) is undefined. |
| * |
| * <p>If an element is provided, the mouse will first be moved to the center |
| * of that element. This is equivalent to: |
| * <pre><code>sequence.mouseMove(element).mouseDown()</code></pre> |
| * |
| * <p>Warning: this method currently only supports the left mouse button. See |
| * http://code.google.com/p/selenium/issues/detail?id=4047 |
| * |
| * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either |
| * the element to interact with or the button to click with. |
| * Defaults to {@link webdriver.Button.LEFT} if neither an element nor |
| * button is specified. |
| * @param {webdriver.Button=} opt_button The button to use. Defaults to |
| * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the |
| * first argument. |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.mouseDown = function(opt_elementOrButton, |
| opt_button) { |
| return this.scheduleMouseAction_('mouseDown', |
| webdriver.CommandName.MOUSE_DOWN, opt_elementOrButton, opt_button); |
| }; |
| |
| |
| /** |
| * Releases a mouse button. Behavior is undefined for calling this function |
| * without a previous call to {@link #mouseDown}. |
| * |
| * <p>If an element is provided, the mouse will first be moved to the center |
| * of that element. This is equivalent to: |
| * <pre><code>sequence.mouseMove(element).mouseUp()</code></pre> |
| * |
| * <p>Warning: this method currently only supports the left mouse button. See |
| * http://code.google.com/p/selenium/issues/detail?id=4047 |
| * |
| * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either |
| * the element to interact with or the button to click with. |
| * Defaults to {@link webdriver.Button.LEFT} if neither an element nor |
| * button is specified. |
| * @param {webdriver.Button=} opt_button The button to use. Defaults to |
| * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the |
| * first argument. |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.mouseUp = function(opt_elementOrButton, |
| opt_button) { |
| return this.scheduleMouseAction_('mouseUp', |
| webdriver.CommandName.MOUSE_UP, opt_elementOrButton, opt_button); |
| }; |
| |
| |
| /** |
| * Convenience function for performing a "drag and drop" manuever. The target |
| * element may be moved to the location of another element, or by an offset (in |
| * pixels). |
| * @param {!webdriver.WebElement} element The element to drag. |
| * @param {(!webdriver.WebElement|{x: number, y: number})} location The |
| * location to drag to, either as another WebElement or an offset in pixels. |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.dragAndDrop = function(element, location) { |
| return this.mouseDown(element).mouseMove(location).mouseUp(); |
| }; |
| |
| |
| /** |
| * Clicks a mouse button. |
| * |
| * <p>If an element is provided, the mouse will first be moved to the center |
| * of that element. This is equivalent to: |
| * <pre><code>sequence.mouseMove(element).click()</code></pre> |
| * |
| * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either |
| * the element to interact with or the button to click with. |
| * Defaults to {@link webdriver.Button.LEFT} if neither an element nor |
| * button is specified. |
| * @param {webdriver.Button=} opt_button The button to use. Defaults to |
| * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the |
| * first argument. |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.click = function(opt_elementOrButton, |
| opt_button) { |
| return this.scheduleMouseAction_('click', |
| webdriver.CommandName.CLICK, opt_elementOrButton, opt_button); |
| }; |
| |
| |
| /** |
| * Double-clicks a mouse button. |
| * |
| * <p>If an element is provided, the mouse will first be moved to the center of |
| * that element. This is equivalent to: |
| * <pre><code>sequence.mouseMove(element).doubleClick()</code></pre> |
| * |
| * <p>Warning: this method currently only supports the left mouse button. See |
| * http://code.google.com/p/selenium/issues/detail?id=4047 |
| * |
| * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either |
| * the element to interact with or the button to click with. |
| * Defaults to {@link webdriver.Button.LEFT} if neither an element nor |
| * button is specified. |
| * @param {webdriver.Button=} opt_button The button to use. Defaults to |
| * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the |
| * first argument. |
| * @return {!webdriver.ActionSequence} A self reference. |
| */ |
| webdriver.ActionSequence.prototype.doubleClick = function(opt_elementOrButton, |
| opt_button) { |
| return this.scheduleMouseAction_('doubleClick', |
| webdriver.CommandName.DOUBLE_CLICK, opt_elementOrButton, opt_button); |
| }; |
| |
| |
| /** |
| * Schedules a keyboard action. |
| * @param {string} description A simple descriptive label for the scheduled |
| * action. |
| * @param {!Array.<(string|!webdriver.Key)>} keys The keys to send. |
| * @return {!webdriver.ActionSequence} A self reference. |
| * @private |
| */ |
| webdriver.ActionSequence.prototype.scheduleKeyboardAction_ = function( |
| description, keys) { |
| var command = |
| new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ACTIVE_ELEMENT). |
| setParameter('value', keys); |
| this.schedule_(description, command); |
| return this; |
| }; |
| |
| |
| /** |
| * Checks that a key is a modifier key. |
| * @param {!webdriver.Key} key The key to check. |
| * @throws {Error} If the key is not a modifier key. |
| * @private |
| */ |
| webdriver.ActionSequence.checkModifierKey_ = function(key) { |
| if (key !== webdriver.Key.ALT && key !== webdriver.Key.CONTROL && |
| key !== webdriver.Key.SHIFT && key !== webdriver.Key.COMMAND) { |
| throw Error('Not a modifier key'); |
| } |
| }; |
| |
| |
| /** |
| * Performs a modifier key press. The modifier key is <em>not released</em> |
| * until {@link #keyUp} or {@link #sendKeys} is called. The key press will be |
| * targetted at the currently focused element. |
| * @param {!webdriver.Key} key The modifier key to push. Must be one of |
| * {ALT, CONTROL, SHIFT, COMMAND, META}. |
| * @return {!webdriver.ActionSequence} A self reference. |
| * @throws {Error} If the key is not a valid modifier key. |
| */ |
| webdriver.ActionSequence.prototype.keyDown = function(key) { |
| webdriver.ActionSequence.checkModifierKey_(key); |
| return this.scheduleKeyboardAction_('keyDown', [key]); |
| }; |
| |
| |
| /** |
| * Performs a modifier key release. The release is targetted at the currently |
| * focused element. |
| * @param {!webdriver.Key} key The modifier key to release. Must be one of |
| * {ALT, CONTROL, SHIFT, COMMAND, META}. |
| * @return {!webdriver.ActionSequence} A self reference. |
| * @throws {Error} If the key is not a valid modifier key. |
| */ |
| webdriver.ActionSequence.prototype.keyUp = function(key) { |
| webdriver.ActionSequence.checkModifierKey_(key); |
| return this.scheduleKeyboardAction_('keyUp', [key]); |
| }; |
| |
| |
| /** |
| * Simulates typing multiple keys. Each modifier key encountered in the |
| * sequence will not be released until it is encountered again. All key events |
| * will be targetted at the currently focused element. |
| * @param {...(string|!webdriver.Key|!Array.<(string|!webdriver.Key)>)} |
| * var_args The keys to type. |
| * @return {!webdriver.ActionSequence} A self reference. |
| * @throws {Error} If the key is not a valid modifier key. |
| */ |
| webdriver.ActionSequence.prototype.sendKeys = function(var_args) { |
| var keys = goog.array.flatten(goog.array.slice(arguments, 0)); |
| return this.scheduleKeyboardAction_('sendKeys', keys); |
| }; |