Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit 56daa54

Browse files
chirayukjuliemr
authored andcommitted
fix(clientsidescripts): convert non-Error exceptions to Errors
If any functions called by clientSideScripts throws a an exception that doesn't inherit from `Error`, the stack trace is completely unhelpful and the message is just "unknown error."  This commit wraps such errors into `Error` instances so that we have meaningful stack traces and the correct exception message.  (e.g. This is the common case when running dart2js code.  This commit gives us the Dart stack trace and exception message.) In addition, I've pushed the construction of the string to install into the browser into clientsidescripts.js.
1 parent 9e5d9e4 commit 56daa54

File tree

2 files changed

+56
-36
lines changed

2 files changed

+56
-36
lines changed

lib/clientsidescripts.js

+51-23
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
/**
22
* All scripts to be run on the client via executeAsyncScript or
3-
* executeScript should be put here. These scripts are transmitted over
4-
* the wire using their toString representation, and cannot reference
5-
* external variables. They can, however use the array passed in to
6-
* arguments.
3+
* executeScript should be put here.
4+
*
5+
* NOTE: These scripts are transmitted over the wire as JavaScript text
6+
* constructed using their toString representation, and *cannot*
7+
* reference external variables.
78
*
89
* Some implementations seem to have issues with // comments, so use star-style
9-
* inside scripts.
10+
* inside scripts. (TODO: add issue number / example implementations
11+
* that caused the switch to avoid the // comments.)
1012
*/
1113

12-
// jshint browser: true
13-
// jshint shadow: true
14-
/* global angular */
15-
var clientSideScripts = exports;
14+
// jshint browser: true
15+
// jshint shadow: true
16+
/* global angular */
17+
var functions = {};
1618

