selenium-webdriver 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -348,7 +348,7 @@ function sendResponseToParsedRequest(toSend, wait) {
348
348
  ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
349
349
  console.log("SENDING RESPOND TO PARSED REQUEST");
350
350
  sendResponseByXHR(JSON.stringify(toSend), wait);
351
- setToolstripsBusy(false);
351
+ setExtensionBusyIndicator(false);
352
352
  }
353
353
 
354
354
  /**
@@ -387,7 +387,7 @@ function parseRequest(request) {
387
387
  return;
388
388
  }
389
389
  ChromeDriver.isBlockedWaitingForResponse = true;
390
- setToolstripsBusy(true);
390
+ setExtensionBusyIndicator(true);
391
391
 
392
392
  switch (request.request) {
393
393
  case "get":
@@ -429,6 +429,9 @@ function parseRequest(request) {
429
429
  }, false);
430
430
  }
431
431
  break;
432
+ case "screenshot":
433
+ getScreenshot();
434
+ break;
432
435
  case "clickElement":
433
436
  case "hoverOverElement":
434
437
  // Falling through, as native events are handled the same
@@ -486,6 +489,19 @@ function parseRequest(request) {
486
489
  }
487
490
  }
488
491
 
492
+ function getScreenshot() {
493
+ chrome.tabs.captureVisibleTab(null, getScreenshotResult);
494
+ }
495
+
496
+ function getScreenshotResult(snapshotDataUrl) {
497
+ var index = snapshotDataUrl.indexOf('base64,');
498
+ if (index == -1) {
499
+ sendResponseToParsedRequest({statusCode: 99}, false);
500
+ return;
501
+ }
502
+ var base64 = snapshotDataUrl.substring(index + 'base64,'.length);
503
+ sendResponseToParsedRequest({statusCode: 0, value: base64}, false);
504
+ }
489
505
 
490
506
  function sendMessageOnActivePortAndAlsoKeepTrackOfIt(message) {
491
507
  ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = message.request;
@@ -889,23 +905,12 @@ function sendEmptyResponseWhenTabIsLoaded(tab) {
889
905
  }
890
906
 
891
907
 
892
- function setToolstripsBusy(busy) {
893
- var toolstrips = chrome.extension.getToolstrips(ChromeDriver.activeWindowId);
908
+ function setExtensionBusyIndicator(busy) {
894
909
  if (busy) {
895
910
  chrome.browserAction.setIcon({path: "icons/busy.png"})
896
911
  } else {
897
912
  chrome.browserAction.setIcon({path: "icons/free.png"})
898
913
  }
899
- for (var toolstrip in toolstrips) {
900
- if (toolstrips[toolstrip].setWebdriverToolstripBusy &&
901
- toolstrips[toolstrip].setWebdriverToolstripFree) {
902
- if (busy) {
903
- toolstrips[toolstrip].setWebdriverToolstripBusy();
904
- } else {
905
- toolstrips[toolstrip].setWebdriverToolstripFree();
906
- }
907
- }
908
- }
909
914
  }
910
915
 
911
916
  function setActivePortByWindowName(handle) {
@@ -2,7 +2,6 @@
2
2
  "name": "WebDriver",
3
3
  "version": "0.3",
4
4
  "description": "WebDriver extension for Chrome",
5
- "toolstrips": ["toolstrip.html"],
6
5
  "browser_action": {
7
6
  "default_title": "WebDriver",
8
7
  "default_icon": "icons/free.png"
@@ -12,7 +11,7 @@
12
11
  {
13
12
  "matches": ["http://*/*", "https://*/*", "file:///*"],
14
13
  "js": ["utils.js", "content_script.js"],
15
- "run_at": "document_start"
14
+ "run_at": "document_end"
16
15
  }
17
16
  ],
18
17
  "permissions": ["http://*/*", "tabs"]
@@ -2,7 +2,6 @@
2
2
  "name": "WebDriver",
3
3
  "version": "0.2",
4
4
  "description": "WebDriver extension for Chrome",
