selenium-webdriver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  2. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  3. data/chrome/src/extension/background.html +9 -0
  4. data/chrome/src/extension/background.js +933 -0
  5. data/chrome/src/extension/content_script.js +1286 -0
  6. data/chrome/src/extension/manifest-nonwin.json +15 -0
  7. data/chrome/src/extension/manifest-win.json +16 -0
  8. data/chrome/src/extension/toolstrip.html +28 -0
  9. data/chrome/src/extension/utils.js +196 -0
  10. data/chrome/src/rb/lib/selenium/webdriver/chrome.rb +8 -0
  11. data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +324 -0
  12. data/chrome/src/rb/lib/selenium/webdriver/chrome/command_executor.rb +70 -0
  13. data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +119 -0
  14. data/common/src/js/abstractcommandprocessor.js +161 -0
  15. data/common/src/js/asserts.js +296 -0
  16. data/common/src/js/by.js +147 -0
  17. data/common/src/js/command.js +274 -0
  18. data/common/src/js/context.js +58 -0
  19. data/common/src/js/extension/README +2 -0
  20. data/common/src/js/extension/dommessenger.js +152 -0
  21. data/common/src/js/factory.js +55 -0
  22. data/common/src/js/future.js +118 -0
  23. data/common/src/js/key.js +117 -0
  24. data/common/src/js/localcommandprocessor.js +181 -0
  25. data/common/src/js/logging.js +249 -0
  26. data/common/src/js/testrunner.js +605 -0
  27. data/common/src/js/timing.js +89 -0
  28. data/common/src/js/wait.js +199 -0
  29. data/common/src/js/webdriver.js +853 -0
  30. data/common/src/js/webelement.js +683 -0
  31. data/common/src/rb/lib/selenium-webdriver.rb +1 -0
  32. data/common/src/rb/lib/selenium/webdriver.rb +52 -0
  33. data/common/src/rb/lib/selenium/webdriver/bridge_helper.rb +88 -0
  34. data/common/src/rb/lib/selenium/webdriver/child_process.rb +85 -0
  35. data/common/src/rb/lib/selenium/webdriver/core_ext/dir.rb +41 -0
  36. data/common/src/rb/lib/selenium/webdriver/driver.rb +128 -0
  37. data/common/src/rb/lib/selenium/webdriver/element.rb +126 -0
  38. data/common/src/rb/lib/selenium/webdriver/error.rb +68 -0
  39. data/common/src/rb/lib/selenium/webdriver/find.rb +69 -0
  40. data/common/src/rb/lib/selenium/webdriver/navigation.rb +23 -0
  41. data/common/src/rb/lib/selenium/webdriver/options.rb +50 -0
  42. data/common/src/rb/lib/selenium/webdriver/platform.rb +82 -0
  43. data/common/src/rb/lib/selenium/webdriver/target_locator.rb +23 -0
  44. data/firefox/prebuilt/nsICommandProcessor.xpt +0 -0
  45. data/firefox/prebuilt/nsINativeEvents.xpt +0 -0
  46. data/firefox/prebuilt/nsIResponseHandler.xpt +0 -0
  47. data/firefox/src/extension/chrome.manifest +3 -0
  48. data/firefox/src/extension/components/context.js +37 -0
  49. data/firefox/src/extension/components/driver-component.js +127 -0
  50. data/firefox/src/extension/components/firefoxDriver.js +706 -0
  51. data/firefox/src/extension/components/json2.js +273 -0
  52. data/firefox/src/extension/components/keytest.html +554 -0
  53. data/firefox/src/extension/components/nsCommandProcessor.js +586 -0
  54. data/firefox/src/extension/components/screenshooter.js +70 -0
  55. data/firefox/src/extension/components/socketListener.js +185 -0
  56. data/firefox/src/extension/components/utils.js +1200 -0
  57. data/firefox/src/extension/components/webLoadingListener.js +57 -0
  58. data/firefox/src/extension/components/webdriverserver.js +101 -0
  59. data/firefox/src/extension/components/wrappedElement.js +609 -0
  60. data/firefox/src/extension/content/fxdriver.xul +30 -0
  61. data/firefox/src/extension/content/server.js +95 -0
  62. data/firefox/src/extension/idl/nsICommandProcessor.idl +38 -0
  63. data/firefox/src/extension/idl/nsIResponseHandler.idl +34 -0
  64. data/firefox/src/extension/install.rdf +29 -0
  65. data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +21 -0
  66. data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +86 -0
  67. data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +426 -0
  68. data/firefox/src/rb/lib/selenium/webdriver/firefox/extension_connection.rb +82 -0
  69. data/firefox/src/rb/lib/selenium/webdriver/firefox/launcher.rb +132 -0
  70. data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +174 -0
  71. data/firefox/src/rb/lib/selenium/webdriver/firefox/profiles_ini.rb +60 -0
  72. data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +23 -0
  73. data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
  74. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
  75. data/jobbie/src/rb/lib/selenium/webdriver/ie.rb +14 -0
  76. data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +552 -0
  77. data/jobbie/src/rb/lib/selenium/webdriver/ie/lib.rb +94 -0
  78. data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +147 -0
  79. data/remote/client/src/rb/lib/selenium/webdriver/remote.rb +16 -0
  80. data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +374 -0
  81. data/remote/client/src/rb/lib/selenium/webdriver/remote/capabilities.rb +105 -0
  82. data/remote/client/src/rb/lib/selenium/webdriver/remote/commands.rb +53 -0
  83. data/remote/client/src/rb/lib/selenium/webdriver/remote/default_http_client.rb +71 -0
  84. data/remote/client/src/rb/lib/selenium/webdriver/remote/response.rb +43 -0
  85. data/remote/client/src/rb/lib/selenium/webdriver/remote/server_error.rb +32 -0
  86. metadata +182 -0