1719
/**
1820
* Wait until Angular has finished rendering and has
@@ -23,7 +25,7 @@ var clientSideScripts = exports;
2325
* @param {string} selector The selector housing an ng-app
2426
* @param {function} callback callback
2527
*/
26-
clientSideScripts.waitForAngular = function(selector, callback) {
28+
functions.waitForAngular = function(selector, callback) {
2729
var el = document.querySelector(selector);
2830
try {
2931
angular.element(el).injector().get('$browser').
@@ -42,7 +44,7 @@ clientSideScripts.waitForAngular = function(selector, callback) {
4244
*
4345
* @return {Array.<Element>} The elements containing the binding.
4446
*/
45-
clientSideScripts.findBindings = function(binding, exactMatch, using) {
47+
functions.findBindings = function(binding, exactMatch, using) {
4648
using = using || document;
4749
var bindings = using.getElementsByClassName('ng-binding');
4850
var matches = [];
@@ -78,7 +80,7 @@ clientSideScripts.findBindings = function(binding, exactMatch, using) {
7880
* @return {Array.<Element>} The row of the repeater, or an array of elements
7981
* in the first row in the case of ng-repeat-start.
8082
*/
81-
clientSideScripts.findRepeaterRows = function(repeater, index, using) {
83+
functions.findRepeaterRows = function(repeater, index, using) {
8284
using = using || document;
8385

8486
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
@@ -126,7 +128,7 @@ clientSideScripts.findBindings = function(binding, exactMatch, using) {
126128
*
127129
* @return {Array.<Element>} All rows of the repeater.
128130
*/
129-
clientSideScripts.findAllRepeaterRows = function(repeater, using) {
131+
functions.findAllRepeaterRows = function(repeater, using) {
130132
using = using || document;
131133

132134
var rows = [];
@@ -171,7 +173,7 @@ clientSideScripts.findBindings = function(binding, exactMatch, using) {
171173
*
172174
* @return {Array.<Element>} The element in an array.
173175
*/
174-
clientSideScripts.findRepeaterElement = function(repeater, index, binding, using) {
176+
functions.findRepeaterElement = function(repeater, index, binding, using) {
175177
var matches = [];
176178
using = using || document;
177179

@@ -254,7 +256,7 @@ clientSideScripts.findRepeaterElement = function(repeater, index, binding, using
254256
*
255257
* @return {Array.<Element>} The elements in the column.
256258
*/
257-
clientSideScripts.findRepeaterColumn = function(repeater, binding, using) {
259+
functions.findRepeaterColumn = function(repeater, binding, using) {
258260
var matches = [];
259261
using = using || document;
260262

@@ -334,7 +336,7 @@ clientSideScripts.findRepeaterColumn = function(repeater, binding, using) {
334336
*
335337
* @return {Array.<Element>} The matching elements.
336338
*/
337-
clientSideScripts.findByModel = function(model, using) {
339+
functions.findByModel = function(model, using) {
338340
using = using || document;
339341
var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
340342
for (var p = 0; p < prefixes.length; ++p) {
@@ -354,7 +356,7 @@ clientSideScripts.findByModel = function(model, using) {
354356
*
355357
* @return {Array.<Element>} The matching elements.
356358
*/
357-
clientSideScripts.findByButtonText = function(searchText, using) {
359+
functions.findByButtonText = function(searchText, using) {
358360
using = using || document;
359361
var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]');
360362
var matches = [];
@@ -382,7 +384,7 @@ clientSideScripts.findByButtonText = function(searchText, using) {
382384
*
383385
* @return {Array.<Element>} The matching elements.
384386
*/
385-
clientSideScripts.findByPartialButtonText = function(searchText, using) {
387+
functions.findByPartialButtonText = function(searchText, using) {
386388
using = using || document;
387389
var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]');
388390
var matches = [];
@@ -411,7 +413,7 @@ clientSideScripts.findByPartialButtonText = function(searchText, using) {
411413
*
412414
* @return {Array.<Element>} An array of matching elements.
413415
*/
414-
clientSideScripts.findByCssContainingText = function(cssSelector, searchText, using) {
416+
functions.findByCssContainingText = function(cssSelector, searchText, using) {
415417
var using = using || document;
416418
var elements = using.querySelectorAll(cssSelector);
417419
var matches = [];
@@ -434,7 +436,7 @@ clientSideScripts.findByCssContainingText = function(cssSelector, searchText, us
434436
* @param {number} attempts Number of times to retry.
435437
* @param {function} asyncCallback callback
436438
*/
437-
clientSideScripts.testForAngular = function(attempts, asyncCallback) {
439+
functions.testForAngular = function(attempts, asyncCallback) {
438440
var callback = function(args) {
439441
setTimeout(function() {
440442
asyncCallback(args);
@@ -468,7 +470,7 @@ clientSideScripts.testForAngular = function(attempts, asyncCallback) {
468470
*
469471
* @return {?Object} The result of the evaluation.
470472
*/
471-
clientSideScripts.evaluate = function(element, expression) {
473+
functions.evaluate = function(element, expression) {
472474

473475
return angular.element(element).scope().$eval(expression);
474476
};
@@ -478,7 +480,7 @@ clientSideScripts.evaluate = function(element, expression) {
478480
*
479481
* @param {string} selector The selector housing an ng-app
480482
*/
481-
clientSideScripts.getLocationAbsUrl = function(selector) {
483+
functions.getLocationAbsUrl = function(selector) {
482484
var el = document.querySelector(selector);
483485
return angular.element(el).injector().get('$location').absUrl();
484486
};
@@ -490,7 +492,7 @@ clientSideScripts.getLocationAbsUrl = function(selector) {
490492
* @param {string} url In page URL using the same syntax as $location.url(),
491493
* /path?search=a&b=c#hash
492494
*/
493-
clientSideScripts.setLocation = function(selector, url) {
495+
functions.setLocation = function(selector, url) {
494496
var el = document.querySelector(selector);
495497
var $injector = angular.element(el).injector();
496498
var $location = $injector.get('$location');
@@ -501,3 +503,29 @@ clientSideScripts.setLocation = function(selector, url) {
501503
$rootScope.$digest();
502504
}
503505
};
506+
507+
/* Publish all the functions as strings to pass to WebDriver's
508+
* exec[Async]Script. In addition, also include a script that will
509+
* install all the functions on window (for debugging.)
510+
*
511+
* We also wrap any exceptions thrown by a clientSideScripts function
512+
* that is not an instance of the Error type into an Error type. If we
513+
* don't do so, then the resulting stack trace is completely unhelpful
514+
* and the exception message is just "unknown error." These types of
515+
* exceptins are the common case for dart2js code. This wrapping gives
516+
* us the Dart stack trace and exception message.
517+
*/
518+
var util = require('util');
519+
var scriptsList = [];
520+
var scriptFmt = (
521+
'try { return (%s).apply(this, arguments); }\n' +
522+
'catch(e) { throw (e instanceof Error) ? e : new Error(e); }');
523+
for (var fnName in functions) {
524+
if (functions.hasOwnProperty(fnName)) {
525+
exports[fnName] = util.format(scriptFmt, functions[fnName]);
526+
scriptsList.push(util.format('%s: %s', fnName, functions[fnName]));
527+
}
528+
}
529+
530+
exports.installInBrowser = (util.format(
531+
'window.clientSideScripts = {%s};', scriptsList.join(', ')));

lib/protractor.js

+5-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ var url = require('url');
22
var webdriver = require('selenium-webdriver');
33

44
var clientSideScripts = require('./clientsidescripts.js');
5+
6+
57
var ProtractorBy = require('./locators.js').ProtractorBy;
68

79
var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';
@@ -1037,19 +1039,9 @@ Protractor.prototype.getLocationAbsUrl = function() {
10371039
*/
10381040
Protractor.prototype.debugger = function() {
10391041
// jshint debug: true
1040-
var clientSideScriptsList = [];
1041-
for (var script in clientSideScripts) {
1042-
clientSideScriptsList.push(
1043-
script + ': ' + clientSideScripts[script].toString());
1044-
}
1045-
1046-
this.driver.executeScript(
1047-
'window.clientSideScripts = {' + clientSideScriptsList.join(', ') + '}');
1048-
1049-
var flow = webdriver.promise.controlFlow();
1050-
flow.execute(function() {
1051-
debugger;
1052-
}, 'add breakpoint to control flow');
1042+
this.driver.executeScript(clientSideScripts.installInBrowser);
1043+
webdriver.promise.controlFlow().execute(function() { debugger; },
1044+
'add breakpoint to control flow');
10531045
};
10541046

10551047
/**

0 commit comments

Comments
 (0)