5
- "toolstrips": ["toolstrip.html"],
6
5
  "browser_action": {
7
6
  "default_title": "WebDriver",
8
7
  "default_icon": "icons/free.png"
@@ -12,7 +11,7 @@
12
11
  {
13
12
  "matches": ["http://*/*", "https://*/*", "file:///*"],
14
13
  "js": ["utils.js", "content_script.js"],
15
- "run_at": "document_start"
14
+ "run_at": "document_end"
16
15
  }
17
16
  ],
18
17
  "plugins": [{"path": "npchromedriver.dll", "public": true}],
@@ -17,7 +17,7 @@ module Selenium
17
17
  end
18
18
 
19
19
  def driver_extensions
20
- []
20
+ [DriverExtensions::TakesScreenshot]
21
21
  end
22
22
 
23
23
  def get(url)
@@ -73,6 +73,10 @@ module Selenium
73
73
  execute :request => 'getCurrentWindowHandle'
74
74
  end
75
75
 
76
+ def getScreenshotAsBase64
77
+ execute :request => "screenshot"
78
+ end
79
+
76
80
  def setSpeed(value)
77
81
  @speed = value
78
82
  end
@@ -96,10 +96,6 @@ webdriver.AbstractCommandProcessor.prototype.execute = function(command,
96
96
  break;
97
97
 
98
98
  case webdriver.CommandName.WAIT:
99
- var callback = goog.bind(this.waitCallback_, this, command, context);
100
- command.parameters[0].start(callback);
101
- break;
102
-
103
99
  case webdriver.CommandName.FUNCTION:
104
100
  try {
105
101
  var result = command.parameters[0]();
@@ -120,35 +116,6 @@ webdriver.AbstractCommandProcessor.prototype.execute = function(command,
120
116
  };
121
117
 
122
118
 
123
- /**
124
- * Callback for {@code webdriver.CommandName.WAIT} commands.
125
- * @param {webdriver.Command} command The wait command.
126
- * @param {webdriver.Context} context The context the command executed in.
127
- * @param {boolean} isTimeout Whether the wait finished from a timeout.
128
- * @param {number} elapsedTime The amount of time spent waiting, in
129
- * milliseconds.
130
- * @param {Error} opt_ex If defined, an error that caused the wait to terminate
131
- * early.
132
- */
133
- webdriver.AbstractCommandProcessor.prototype.waitCallback_ = function(
134
- command, context, isTimeout, elapsedTime, opt_ex) {
135
- var message;
136
- if (opt_ex) {
137
- message = 'Error';
138
- } else if (isTimeout) {
139
- message = 'Timeout';
140
- } else {
141
- message = 'Ready';
142
- }
143
- var response = new webdriver.Response(isTimeout || opt_ex, context,
144
- message + ' after ' + elapsedTime + 'ms');
145
- if (opt_ex) {
146
- response.errors.push(opt_ex);
147
- }
148
- command.setResponse(response);
149
- };
150
-
151
-
152
119
  /**
153
120
  * Sends a command to be executed by a browser driver. This method must be
154
121
  * implemented by each subclass.
@@ -164,7 +164,7 @@ webdriver.asserts.Matcher.isTheSameLocationAs = function(expected) {
164
164
  function () {
165
165
  return 'equal ' + webdriver.asserts.objToString(expected);
166
166
  });
167
- }
167
+ };
168
168
  goog.exportSymbol('isTheSameLocationAs',
169
169
  webdriver.asserts.Matcher.isTheSameLocationAs);
170
170
 
@@ -200,7 +200,7 @@ webdriver.asserts.assertThat = function(a, b, opt_c) {
200
200
  'Expected to ' + matcher.describe() +
201
201
  '\n but was ' + webdriver.asserts.objToString(future));
202
202
  }
203
- }
203
+ };
204
204
 
205
205
  if (future instanceof webdriver.Future) {
206
206
  // Schedule a function with the Future's controlling driver so the value
@@ -25,6 +25,8 @@ goog.provide('webdriver.By.Locator');
25
25
  goog.provide('webdriver.By.Strategy');
26
26
 
27
27
  goog.require('goog.object');
28
+ goog.require('goog.string');
29
+
28
30
 
29
31
 
30
32
  /**
@@ -25,22 +25,44 @@ goog.provide('webdriver.CommandName');
25
25
  goog.provide('webdriver.Response');
26
26
 
27
27
  goog.require('goog.array');
28
+ goog.require('goog.events.EventTarget');
29
+ goog.require('goog.testing.stacktrace');
30
+ goog.require('webdriver.Future');
28
31
 
29
32
 
30
33
  /**
31
34
  * Describes a command to be executed by a
32
35
  * {@code webdriver.AbstractCommandProcessor}.
33
- * @param {string} name The name of this command.
34
- * @param {webdriver.WebElement} opt_element The element to perform this command
35
- * on. If not defined, the command will be performed relative to the
36
- * document root.
36
+ * @param {webdriver.WebDriver} driver The driver that this is a command for.
37
+ * @param {webdriver.CommandName} name The name of this command.
38
+ * @param {webdriver.WebElement} opt_element The element to perform this
39
+ * command on. If not defined, the command will be performed relative to
40
+ * the document root.
37
41
  * @constructor
42
+ * @extends {goog.events.EventTarget}
38
43
  */
39
- webdriver.Command = function(name, opt_element) {
44
+ webdriver.Command = function(driver, name, opt_element) {
45
+ goog.events.EventTarget.call(this);
46
+
47
+ /**
48
+ * The driver that this is a command to.
49
+ * @type {webdriver.WebDriver}
50
+ * @private
51
+ */
52
+ this.driver_ = driver;
53
+
54
+ /**
55
+ * A future that will be automatically updated with the value of this
56
+ * command's response when it is ready. If the command fails, the
57
+ * future's value will not be set.
58
+ * @type {webdriver.Future}
59
+ * @private
60
+ */
61
+ this.futureResult_ = new webdriver.Future(this.driver_);
40
62
 
41
63
  /**
42
64
  * The name of this command.
43
- * @type {string}
65
+ * @type {webdriver.CommandName}
44
66
  */
45
67
  this.name = name;
46
68
 
@@ -75,26 +97,74 @@ webdriver.Command = function(name, opt_element) {
75
97
  */
76
98
  this.onFailureCallbackFn = null;
77
99
 
78
- /**
79
- * Callback for when this command is completely finished, which is after the
80
- * response is set and success/failure callbacks have been run. The function
81
- * should take a single argument, a reference to this command.
82
- * @type {?function}
83
- * @private
84
- */
85
- this.onCompleteCallbackFn_ = null;
86
-
87
100
  /**
88
101
  * The response to this command.
89
- * @type {webdriver.Response}
102
+ * @type {?webdriver.Response}
90
103
  */
91
104
  this.response = null;
92
105
 
93
- /**
94
- * Whether this command was aborted.
95
- * @type {boolean}
96
- */
97
- this.abort = false;
106
+ };
107
+ goog.inherits(webdriver.Command, goog.events.EventTarget);
108
+
109
+
110
+ /**
111
+ * The event dispatched by a command when it fails.
112
+ * @type {string}
113
+ */
114
+ webdriver.Command.ERROR_EVENT = 'ERROR';
115
+
116
+
117
+ /** @override */
118
+ webdriver.Command.prototype.disposeInternal = function() {
119
+ webdriver.Command.superClass_.disposeInternal.call(this);
120
+ this.futureResult_.dispose();
121
+ delete this.driver_;
122
+ delete this.futureResult_;
123
+ delete this.name;
124
+ delete this.element;
125
+ delete this.parameters;
126
+ delete this.onSuccessCallbackFn;
127
+ delete this.onFailureCallbackFn;
128
+ delete this.response;
129
+ };
130
+
131
+
132
+ /** @override */
133
+ webdriver.Command.prototype.toString = function() {
134
+ return this.name;
135
+ };
136
+
137
+
138
+ /**
139
+ * @return {webdriver.WebDriver} The driver that this is a command to.
140
+ */
141
+ webdriver.Command.prototype.getDriver = function() {
142
+ return this.driver_;
143
+ };
144
+
145
+
146
+ /**
147
+ * @return {webdriver.CommandName} This command's name.
148
+ */
149
+ webdriver.Command.prototype.getName = function() {
150
+ return this.name;
151
+ };
152
+
153
+
154
+ /**
155
+ * @return {webdriver.Future} The future result (value-only) of this command.
156
+ */
157
+ webdriver.Command.prototype.getFutureResult = function() {
158
+ return this.futureResult_;
159
+ };
160
+
161
+
162
+ /**
163
+ * @return {boolean} Whether this command has finished; aborted commands are
164
+ * never considered finished.
165
+ */
166
+ webdriver.Command.prototype.isFinished = function() {
167
+ return !!this.response;
98
168
  };
99
169
 
100
170
 
@@ -143,17 +213,10 @@ webdriver.Command.prototype.setFailureCallback = function(callbackFn,
143
213
 
144
214
 
145
215
  /**
146
- * Set the function to call with this command when it is completed.
147
- * @param {function} callbackFn The function to call on command completion.
148
- * @param {Object} opt_selfObj The object in whose context to execute the
149
- * function.
216
+ * @return {?webdriver.Response} The response to this command if it is ready.
150
217
  */
151
- webdriver.Command.prototype.setCompleteCallback = function(callbackFn,
152
- opt_selfObj) {
153
- if (callbackFn) {
154
- this.onCompleteCallbackFn_ = goog.bind(callbackFn, opt_selfObj);
155
- }
156
- return this;
218
+ webdriver.Command.prototype.getResponse = function() {
219
+ return this.response;
157
220
  };
158
221
 
159
222
 
@@ -164,7 +227,7 @@ webdriver.Command.prototype.setCompleteCallback = function(callbackFn,
164
227
  * @throws If the response was already set.
165
228
  */
166
229
  webdriver.Command.prototype.setResponse = function(response) {
167
- if (this.response) {
230
+ if (this.isDisposed() || this.isFinished()) {
168
231
  return;
169
232
  }
170
233
  this.response = response;
@@ -188,8 +251,10 @@ webdriver.Command.prototype.setResponse = function(response) {
188
251
  }
189
252
  }
190
253
 
191
- if (this.onCompleteCallbackFn_) {
192
- this.onCompleteCallbackFn_(this);
254
+ if (!this.response.isFailure) {
255
+ this.futureResult_.setValue(this.response.value);
256
+ } else {
257
+ this.dispatchEvent(webdriver.Command.ERROR_EVENT);
193
258
  }
194
259
  };
195
260
 
@@ -259,7 +324,7 @@ webdriver.CommandName = {
259
324
  * {@code true}, then {@code value} contains the error message.
260
325
  * @param {webdriver.Context} context The (potentially new) context resulting
261
326
  * from the command.
262
- * @param {string} value The value of the response, the meaning of which depends
327
+ * @param {*} value The value of the response, the meaning of which depends
263
328
  * on the command.
264
329
  * @parma {Error} opt_error An error that caused this command to fail
265
330
  * prematurely.
@@ -270,5 +335,34 @@ webdriver.Response = function(isFailure, context, value, opt_error) {
270
335
  this.context = context;
271
336
  this.value = value;
272
337
  this.errors = goog.array.slice(arguments, 3);
273
- this.extraData = {};
338
+ };
339
+
340
+
341
+ /**
342
+ * @return {?string} A formatted error message, or {@code null} if this is not a
343
+ * failure response.
344
+ */
345
+ webdriver.Response.prototype.getErrorMessage = function() {
346
+ if (!this.isFailure) {
347
+ return null;
348
+ }
349
+ var message = [];
350
+ if (goog.isString(this.value)) {
351
+ message.push(this.value);
352
+ } else if (null != this.value && goog.isDef(this.value.message)) {
353
+ message.push(this.value.message);
354
+ if (goog.isDef(this.value.fileName)) {
355
+ message.push(this.value.fileName + '@' + this.value.lineNumber);
356
+ }
357
+ }
358
+ goog.array.extend(message, goog.array.map(this.errors, function(error) {
359
+ if (goog.isString(error)) {
360
+ return error;
361
+ }
362
+ var errMsg = error.message || error.description || error.toString();
363
+ var stack = error.stack ?
364
+ goog.testing.stacktrace.canonicalize(error.stack) : error['stackTrace'];
365
+ return errMsg + '\n' + stack;
366
+ }));
367
+ return message.join('\n');
274
368
  };
@@ -23,24 +23,20 @@ limitations under the License.
23
23
 
24
24
  goog.provide('webdriver.Future');
25
25
 
26
- goog.require('goog.events.EventType');
27
- goog.require('goog.events.EventTarget');
26
+ goog.require('goog.Disposable');
27
+ goog.require('goog.array');
28
28
 
29
29
 
30
30
  /**
31
31
  * Represents the result of an asynchronous {@code webdriver.Command}. Methods
32
32
  * are provided to check if the result has been set and to retrieve the result.
33
- * <p/>
34
- * This instance will dispatch a {@code goog.events.EventType.CHANGE} event when
35
- * its value is set. An {@code Error} will be thrown if {@code #getValue()} is
36
- * called before the value has been set.
37
33
  * @param {webdriver.WebDriver} driver The WebDriver instance that will
38
34
  * eventually set this instance's value.
39
35
  * @constructor
40
- * @extends {goog.events.EventTarget}
36
+ * @extends {goog.Disposable}
41
37
  */
42
38
  webdriver.Future = function(driver) {
43
- goog.events.EventTarget.call(this);
39
+ goog.Disposable.call(this);
44
40
 
45
41
  /**
46
42
  * The WebDriver that will eventaully set this instance's value.
@@ -55,8 +51,16 @@ webdriver.Future = function(driver) {
55
51
  * @private
56
52
  */
57
53
  this.value_ = webdriver.Future.NOT_SET_;
54
+
55
+ /**
56
+ * Futures whose values are linked to this one. When this future's value is
57
+ * set, all of the linked futures will also be updated.
58
+ * @type {Array.<webdriver.Future>}
59
+ * @private
60
+ */
61
+ this.linkedFutures_ = [];
58
62
  };
59
- goog.inherits(webdriver.Future, goog.events.EventTarget);
63
+ goog.inherits(webdriver.Future, goog.Disposable);
60
64
 
61
65
 
62
66
  /**
@@ -68,6 +72,15 @@ goog.inherits(webdriver.Future, goog.events.EventTarget);
68
72
  webdriver.Future.NOT_SET_ = {};
69
73
 
70
74
 
75
+ /** @override */
76
+ webdriver.Future.prototype.disposeInternal = function() {
77
+ delete this.driver_;
78
+ delete this.value_;
79
+ delete this.linkedFutures_;
80
+ webdriver.Future.superClass_.disposeInternal.call(this);
81
+ };
82
+
83
+
71
84
  /**
72
85
  * @return {*} The value of this Future.
73
86
  * @throws If the value has not been set yet.
@@ -90,23 +103,29 @@ webdriver.Future.prototype.getDriver = function() {
90
103
 
91
104
 
92
105
  /**
93
- * Sets the value of this Future. Dispatches a
94
- * {@code goog.events.EventType.CHANGE} event.
106
+ * Sets the value of this Future and dispatches a
107
+ * {@code goog.events.EventType.CHANGE} event. If the given value is another
108
+ * future and its value has not been set, a callback will be registered to
109
+ * set this instance's value when the input future's value is set. Note that
110
+ * the value of a future may only be set once.
95
111
  * @param {*} value The new value.
96
112
  */
97
113
  webdriver.Future.prototype.setValue = function(value) {
98
- this.value_ = value;
99
- this.dispatchEvent(goog.events.EventType.CHANGE);
100
- };
101
-
102
-
103
- /**
104
- * Sets the value of this future from the value of a Response object.
105
- * @param {webdriver.Response} response The webdriver.Response to set the value
106
- * from.
107
- */
108
- webdriver.Future.prototype.setValueFromResponse = function(response) {
109
- this.setValue(response.value);
114
+ if (this.isSet()) {
115
+ return;
116
+ }
117
+ if (value instanceof webdriver.Future) {
118
+ if (value.isSet()) {
119
+ this.value_ = value.getValue();
120
+ } else {
121
+ value.linkedFutures_.push(this);
122
+ }
123
+ } else {
124
+ this.value_ = value;
125
+ goog.array.forEach(this.linkedFutures_, function(future) {
126
+ future.setValue(value);
127
+ });
128
+ }
110
129
  };
111
130
 
112
131