selenium-webdriver 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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