@@ -0,0 +1,89 @@
1
+ /** @license
2
+ Copyright 2007-2009 WebDriver committers
3
+ Copyright 2007-2009 Google Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ */
17
+
18
+ /**
19
+ * @fileoverview Defines a namespace that implements the global timing
20
+ * functions: setTimeout, setInterval, clearTimeout, and clearInterval.
21
+ * Internally, this namespaces uses protected references to the real global
22
+ * functions so that users can override them without interfering with WebDriver
23
+ * functionality.
24
+ * @author jmleyba@gmail.com (Jason Leyba)
25
+ */
26
+
27
+ goog.provide('webdriver.timing');
28
+
29
+ goog.require('goog.userAgent');
30
+
31
+
32
+ webdriver.timing.protectedSetTimeout_ = goog.global['setTimeout'];
33
+ webdriver.timing.protectedSetInterval_ = goog.global['setInterval'];
34
+ webdriver.timing.protectedClearTimeout_ = goog.global['clearTimeout'];
35
+ webdriver.timing.protectedClearInterval_ = goog.global['clearInterval'];
36
+
37
+
38
+ /**
39
+ * Schedules a function to be executed after the given {@code delay}.
40
+ * @param {function} fn The function to call after {@code delay} milliseconds.
41
+ * @param {number} delay The number of milliseconds to delay executing the
42
+ * function by.
43
+ * @return {number} The timeout ID that can be used with {@code #clearTimeout}
44
+ * to cancel executing {@code fn}.
45
+ */
46
+ webdriver.timing.setTimeout = function(fn, delay) {
47
+ return goog.userAgent.IE ?
48
+ webdriver.timing.protectedSetTimeout_(fn, delay) :
49
+ webdriver.timing.protectedSetTimeout_.call(null, fn, delay);
50
+ };
51
+
52
+
53
+ /**
54
+ * Schedules a function to be executed every {@code interval} milliseconds.
55
+ * @param {function} fn The function to call every {@code delay} milliseconds.
56
+ * @param {number} interval The number of milliseconds to delay executing the
57
+ * function by.
58
+ * @return {number} The interval ID that can be used with {@code #clearInterval}
59
+ * to cancel this interval.
60
+ */
61
+ webdriver.timing.setInterval = function(fn, interval) {
62
+ return goog.userAgent.IE ?
63
+ webdriver.timing.protectedSetInterval_(fn, interval) :
64
+ webdriver.timing.protectedSetInterval_.call(goog.global, fn, interval);
65
+ };
66
+
67
+
68
+ /**
69
+ * Cancels a timeout scheduled with {@code #setTimeout()}.
70
+ * @param {number} timeoutId ID of the timeout to cancel as returned by
71
+ * {@code #setTimeout}. Passing an invalid ID results in a no-op.
72
+ */
73
+ webdriver.timing.clearTimeout = function(timeoutId) {
74
+ return goog.userAgent.IE ?
75
+ webdriver.timing.protectedClearTimeout_(timeoutId) :
76
+ webdriver.timing.protectedClearTimeout_.call(goog.global, timeoutId);
77
+ };
78
+
79
+
80
+ /**
81
+ * Cancels an interval scheduled with {@code #clearInterval()}.
82
+ * @param {number} intervalId ID of the interval to cancel as returned by
83
+ * {@code #setInterval}. Passing an invalid ID results in a no-op.
84
+ */
85
+ webdriver.timing.clearInterval = function(intervalId) {
86
+ return goog.userAgent.IE ?
87
+ webdriver.timing.protectedClearInterval_(timeoutId) :
88
+ webdriver.timing.protectedClearInterval_.call(goog.global, intervalId);
89
+ };
@@ -0,0 +1,199 @@
1
+ /** @license
2
+ Copyright 2007-2009 WebDriver committers
3
+ Copyright 2007-2009 Google Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ */
17
+
18
+ /**
19
+ * @fileoverview Defines a mechanism for blocking {@code webdriver.WebDriver}
20
+ * command execution on user-defined conditions.
21
+ * @author jmleyba@gmail.com (Jason Leyba)
22
+ */
23
+
24
+ goog.provide('webdriver.Wait');
25
+
26
+ goog.require('goog.events');
27
+ goog.require('webdriver.Future');
28
+ goog.require('webdriver.timing');
29
+
30
+
31
+ /**
32
+ * Controls polling a condition that a {@code webdriver.WebDriver} instance
33
+ * should block command execution on.
34
+ * @param {function} conditionFn The condition function that the driver will
35
+ * poll. The function should require no arguments and return a boolean or a
36
+ * {@code webdriver.Future} that will evaluate to a boolean.
37
+ * @param {number} timeout The amount of time to wait, in milliseconds, for the
38
+ * condition to hold.
39
+ * @constructor
40
+ * @private
41
+ */
42
+ webdriver.Wait = function(conditionFn, timeout) {
43
+
44
+ /**
45
+ * Function to call for evaluating the condition being waited on.
46
+ * @type {function: boolean}
47
+ * @private
48
+ */
49
+ this.conditionFn_ = conditionFn;
50
+
51
+ /**
52
+ * The maximum amount of time in milliseconds to wait for condition.
53
+ * @type {number}
54
+ * @private
55
+ */
56
+ this.timeout_ = timeout;
57
+
58
+ /**
59
+ * When the wait began. Set on the first call to {@code #start()}.
60
+ * @type {number}
61
+ * @private
62
+ */
63
+ this.started_ = 0;
64
+
65
+ /**
66
+ * ID of the timeout timer. Initialized on the first call to {@code #start()}.
67
+ * @type {number}
68
+ * @private
69
+ */
70
+ this.timeoutId_ = null;
71
+
72
+ /**
73
+ * ID of the interval timer. Initialized on the first call to
74
+ * {@code #start()}.
75
+ * @type {number}
76
+ * @private
77
+ */
78
+ this.pollingIntervalId_ = null;
79
+
80
+ /**
81
+ * Whether to wait on the inverse of the wait condition.
82
+ * @type {boolean}
83
+ * @private
84
+ */
85
+ this.waitOnInverse_ = false;
86
+ };
87
+
88
+
89
+ /**
90
+ * Set this instance to wait for the inverse of its condition.
91
+ * @param {boolean} wait Whether to wait for the inverse of the condition.
92
+ */
93
+ webdriver.Wait.prototype.waitOnInverse = function(wait) {
94
+ this.waitOnInverse_ = wait;
95
+ };
96
+
97
+
98
+ /**
99
+ * Starts the timer the and starts evaluating the condition.
100
+ * @param {function} callbackFn The function to call with the result. The
101
+ * function should take 3 arguments: a boolean for whether the wait timed
102
+ * out (will only be true on a timeout), the amount of time spent in the
103
+ * wait. The 3rd parameter will be an Error, if any, that caused the wait
104
+ * to abort early.
105
+ */
106
+ webdriver.Wait.prototype.start = function(callbackFn) {
107
+ if (!!!this.started_) {
108
+ this.started_ = goog.now();
109
+
110
+ var tickHandler = goog.bind(this.onTick_, this, callbackFn);
111
+ var timeoutHandler = goog.bind(this.onTimeout_, this, callbackFn);
112
+
113
+ this.pollingIntervalId_ = webdriver.timing.setInterval(tickHandler, 5);
114
+ this.timeoutId_ =
115
+ webdriver.timing.setTimeout(timeoutHandler, this.timeout_);
116
+
117
+ this.onTick_(callbackFn);
118
+ }
119
+ };
120
+
121
+
122
+ /**
123
+ * Callback for when the wait times out.
124
+ * @param {function} callbackFn The function to call with the result.
125
+ * @private
126
+ */
127
+ webdriver.Wait.prototype.onTimeout_ = function(callbackFn) {
128
+ webdriver.timing.clearInterval(this.pollingIntervalId_);
129
+ if (this.pendingFuture_) {
130
+ this.pendingFuture_.dispose();
131
+ }
132
+ callbackFn(true, goog.now() - this.started_);
133
+ };
134
+
135
+
136
+ /**
137
+ * Evaluates the wait condition and determines whether to abort the wait.
138
+ * @param {function} callbackFn The function to call with the result.
139
+ * @private
140
+ */
141
+ webdriver.Wait.prototype.onTick_ = function(callbackFn) {
142
+ if (this.pendingFuture_) {
143
+ return;
144
+ }
145
+
146
+ var elapsed = goog.now() - this.started_;
147
+ var value;
148
+ try {
149
+ value = this.conditionFn_();
150
+ if (value instanceof webdriver.Future) {
151
+ if (value.isSet() &&
152
+ this.checkValue_(value.getValue(), elapsed, callbackFn)) {
153
+ return;
154
+ }
155
+
156
+ this.pendingFuture_ = value;
157
+ goog.events.listenOnce(value, goog.events.EventType.CHANGE, function() {
158
+ this.pendingFuture_ = null;
159
+ this.checkValue_(value.getValue(), goog.now() - this.started_,
160
+ callbackFn);
161
+ }, false, this);
162
+
163
+ } else {
164
+ this.checkValue_(value, elapsed, callbackFn);
165
+ }
166
+
167
+ } catch (ex) {
168
+ webdriver.timing.clearInterval(this.pollingIntervalId_);
169
+ webdriver.timing.clearTimeout(this.timeoutId_);
170
+ if (this.pendingFuture_) {
171
+ this.pendingFuture_.dispose();
172
+ }
173
+ callbackFn(true, elapsed, ex);
174
+ }
175
+ };
176
+
177
+
178
+ /**
179
+ * Checks the value returned by the condition function.
180
+ * @param {boolean} value The result of the condition function.
181
+ * @param {number} elapsed The time elapsed since the wait started.
182
+ * @param {function} callbackFn The function to call with the result.
183
+ * @private
184
+ */
185
+ webdriver.Wait.prototype.checkValue_ = function(value, elapsed, callbackFn) {
186
+ value = !!value;
187
+ if (this.waitOnInverse_) {
188
+ value = !value;
189
+ }
190
+ if (value) {
191
+ webdriver.timing.clearInterval(this.pollingIntervalId_);
192
+ webdriver.timing.clearTimeout(this.timeoutId_);
193
+ if (this.pendingFuture_) {
194
+ this.pendingFuture_.dispose();
195
+ }
196
+ callbackFn(false, elapsed);
197
+ }
198
+ return value;
199
+ };
@@ -0,0 +1,853 @@
1
+ /** @license
2
+ Copyright 2007-2009 WebDriver committers
3
+ Copyright 2007-2009 Google Inc.
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ */
17
+
18
+ /**
19
+ * @fileoverview The heart of the WebDriver JavaScript API.
20
+ * @author jmleyba@gmail.com (Jason Leyba)
21
+ */
22
+
23
+ goog.provide('webdriver.WebDriver');
24
+ goog.provide('webdriver.WebDriver.EventType');
25
+
26
+ goog.require('goog.events');
27
+ goog.require('goog.events.EventTarget');
28
+ goog.require('webdriver.Command');
29
+ goog.require('webdriver.CommandName');
30
+ goog.require('webdriver.Context');
31
+ goog.require('webdriver.Future');
32
+ goog.require('webdriver.Response');
33
+ goog.require('webdriver.Wait');
34
+ goog.require('webdriver.WebElement');
35
+ goog.require('webdriver.logging');
36
+ goog.require('webdriver.timing');
37
+
38
+
39
+ /**
40
+ * The main interface for controlling a web browser. How the browser is
41
+ * controlled is dictated by the injected {@code commandProcessor}. The command
42
+ * processor may control the browser either through an extension or plugin, or
43
+ * by sending commands to a RemoteWebDriver server.
44
+ *
45
+ * Any WebDriver command that is expected to produce a return value will return
46
+ * a {@code webdriver.Future}. This Future can passed as an argument to another
47
+ * command, or an assertion function in the {@code webdriver.asserts} namespace.
48
+ * For example:
49
+ * driver.get('http://www.google.com');
50
+ * var futureTitle = driver.getTitle();
51
+ * assertThat(futureTitle, equals('Google Search'));
52
+ *
53
+ * The WebDriver will dispatch the following events:
54
+ * <ul>
55
+ * <li>webdriver.WebDriver.EventType.PAUSED - Command execution has been halted
56
+ * and no more commands will be processed until {@code #resume()} is called
57
+ * </li>
58
+ * <li>webdriver.WebDriver.EventType.RESUMED - The driver has resumed execution
59
+ * after being paused</li>
60
+ * <li>webdriver.WebDriver.EventType.ERROR - Dispatched whenever a WebDriver
61
+ * command fails</li>
62
+ * </ul>
63
+ *
64
+ * @param {Object} commandProcessor The command processor to use for executing
65
+ * individual {@code webdriver.Command}s.
66
+ * @constructor
67
+ * @extends {goog.events.EventTarget}
68
+ */
69
+ webdriver.WebDriver = function(commandProcessor) {
70
+ goog.events.EventTarget.call(this);
71
+
72
+ /**
73
+ * The command processor to use for executing commands.
74
+ * @type {Object}
75
+ * @private
76
+ */
77
+ this.commandProcessor_ = commandProcessor;
78
+
79
+ /**
80
+ * List of commands that have been sent to the command processor and are
81
+ * await results. This array should only ever have three sizes:
82
+ * 0: there are no pending commands with the command processor
83
+ * 1: a single command is pending with the command processor
84
+ * 2: a command within a wait condition is pending with the command
85
+ * processor
86
+ * @type {Array.<webdriver.Command>}
87
+ * @private
88
+ */
89
+ this.pendingCommands_ = [];
90
+
91
+ /**
92
+ * A stack of frames for managing batched command execution order.
93
+ * @type {Array.<Array.<webdriver.Command>>}
94
+ * @private
95
+ */
96
+ this.frames_ = [[]];
97
+
98
+ /**
99
+ * A list of commands that have been successfully completed since the last
100
+ * reset.
101
+ * @type {Array.<webdriver.Command>}
102
+ * @priate
103
+ */
104
+ this.commandHistory_ = [];
105
+
106
+ /**
107
+ * Whether this instance is paused. When paused, commands can still be issued,
108
+ * but no commands will be executed.
109
+ * @type {boolean}
110
+ * @private
111
+ */
112
+ this.isPaused_ = false;
113
+
114
+ /**
115
+ * This instances current context (window and frame ID).
116
+ * @type {webdriver.Context}
117
+ * @private
118
+ */
119
+ this.context_ = new webdriver.Context();
120
+
121
+ /**
122
+ * Whether this instance is locked into its current session. Once locked in,
123
+ * any further calls to {@code webdriver.WebDriver.prototype.newSession} will
124
+ * be ignored.
125
+ * @type {boolean}
126
+ * @private
127
+ */
128
+ this.sessionLocked_ = false;
129
+
130
+ /**
131
+ * This instance's current session ID. Set with the
132
+ * {@code webdriver.WebDriver.prototype.newSession} command.
133
+ * @type {?string}
134
+ * @private
135
+ */
136
+ this.sessionId_ = null;
137
+
138
+ /**
139
+ * Interval ID for the command processing loop.
140
+ * @type {number}
141
+ * @private
142
+ */
143
+ this.commandInterval_ = webdriver.timing.setInterval(
144
+ goog.bind(this.processCommands_, this),
145
+ webdriver.WebDriver.COMMAND_INTERVAL_LENGTH_);
146
+ };
147
+ goog.inherits(webdriver.WebDriver, goog.events.EventTarget);
148
+
149
+
150
+ /**
151
+ * The amount of time in milliseconds between ticks of the command processing
152
+ * interval.
153
+ * @type {number}
154
+ * @private
155
+ */
156
+ webdriver.WebDriver.COMMAND_INTERVAL_LENGTH_ = 10;
157
+
158
+
159
+ /**
160
+ * Enumeration of the events that may be dispatched by an instance of
161
+ * {@code webdriver.WebDriver}.
162
+ * @enum {string}
163
+ */
164
+ webdriver.WebDriver.EventType = {
165
+ ERROR: 'ERROR',
166
+ PAUSED: 'PAUSED',
167
+ RESUMED: 'RESUMED'
168
+ };
169
+
170
+
171
+ /**
172
+ * Enumeration of the supported mouse speeds.
173
+ * @enum {number}
174
+ * @see webdriver.WebDriver.prototype.setMouseSpeed
175
+ * @see webdriver.WebDriver.prototype.getMouseSpeed
176
+ */
177
+ webdriver.WebDriver.Speed = {
178
+ SLOW: 1,
179
+ MEDIUM: 10,
180
+ FAST: 100
181
+ };
182
+
183
+
184
+ /**
185
+ * @override
186
+ */
187
+ webdriver.WebDriver.prototype.dispose = function() {
188
+ this.commandProcessor_.dispose();
189
+ webdriver.timing.clearInterval(this.commandInterval_);
190
+
191
+ delete this.commandProcessor_;
192
+ delete this.pendingCommands_;
193
+ delete this.frames_;
194
+ delete this.commandHistory_;
195
+ delete this.isPaused_;
196
+ delete this.context_;
197
+ delete this.sessionLocked_;
198
+ delete this.sessionId_;
199
+ delete this.commandInterval_;
200
+
201
+ webdriver.WebDriver.superClass_.dispose.call(this);
202
+ };
203
+
204
+
205
+ /**
206
+ * Queues a command to execute.
207
+ * @param {webdriver.Command} command The command to execute.
208
+ * @param {boolean} opt_addToFront Whether to add the command to the front or
209
+ * back of the queue. Defaults to false.
210
+ * @protected
211
+ */
212
+ webdriver.WebDriver.prototype.addCommand = function(command, opt_addToFront) {
213
+ if (!(command instanceof webdriver.Command)) {
214
+ throw new Error(
215
+ 'IllegalArgument: command not an instanceof webdriver.Command');
216
+ }
217
+ var frame = goog.array.peek(this.frames_);
218
+ if (opt_addToFront) {
219
+ goog.array.insertAt(frame, command, 0);
220
+ } else {
221
+ frame.push(command);
222
+ }
223
+ };
224
+
225
+
226
+ /**
227
+ * @return {webdriver.Command} The command currently being executed or
228
+ * {@code undefined}.
229
+ */
230
+ webdriver.WebDriver.prototype.getPendingCommand = function() {
231
+ return goog.array.peek(this.pendingCommands_);
232
+ };
233
+
234
+
235
+ /**
236
+ * Aborts the pending command, if any. If the pending command is part of a
237
+ * {@code #wait()}, then the entire wait operation will be aborted.
238
+ */
239
+ webdriver.WebDriver.prototype.abortPendingCommand = function() {
240
+ goog.array.forEach(this.pendingCommands_, function(command) {
241
+ command.abort = true;
242
+ });
243
+ this.pendingCommands_ = [];
244
+ this.waitFrame_ = null;
245
+ };
246
+
247
+
248
+ /**
249
+ * Immediately pauses the driver so it will not execute anymore commands until
250
+ * {@code #resume()} is called.
251
+ * Dispatches a {@code webdriver.WebDriver.EventType.PAUSED} event.
252
+ */
253
+ webdriver.WebDriver.prototype.pauseImmediately = function() {
254
+ this.isPaused_ = true;
255
+ webdriver.logging.debug('Webdriver paused');
256
+ this.dispatchEvent(webdriver.WebDriver.EventType.PAUSED);
257
+ };
258
+
259
+
260
+ /**
261
+ * Unpauses this driver so it can execute commands again. Dispatches a
262
+ * {@code webdriver.WebDriver.EventType.RESUMED} event.
263
+ */
264
+ webdriver.WebDriver.prototype.resume = function() {
265
+ this.isPaused_ = false;
266
+ webdriver.logging.debug('Webdriver resumed');
267
+ this.dispatchEvent(webdriver.WebDriver.EventType.RESUMED);
268
+ };
269
+
270
+
271
+ /**
272
+ * Event handler for whenever this driver is ready to execute a command.
273
+ * @private
274
+ */
275
+ webdriver.WebDriver.prototype.processCommands_ = function() {
276
+ if (this.isPaused_) {
277
+ return;
278
+ }
279
+
280
+ var pendingCommand = this.getPendingCommand();
281
+ if (pendingCommand && webdriver.CommandName.WAIT != pendingCommand.name) {
282
+ return;
283
+ }
284
+
285
+ var currentFrame = goog.array.peek(this.frames_);
286
+ var nextCommand = currentFrame.shift();
287
+ if (nextCommand) {
288
+ this.pendingCommands_.push(nextCommand);
289
+ if (nextCommand.name == webdriver.CommandName.FUNCTION) {
290
+ this.frames_.push([]);
291
+ } else if (nextCommand.name == webdriver.CommandName.WAIT) {
292
+ this.waitFrame_ = [];
293
+ this.frames_.push(this.waitFrame_);
294
+ }
295
+
296
+ nextCommand.setCompleteCallback(this.onCommandComplete_, this);
297
+ this.commandProcessor_.execute(nextCommand, this.sessionId_, this.context_);
298
+ } else if (this.frames_.length > 1) {
299
+ if (currentFrame !== this.waitFrame_) {
300
+ this.frames_.pop();
301
+ }
302
+ }
303
+ };
304
+
305
+
306
+ /**
307
+ * Callback for when a pending {@code webdriver.Command} is finished.
308
+ * @private
309
+ */
310
+ webdriver.WebDriver.prototype.onCommandComplete_ = function(command) {
311
+ this.commandHistory_.push(command);
312
+ if (command.response.isFailure || command.response.errors.length) {
313
+ if (webdriver.CommandName.WAIT == command.name) {
314
+ // The wait terminated early. Abort all other commands issued inside the
315
+ // wait condition.
316
+ for (var i = 1; i < this.pendingCommands_.length; i++) {
317
+ this.pendingCommands_[i].abort = true;
318
+ }
319
+ this.pendingCommands_ = [this.pendingCommands_[0]];
320
+ }
321
+ this.dispatchEvent(webdriver.WebDriver.EventType.ERROR);
322
+ } else {
323
+ this.pendingCommands_.pop();
324
+ if (webdriver.CommandName.WAIT == command.name) {
325
+ this.waitFrame_ = null;
326
+ }
327
+ }
328
+ };
329
+
330
+
331
+
332
+ /**
333
+ * @return {?string} This instance's current session ID or {@code null} if it
334
+ * does not have one yet.
335
+ */
336
+ webdriver.WebDriver.prototype.getSessionId = function() {
337
+ return this.sessionId_;
338
+ };
339
+
340
+
341
+ /**
342
+ * @return {webdriver.Context} This instance's current context.
343
+ */
344
+ webdriver.WebDriver.prototype.getContext = function() {
345
+ return this.context_;
346
+ };
347
+
348
+
349
+ // ----------------------------------------------------------------------------
350
+ // Client command functions:
351
+ // ----------------------------------------------------------------------------
352
+
353
+ /**
354
+ * Adds an event handler to catch any {@code ERROR} events from the previous
355
+ * command. If the previous command generates an ERROR, that error will be
356
+ * suppressed and this instance will continue executing commands. If an error
357
+ * was not raised, a new {@code webdriver.WebDriver.EventType.ERROR} event will
358
+ * be dispatched for the unexpected behavior.
359
+ * @param {string} opt_errorMsg The message to include with the ERROR event if
360
+ * the expected error does not occur.
361
+ */
362
+ webdriver.WebDriver.prototype.catchExpectedError = function(opt_errorMsg,
363
+ opt_handlerFn) {
364
+ var currentFrame = goog.array.peek(this.frames_);
365
+ var previousCommand = currentFrame.pop();
366
+ if (!previousCommand) {
367
+ throw new Error('No commands in the queue to expect an error from');
368
+ }
369
+
370
+ var listener =
371
+ goog.events.getListener(this, webdriver.WebDriver.EventType.ERROR, true);
372
+ if (listener) {
373
+ throw new Error('IllegalState: Driver already has a registered ' +
374
+ 'expected error handler');
375
+ }
376
+
377
+ var caughtError = false;
378
+ var handleError = function(e) {
379
+ caughtError = true;
380
+ e.stopPropagation();
381
+ e.preventDefault();
382
+ if (goog.isFunction(opt_handlerFn)) {
383
+ opt_handlerFn(e.target.getPendingCommand());
384
+ }
385
+ goog.events.removeAll(
386
+ e.target, webdriver.WebDriver.EventType.ERROR, /*capture=*/true);
387
+
388
+ // Errors cause the pending command to hang. Go ahead and abort that command
389
+ // so we can proceed.
390
+ this.abortPendingCommand();
391
+ var frame = goog.array.peek(this.frames_);
392
+ while (frame !== currentFrame) {
393
+ this.frames_.pop();
394
+ frame = goog.array.peek(this.frames_);
395
+ }
396
+ return false;
397
+ };
398
+
399
+ // Surround the last command with two new commands. The first enables our
400
+ // error listener which cancels any errors. The second verifies that we
401
+ // caught an error. If not, it fails the test.
402
+ var catchExpected = new webdriver.Command(webdriver.CommandName.FUNCTION).
403
+ setParameters(goog.bind(function() {
404
+ goog.events.listenOnce(this, webdriver.WebDriver.EventType.ERROR,
405
+ handleError, /*capture=*/true);
406
+ }, this));
407
+
408
+ var cleanupCatch = new webdriver.Command(webdriver.CommandName.FUNCTION).
409
+ setParameters(goog.bind(function() {
410
+ // Need to unlisten for error events so the error below doesn't get
411
+ // blocked.
412
+ goog.events.unlisten(this, webdriver.WebDriver.EventType.ERROR,
413
+ handleError, /*capture=*/true);
414
+ if (!caughtError) {
415
+ throw new Error(
416
+ (opt_errorMsg ? (opt_errorMsg + '\n') : '') +
417
+ 'Expected an error but none were raised.');
418
+ }
419
+ }, this));
420
+
421
+ currentFrame.push(catchExpected);
422
+ currentFrame.push(previousCommand);
423
+ currentFrame.push(cleanupCatch);
424
+ };
425
+
426
+
427
+ /**
428
+ * Adds a command to pause this driver so it will not execute anymore commands
429
+ * until {@code #resume()} is called. When this command executes, a
430
+ * {@code webdriver.WebDriver.EventType.PAUSED} event will be dispatched.
431
+ */
432
+ webdriver.WebDriver.prototype.pause = function() {
433
+ this.callFunction(goog.bind(this.pauseImmediately, this));
434
+ };
435
+
436
+
437
+ /**
438
+ * Has the driver temporarily halt command execution. This command does
439
+ * <em>not</em> result in a {@code webdriver.WebDriver.EventType.PAUSED} event.
440
+ * @param {number} ms The amount of time in milliseconds for the driver to
441
+ * sleep.
442
+ */
443
+ webdriver.WebDriver.prototype.sleep = function(ms) {
444
+ this.addCommand(new webdriver.Command(webdriver.CommandName.SLEEP).
445
+ setParameters(ms));
446
+ };
447
+
448
+
449
+ /**
450
+ * Inserts a function into the command queue for the driver to call. The
451
+ * function will be passed the last {@code webdriver.Response} retrieved from
452
+ * the command processor. The result of the function will be stored in a new
453
+ * {@code webdriver.Response} and passed to any subsequent function commands.
454
+ * @param {function} fn The function to call; should take a single
455
+ * {@code webdriver.Response} object.
456
+ */
457
+ webdriver.WebDriver.prototype.callFunction = function(fn, opt_selfObj,
458
+ var_args) {
459
+ var args = goog.array.slice(arguments, 2);
460
+ var wrappedFunction = goog.bind(function() {
461
+ var lastCommand = goog.array.peek(this.commandHistory_);
462
+ args.push(lastCommand ? lastCommand.response :null);
463
+ fn.apply(opt_selfObj, args);
464
+ }, this);
465
+ this.addCommand(new webdriver.Command(webdriver.CommandName.FUNCTION).
466
+ setParameters(wrappedFunction));
467
+ };
468
+
469
+
470
+ /**
471
+ * Waits for a condition to be true before executing the next command. If the
472
+ * condition does not hold after the given {@code timeout}, an error will be
473
+ * raised. Only one wait may be performed at a time (e.g. no nesting).
474
+ * Example:
475
+ * <code>
476
+ * driver.get('http://www.google.com');
477
+ * var element = driver.findElement({name: 'q'});
478
+ * driver.wait(element.isDisplayed, 3000, element);
479
+ * </code>
480
+ * @param {function} conditionFn The function to evaluate.
481
+ * @param {number} timeout The maximum amount of time to wait, in milliseconds.
482
+ * @param {Object} opt_self (Optional) The object in whose context to execute
483
+ * the {@code conditionFn}.
484
+ * @throws If this driver is currently executing another wait command.
485
+ * @see webdriver.Wait
486
+ */
487
+ webdriver.WebDriver.prototype.wait = function(conditionFn, timeout, opt_self) {
488
+ if (this.pendingCommands_.length) {
489
+ var command = this.pendingCommands_[0];
490
+ if (webdriver.CommandName.WAIT == command.name) {
491
+ throw new Error('Nested waits are not supported');
492
+ }
493
+ }
494
+
495
+ if (opt_self) {
496
+ conditionFn = goog.bind(conditionFn, opt_self);
497
+ }
498
+ var waitOp = new webdriver.Wait(conditionFn, timeout);
499
+ this.addCommand(new webdriver.Command(webdriver.CommandName.WAIT).
500
+ setParameters(waitOp));
501
+ };
502
+
503
+
504
+ /**
505
+ * Waits for the inverse of a condition to be true before executing the next
506
+ * command. If the condition does not hold after the given {@code timeout}, an
507
+ * error will be raised. Example:
508
+ * <code>
509
+ * driver.get('http://www.google.com');
510
+ * var element = driver.findElement({name: 'q'});
511
+ * driver.waitNot(element.isDisplayed, 3000, element);
512
+ * </code>
513
+ * @param {function} conditionFn The function to evaluate.
514
+ * @param {number} timeout The maximum amount of time to wait, in milliseconds.
515
+ * @param {Object} opt_self (Optional) The object in whose context to execute
516
+ * the {@code conditionFn}.
517
+ * @see webdriver.Wait
518
+ */
519
+ webdriver.WebDriver.prototype.waitNot = function(conditionFn, timeout, opt_self,
520
+ opt_interval) {
521
+ if (opt_self) {
522
+ conditionFn = goog.bind(conditionFn, opt_self);
523
+ }
524
+ var waitOp = new webdriver.Wait(conditionFn, timeout);
525
+ waitOp.waitOnInverse(true);
526
+ this.addCommand(new webdriver.Command(webdriver.CommandName.WAIT).
527
+ setParameters(waitOp));
528
+ };
529
+
530
+
531
+ /**
532
+ * Request a new session ID. This is a no-op if this instance is already locked
533
+ * into a session.
534
+ * @param {boolean} lockSession Whether to lock this instance into the returned
535
+ * session. Once locked into a session, the driver cannot ask for a new
536
+ * session (a new instance must be created).
537
+ */
538
+ webdriver.WebDriver.prototype.newSession = function(lockSession) {
539
+ if (lockSession) {
540
+ this.addCommand(new webdriver.Command(webdriver.CommandName.NEW_SESSION).
541
+ setSuccessCallback(function(response) {
542
+ this.sessionLocked_ = lockSession;
543
+ this.sessionId_ = response.value;
544
+ this.context_ = response.context;
545
+ }, this));
546
+ } else {
547
+ webdriver.logging.warn(
548
+ 'Cannot start new session; driver is locked into current session');
549
+ }
550
+ };
551
+
552
+
553
+ /**
554
+ * Switch the focus of future commands for this driver to the window with the
555
+ * given name.
556
+ * @param {string|webdriver.Future} name The name of the window to transfer
557
+ * control to. Alternatively, the UUID of a window handle, returned by
558
+ * {@code #getWindowHandle()} or {@code #getAllWindowHandles()}.
559
+ */
560
+ webdriver.WebDriver.prototype.switchToWindow = function(name) {
561
+ this.addCommand(new webdriver.Command(webdriver.CommandName.SWITCH_TO_WINDOW).
562
+ setParameters(name).
563
+ setSuccessCallback(function(response) {
564
+ this.context_ = response.value;
565
+ }, this));
566
+ };
567
+
568
+
569
+ /**
570
+ * Switch the focus of future commands for this driver to the frame with the
571
+ * given name or ID. To select sub-frames, simply separate the frame names/IDs
572
+ * by dots. As an example, {@code 'main.child'} will select the frame with the
573
+ * name 'main' and hten its child 'child'. If a frame name is a number, then it
574
+ * will be treated as an index into the {@code window.frames} array of the
575
+ * current window.
576
+ * @param {string|number|webdriver.WebElement} frame Identifier for the frame
577
+ * to transfer control to.
578
+ */
579
+ webdriver.WebDriver.prototype.switchToFrame = function(frame) {
580
+ var commandName = webdriver.CommandName.SWITCH_TO_FRAME;
581
+ var command;
582
+ if (goog.isString(frame) || goog.isNumber(frame)) {
583
+ command = new webdriver.Command(commandName).setParameters(frame);
584
+ } else {
585
+ command = new webdriver.Command(commandName, frame);
586
+ }
587
+ this.addCommand(command.setSuccessCallback(function(response) {
588
+ this.context_ = response.context;
589
+ }, this));
590
+ };
591
+
592
+
593
+ /**
594
+ * Selects either the first frame on the page, or the main document when a page
595
+ * contains iframes.
596
+ */
597
+ webdriver.WebDriver.prototype.switchToDefaultContent = function() {
598
+ var command =
599
+ new webdriver.Command(webdriver.CommandName.SWITCH_TO_DEFAULT_CONTENT).
600
+ setParameters(null).
601
+ setSuccessCallback(function(response) {
602
+ this.context_ = response.context;
603
+ }, this);
604
+ this.addCommand(command);
605
+ };
606
+
607
+
608
+ /**
609
+ * Retrieves the internal UUID handle for the current window.
610
+ * @return {webdriver.Future} The current handle wrapped in a Future.
611
+ */
612
+ webdriver.WebDriver.prototype.getWindowHandle = function() {
613
+ var handle = new webdriver.Future(this);
614
+ var command =
615
+ new webdriver.Command(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE).
616
+ setSuccessCallback(handle.setValueFromResponse, handle);
617
+ this.addCommand(command);
618
+ return handle;
619
+ };
620
+
621
+
622
+ /**
623
+ * Retrieves the handles for all known windows.
624
+ */
625
+ webdriver.WebDriver.prototype.getAllWindowHandles = function() {
626
+ var command =
627
+ new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES).
628
+ setSuccessCallback(function(response) {
629
+ response.value = response.value.split(',');
630
+ });
631
+ this.addCommand(command);
632
+ };
633
+
634
+
635
+ /**
636
+ * Retrieves the HTML source of the current page.
637
+ * @return {webdriver.Future} The page source wrapped in a Future.
638
+ */
639
+ webdriver.WebDriver.prototype.getPageSource = function() {
640
+ var source = new webdriver.Future(this);
641
+ var command = new webdriver.Command(webdriver.CommandName.GET_PAGE_SOURCE).
642
+ setSuccessCallback(source.setValueFromResponse, source);
643
+ this.addCommand(command);
644
+ return source;
645
+ };
646
+
647
+
648
+ /**
649
+ * Closes the current window.
650
+ * <strong>WARNING: This command provides no protection against closing the
651
+ * script window (e.g. the window sending commands to the driver)</strong>
652
+ */
653
+ webdriver.WebDriver.prototype.close = function() {
654
+ this.addCommand(new webdriver.Command(webdriver.CommandName.CLOSE));
655
+ };
656
+
657
+
658
+
659
+ /**
660
+ * Helper function for converting an argument to a script into a parameter
661
+ * object to send with the {@code webdriver.Command}.
662
+ * @param {*} arg The value to convert.
663
+ * @return {Object} A JSON object with "type" and "value" properties.
664
+ * @see {webdriver.WebDriver.prototype.executeScript}
665
+ * @private
666
+ */
667
+ webdriver.WebDriver.mapToExecuteScriptArgument_ = function(arg) {
668
+ var type, value;
669
+ if (arg instanceof webdriver.WebElement) {
670
+ type = 'ELEMENT';
671
+ value = arg.getId();
672
+ } else if (goog.isBoolean(arg) ||
673
+ goog.isNumber(arg) ||
674
+ goog.isString(arg)) {
675
+ type = goog.typeOf(arg).toUpperCase();
676
+ value = arg;
677
+ } else {
678
+ throw new Error('Invalid script argument type: ' + goog.typeOf(arg));
679
+ }
680
+ return {'type': type, 'value': value};
681
+ };
682
+
683
+
684
+ /**
685
+ * Adds a command to execute a JavaScript snippet in the window of the page
686
+ * currently under test.
687
+ * @param {string} script The JavaScript snippet to execute.
688
+ * @param {*} var_args The arguments to pass to the script.
689
+ * @return {webdriver.Future} The result of the executed script, wrapped in a
690
+ * {@code webdriver.Future} instance.
691
+ */
692
+ webdriver.WebDriver.prototype.executeScript = function(script, var_args) {
693
+ var args = goog.array.map(
694
+ goog.array.slice(arguments, 1),
695
+ webdriver.WebDriver.mapToExecuteScriptArgument_);
696
+ var result = new webdriver.Future(this);
697
+ this.addCommand(new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT).
698
+ setParameters(script, args).
699
+ setSuccessCallback(function(response) {
700
+ switch(response.extraData['resultType']) {
701
+ case 'NULL':
702
+ response.value = null;
703
+ break;
704
+
705
+ case 'ELEMENT':
706
+ var id = response.value;
707
+ response.value = new webdriver.WebElement(this);
708
+ response.value.getId().setValue(id);
709
+ break;
710
+
711
+ case 'OTHER': // Fall-through
712
+ default:
713
+ break;
714
+ }
715
+ result.setValue(response.value);
716
+ }, this));
717
+ return result;
718
+ };
719
+
720
+
721
+ /**
722
+ * Adds a command to fetch the given URL.
723
+ * @param {goog.Uri|string} url The URL to fetch.
724
+ */
725
+ webdriver.WebDriver.prototype.get = function(url) {
726
+ this.addCommand(new webdriver.Command(webdriver.CommandName.GET).
727
+ setParameters(url.toString()).
728
+ setSuccessCallback(function(response) {
729
+ this.context_ = response.context;
730
+ }, this));
731
+ };
732
+
733
+
734
+ /**
735
+ * Navigate backwards in the current browser window's history.
736
+ */
737
+ webdriver.WebDriver.prototype.back = function() {
738
+ this.addCommand(new webdriver.Command(webdriver.CommandName.BACK));
739
+ };
740
+
741
+
742
+ /**
743
+ * Navigate forwards in the current browser window's history.
744
+ */
745
+ webdriver.WebDriver.prototype.forward = function() {
746
+ this.addCommand(new webdriver.Command(webdriver.CommandName.FORWARD));
747
+ };
748
+
749
+
750
+ /**
751
+ * Refresh the current page.
752
+ */
753
+ webdriver.WebDriver.prototype.refresh = function() {
754
+ this.addCommand(new webdriver.Command(webdriver.CommandName.REFRESH));
755
+ };
756
+
757
+
758
+ /**
759
+ * Retrieves the current window URL.
760
+ * @return {webdriver.Future} The current URL in a webdriver.Future.
761
+ */
762
+ webdriver.WebDriver.prototype.getCurrentUrl = function() {
763
+ var url = new webdriver.Future(this);
764
+ this.addCommand(new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL).
765
+ setSuccessCallback(url.setValueFromResponse, url));
766
+ return url;
767
+ };
768
+
769
+
770
+ /**
771
+ * Retrieves the current page's title.
772
+ * @return {webdriver.Future} The current page title.
773
+ */
774
+ webdriver.WebDriver.prototype.getTitle = function() {
775
+ var title = new webdriver.Future(this);
776
+ this.addCommand(new webdriver.Command(webdriver.CommandName.GET_TITLE).
777
+ setSuccessCallback(title.setValueFromResponse, title));
778
+ return title;
779
+ };
780
+
781
+
782
+ /**
783
+ * Find an element on the current page. If the element cannot be found, an
784
+ * {@code webdriver.WebDriver.EventType.ERROR} event will be dispatched.
785
+ * @param {webdriver.By.Locator|object} by An object describing the locator
786
+ * strategy to use.
787
+ * @return {webdriver.WebElement} A WebElement wrapper that can be used to
788
+ * issue commands against the located element.
789
+ */
790
+ webdriver.WebDriver.prototype.findElement = function(by) {
791
+ return webdriver.WebElement.findElement(this, by);
792
+ };
793
+
794
+
795
+ /**
796
+ * Determine if an element is present on the page.
797
+ * @param {webdriver.By.Locator|{*: string}} by The locator to use for finding
798
+ * the element, or a short-hand object that can be converted into a locator.
799
+ * @return {webdriver.Future} Whether the element was present on the page. The
800
+ * return value is wrapped in a Future that will be defined when the driver
801
+ * completes the command.
802
+ * @see webdriver.By.Locator.createFromObj
803
+ */
804
+ webdriver.WebDriver.prototype.isElementPresent = function(by) {
805
+ return webdriver.WebElement.isElementPresent(this, by);
806
+ };
807
+
808
+
809
+
810
+ /**
811
+ * Search for multiple elements on the current page. The result of this
812
+ * operation can be accessed from the last saved {@code webdriver.Response}
813
+ * object:
814
+ * driver.findElements({xpath: '//div'});
815
+ * driver.callFunction(function(response) {
816
+ * response.value[0].click();
817
+ * response.value[1].click();
818
+ * // etc.
819
+ * });
820
+ * @param {webdriver.By.Locator|{*: string}} by The locator to use for finding
821
+ * the element, or a short-hand object that can be converted into a locator.
822
+ * @see webdriver.By.Locator.createFromObj
823
+ */
824
+ webdriver.WebDriver.prototype.findElements = function(by) {
825
+ return webdriver.WebElement.findElements(this, by);
826
+ };
827
+
828
+
829
+ /**
830
+ * Adjust the speed of the mouse for mouse related commands.
831
+ * @param {webdriver.WebDriver.Speed} speed The new speed setting.
832
+ */
833
+ webdriver.WebDriver.prototype.setMouseSpeed = function(speed) {
834
+ this.addCommand(new webdriver.Command(webdriver.CommandName.SET_MOUSE_SPEED).
835
+ setParameters(speed));
836
+ };
837
+
838
+
839
+ /**
840
+ * Fetch the current mouse speed.
841
+ * @return {webdriver.Future} A Future whose value will be set by this driver
842
+ * when the query command completes.
843
+ */
844
+ webdriver.WebDriver.prototype.getMouseSpeed = function() {
845
+ var speed = new webdriver.Future(this);
846
+ this.addCommand(
847
+ new webdriver.Command(webdriver.CommandName.GET_MOUSE_SPEED).
848
+ setSuccessCallback(function(response) {
849
+ response.value = Number(response.value);
850
+ speed.setValue(response.value);
851
+ }));
852
+ return speed;
853
+ };