123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939 |
- /**
- * All scripts to be run on the client via executeAsyncScript or
- * executeScript should be put here.
- *
- * NOTE: These scripts are transmitted over the wire as JavaScript text
- * constructed using their toString representation, and *cannot*
- * reference external variables.
- *
- * Some implementations seem to have issues with // comments, so use star-style
- * inside scripts. (TODO: add issue number / example implementations
- * that caused the switch to avoid the // comments.)
- */
- // jshint browser: true
- // jshint shadow: true
- /* global angular */
- var functions = {};
- ///////////////////////////////////////////////////////
- //// ////
- //// HELPERS ////
- //// ////
- ///////////////////////////////////////////////////////
- /* Wraps a function up into a string with its helper functions so that it can
- * call those helper functions client side
- *
- * @param {function} fun The function to wrap up with its helpers
- * @param {...function} The helper functions. Each function must be named
- *
- * @return {string} The string which, when executed, will invoke fun in such a
- * way that it has access to its helper functions
- */
- function wrapWithHelpers(fun) {
- var helpers = Array.prototype.slice.call(arguments, 1);
- if (!helpers.length) {
- return fun;
- }
- var FunClass = Function; // Get the linter to allow this eval
- return new FunClass(
- helpers.join(';') + String.fromCharCode(59) +
- ' return (' + fun.toString() + ').apply(this, arguments);');
- }
- /* Tests if an ngRepeat matches a repeater
- *
- * @param {string} ngRepeat The ngRepeat to test
- * @param {string} repeater The repeater to test against
- * @param {boolean} exact If the ngRepeat expression needs to match the whole
- * repeater (not counting any `track by ...` modifier) or if it just needs to
- * match a substring
- * @return {boolean} If the ngRepeat matched the repeater
- */
- function repeaterMatch(ngRepeat, repeater, exact) {
- if (exact) {
- return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
- split('=')[0].trim() == repeater;
- } else {
- return ngRepeat.indexOf(repeater) != -1;
- }
- }
- /* Tries to find $$testability and possibly $injector for an ng1 app
- *
- * By default, doesn't care about $injector if it finds $$testability. However,
- * these priorities can be reversed.
- *
- * @param {string=} selector The selector for the element with the injector. If
- * falsy, tries a variety of methods to find an injector
- * @param {boolean=} injectorPlease Prioritize finding an injector
- * @return {$$testability?: Testability, $injector?: Injector} Returns whatever
- * ng1 app hooks it finds
- */
- function getNg1Hooks(selector, injectorPlease) {
- function tryEl(el) {
- try {
- if (!injectorPlease && angular.getTestability) {
- var $$testability = angular.getTestability(el);
- if ($$testability) {
- return {$$testability: $$testability};
- }
- } else {
- var $injector = angular.element(el).injector();
- if ($injector) {
- return {$injector: $injector};
- }
- }
- } catch(err) {}
- }
- function trySelector(selector) {
- var els = document.querySelectorAll(selector);
- for (var i = 0; i < els.length; i++) {
- var elHooks = tryEl(els[i]);
- if (elHooks) {
- return elHooks;
- }
- }
- }
- if (selector) {
- return trySelector(selector);
- } else if (window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__) {
- var $injector = window.__TESTABILITY__NG1_APP_ROOT_INJECTOR__;
- var $$testability = null;
- try {
- $$testability = $injector.get('$$testability');
- } catch (e) {}
- return {$injector: $injector, $$testability: $$testability};
- } else {
- return tryEl(document.body) ||
- trySelector('[ng-app]') || trySelector('[ng\\:app]') ||
- trySelector('[ng-controller]') || trySelector('[ng\\:controller]');
- }
- }
- ///////////////////////////////////////////////////////
- //// ////
- //// SCRIPTS ////
- //// ////
- ///////////////////////////////////////////////////////
- /**
- * Wait until Angular has finished rendering and has
- * no outstanding $http calls before continuing. The specific Angular app
- * is determined by the rootSelector.
- *
- * Asynchronous.
- *
- * @param {string} rootSelector The selector housing an ng-app
- * @param {function(string)} callback callback. If a failure occurs, it will
- * be passed as a parameter.
- */
- functions.waitForAngular = function(rootSelector, callback) {
- try {
- // Wait for both angular1 testability and angular2 testability.
- var testCallback = callback;
- // Wait for angular1 testability first and run waitForAngular2 as a callback
- var waitForAngular1 = function(callback) {
- if (window.angular) {
- var hooks = getNg1Hooks(rootSelector);
- if (!hooks){
- callback(); // not an angular1 app
- }
- else{
- if (hooks.$$testability) {
- hooks.$$testability.whenStable(callback);
- } else if (hooks.$injector) {
- hooks.$injector.get('$browser')
- .notifyWhenNoOutstandingRequests(callback);
- } else if (!rootSelector) {
- throw new Error(
- 'Could not automatically find injector on page: "' +
- window.location.toString() + '". Consider using config.rootEl');
- } else {
- throw new Error(
- 'root element (' + rootSelector + ') has no injector.' +
- ' this may mean it is not inside ng-app.');
- }
- }
- }
- else {callback();} // not an angular1 app
- };
- // Wait for Angular2 testability and then run test callback
- var waitForAngular2 = function() {
- if (window.getAngularTestability) {
- if (rootSelector) {
- var testability = null;
- var el = document.querySelector(rootSelector);
- try{
- testability = window.getAngularTestability(el);
- }
- catch(e){}
- if (testability) {
- testability.whenStable(testCallback);
- return;
- }
- }
- // Didn't specify root element or testability could not be found
- // by rootSelector. This may happen in a hybrid app, which could have
- // more than one root.
- var testabilities = window.getAllAngularTestabilities();
- var count = testabilities.length;
- // No angular2 testability, this happens when
- // going to a hybrid page and going back to a pure angular1 page
- if (count === 0) {
- testCallback();
- return;
- }
- var decrement = function() {
- count--;
- if (count === 0) {
- testCallback();
- }
- };
- testabilities.forEach(function(testability) {
- testability.whenStable(decrement);
- });
- }
- else {testCallback();} // not an angular2 app
- };
- if (!(window.angular) && !(window.getAngularTestability)) {
- // no testability hook
- throw new Error(
- 'both angularJS testability and angular testability are undefined.' +
- ' This could be either ' +
- 'because this is a non-angular page or because your test involves ' +
- 'client-side navigation, which can interfere with Protractor\'s ' +
- 'bootstrapping. See http://git.io/v4gXM for details');
- } else {waitForAngular1(waitForAngular2);} // Wait for angular1 and angular2
- // Testability hooks sequentially
- } catch (err) {
- callback(err.message);
- }
- };
- /**
- * Find a list of elements in the page by their angular binding.
- *
- * @param {string} binding The binding, e.g. {{cat.name}}.
- * @param {boolean} exactMatch Whether the binding needs to be matched exactly
- * @param {Element} using The scope of the search.
- * @param {string} rootSelector The selector to use for the root app element.
- *
- * @return {Array.<Element>} The elements containing the binding.
- */
- functions.findBindings = function(binding, exactMatch, using, rootSelector) {
- using = using || document;
- if (angular.getTestability) {
- return getNg1Hooks(rootSelector).$$testability.
- findBindings(using, binding, exactMatch);
- }
- var bindings = using.getElementsByClassName('ng-binding');
- var matches = [];
- for (var i = 0; i < bindings.length; ++i) {
- var dataBinding = angular.element(bindings[i]).data('$binding');
- if (dataBinding) {
- var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;
- if (exactMatch) {
- var matcher = new RegExp('({|\\s|^|\\|)' +
- /* See http://stackoverflow.com/q/3561711 */
- binding.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') +
- '(}|\\s|$|\\|)');
- if (matcher.test(bindingName)) {
- matches.push(bindings[i]);
- }
- } else {
- if (bindingName.indexOf(binding) != -1) {
- matches.push(bindings[i]);
- }
- }
- }
- }
- return matches; /* Return the whole array for webdriver.findElements. */
- };
- /**
- * Find an array of elements matching a row within an ng-repeat.
- * Always returns an array of only one element for plain old ng-repeat.
- * Returns an array of all the elements in one segment for ng-repeat-start.
- *
- * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
- * @param {boolean} exact Whether the repeater needs to be matched exactly
- * @param {number} index The row index.
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} The row of the repeater, or an array of elements
- * in the first row in the case of ng-repeat-start.
- */
- function findRepeaterRows(repeater, exact, index, using) {
- using = using || document;
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- var rows = [];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- rows.push(repeatElems[i]);
- }
- }
- }
- /* multiRows is an array of arrays, where each inner array contains
- one row of elements. */
- var multiRows = [];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat-start';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- var elem = repeatElems[i];
- var row = [];
- while (elem.nodeType != 8 ||
- !repeaterMatch(elem.nodeValue, repeater)) {
- if (elem.nodeType == 1) {
- row.push(elem);
- }
- elem = elem.nextSibling;
- }
- multiRows.push(row);
- }
- }
- }
- var row = rows[index] || [], multiRow = multiRows[index] || [];
- return [].concat(row, multiRow);
- }
- functions.findRepeaterRows = wrapWithHelpers(findRepeaterRows, repeaterMatch);
- /**
- * Find all rows of an ng-repeat.
- *
- * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
- * @param {boolean} exact Whether the repeater needs to be matched exactly
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} All rows of the repeater.
- */
- function findAllRepeaterRows(repeater, exact, using) {
- using = using || document;
- var rows = [];
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- rows.push(repeatElems[i]);
- }
- }
- }
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat-start';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- var elem = repeatElems[i];
- while (elem.nodeType != 8 ||
- !repeaterMatch(elem.nodeValue, repeater)) {
- if (elem.nodeType == 1) {
- rows.push(elem);
- }
- elem = elem.nextSibling;
- }
- }
- }
- }
- return rows;
- }
- functions.findAllRepeaterRows = wrapWithHelpers(findAllRepeaterRows, repeaterMatch);
- /**
- * Find an element within an ng-repeat by its row and column.
- *
- * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
- * @param {boolean} exact Whether the repeater needs to be matched exactly
- * @param {number} index The row index.
- * @param {string} binding The column binding, e.g. '{{cat.name}}'.
- * @param {Element} using The scope of the search.
- * @param {string} rootSelector The selector to use for the root app element.
- *
- * @return {Array.<Element>} The element in an array.
- */
- function findRepeaterElement(repeater, exact, index, binding, using, rootSelector) {
- var matches = [];
- using = using || document;
- var rows = [];
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- rows.push(repeatElems[i]);
- }
- }
- }
- /* multiRows is an array of arrays, where each inner array contains
- one row of elements. */
- var multiRows = [];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat-start';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- var elem = repeatElems[i];
- var row = [];
- while (elem.nodeType != 8 || (elem.nodeValue &&
- !repeaterMatch(elem.nodeValue, repeater))) {
- if (elem.nodeType == 1) {
- row.push(elem);
- }
- elem = elem.nextSibling;
- }
- multiRows.push(row);
- }
- }
- }
- var row = rows[index];
- var multiRow = multiRows[index];
- var bindings = [];
- if (row) {
- if (angular.getTestability) {
- matches.push.apply(
- matches,
- getNg1Hooks(rootSelector).$$testability.findBindings(row, binding));
- } else {
- if (row.className.indexOf('ng-binding') != -1) {
- bindings.push(row);
- }
- var childBindings = row.getElementsByClassName('ng-binding');
- for (var i = 0; i < childBindings.length; ++i) {
- bindings.push(childBindings[i]);
- }
- }
- }
- if (multiRow) {
- for (var i = 0; i < multiRow.length; ++i) {
- var rowElem = multiRow[i];
- if (angular.getTestability) {
- matches.push.apply(
- matches,
- getNg1Hooks(rootSelector).$$testability.findBindings(rowElem,
- binding));
- } else {
- if (rowElem.className.indexOf('ng-binding') != -1) {
- bindings.push(rowElem);
- }
- var childBindings = rowElem.getElementsByClassName('ng-binding');
- for (var j = 0; j < childBindings.length; ++j) {
- bindings.push(childBindings[j]);
- }
- }
- }
- }
- for (var i = 0; i < bindings.length; ++i) {
- var dataBinding = angular.element(bindings[i]).data('$binding');
- if (dataBinding) {
- var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;
- if (bindingName.indexOf(binding) != -1) {
- matches.push(bindings[i]);
- }
- }
- }
- return matches;
- }
- functions.findRepeaterElement =
- wrapWithHelpers(findRepeaterElement, repeaterMatch, getNg1Hooks);
- /**
- * Find the elements in a column of an ng-repeat.
- *
- * @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
- * @param {boolean} exact Whether the repeater needs to be matched exactly
- * @param {string} binding The column binding, e.g. '{{cat.name}}'.
- * @param {Element} using The scope of the search.
- * @param {string} rootSelector The selector to use for the root app element.
- *
- * @return {Array.<Element>} The elements in the column.
- */
- function findRepeaterColumn(repeater, exact, binding, using, rootSelector) {
- var matches = [];
- using = using || document;
- var rows = [];
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- rows.push(repeatElems[i]);
- }
- }
- }
- /* multiRows is an array of arrays, where each inner array contains
- one row of elements. */
- var multiRows = [];
- for (var p = 0; p < prefixes.length; ++p) {
- var attr = prefixes[p] + 'repeat-start';
- var repeatElems = using.querySelectorAll('[' + attr + ']');
- attr = attr.replace(/\\/g, '');
- for (var i = 0; i < repeatElems.length; ++i) {
- if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
- var elem = repeatElems[i];
- var row = [];
- while (elem.nodeType != 8 || (elem.nodeValue &&
- !repeaterMatch(elem.nodeValue, repeater))) {
- if (elem.nodeType == 1) {
- row.push(elem);
- }
- elem = elem.nextSibling;
- }
- multiRows.push(row);
- }
- }
- }
- var bindings = [];
- for (var i = 0; i < rows.length; ++i) {
- if (angular.getTestability) {
- matches.push.apply(
- matches,
- getNg1Hooks(rootSelector).$$testability.findBindings(rows[i],
- binding));
- } else {
- if (rows[i].className.indexOf('ng-binding') != -1) {
- bindings.push(rows[i]);
- }
- var childBindings = rows[i].getElementsByClassName('ng-binding');
- for (var k = 0; k < childBindings.length; ++k) {
- bindings.push(childBindings[k]);
- }
- }
- }
- for (var i = 0; i < multiRows.length; ++i) {
- for (var j = 0; j < multiRows[i].length; ++j) {
- if (angular.getTestability) {
- matches.push.apply(
- matches,
- getNg1Hooks(rootSelector).$$testability.findBindings(
- multiRows[i][j], binding));
- } else {
- var elem = multiRows[i][j];
- if (elem.className.indexOf('ng-binding') != -1) {
- bindings.push(elem);
- }
- var childBindings = elem.getElementsByClassName('ng-binding');
- for (var k = 0; k < childBindings.length; ++k) {
- bindings.push(childBindings[k]);
- }
- }
- }
- }
- for (var j = 0; j < bindings.length; ++j) {
- var dataBinding = angular.element(bindings[j]).data('$binding');
- if (dataBinding) {
- var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding;
- if (bindingName.indexOf(binding) != -1) {
- matches.push(bindings[j]);
- }
- }
- }
- return matches;
- }
- functions.findRepeaterColumn =
- wrapWithHelpers(findRepeaterColumn, repeaterMatch, getNg1Hooks);
- /**
- * Find elements by model name.
- *
- * @param {string} model The model name.
- * @param {Element} using The scope of the search.
- * @param {string} rootSelector The selector to use for the root app element.
- *
- * @return {Array.<Element>} The matching elements.
- */
- functions.findByModel = function(model, using, rootSelector) {
- using = using || document;
- if (angular.getTestability) {
- return getNg1Hooks(rootSelector).$$testability.
- findModels(using, model, true);
- }
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- for (var p = 0; p < prefixes.length; ++p) {
- var selector = '[' + prefixes[p] + 'model="' + model + '"]';
- var elements = using.querySelectorAll(selector);
- if (elements.length) {
- return elements;
- }
- }
- };
- /**
- * Find elements by options.
- *
- * @param {string} optionsDescriptor The descriptor for the option
- * (i.e. fruit for fruit in fruits).
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} The matching elements.
- */
- functions.findByOptions = function(optionsDescriptor, using) {
- using = using || document;
- var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
- for (var p = 0; p < prefixes.length; ++p) {
- var selector = '[' + prefixes[p] + 'options="' + optionsDescriptor + '"] option';
- var elements = using.querySelectorAll(selector);
- if (elements.length) {
- return elements;
- }
- }
- };
- /**
- * Find buttons by textual content.
- *
- * @param {string} searchText The exact text to match.
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} The matching elements.
- */
- functions.findByButtonText = function(searchText, using) {
- using = using || document;
- var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]');
- var matches = [];
- for (var i = 0; i < elements.length; ++i) {
- var element = elements[i];
- var elementText;
- if (element.tagName.toLowerCase() == 'button') {
- elementText = element.textContent || element.innerText || '';
- } else {
- elementText = element.value;
- }
- if (elementText.trim() === searchText) {
- matches.push(element);
- }
- }
- return matches;
- };
- /**
- * Find buttons by textual content.
- *
- * @param {string} searchText The exact text to match.
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} The matching elements.
- */
- functions.findByPartialButtonText = function(searchText, using) {
- using = using || document;
- var elements = using.querySelectorAll('button, input[type="button"], input[type="submit"]');
- var matches = [];
- for (var i = 0; i < elements.length; ++i) {
- var element = elements[i];
- var elementText;
- if (element.tagName.toLowerCase() == 'button') {
- elementText = element.textContent || element.innerText || '';
- } else {
- elementText = element.value;
- }
- if (elementText.indexOf(searchText) > -1) {
- matches.push(element);
- }
- }
- return matches;
- };
- /**
- * Find elements by css selector and textual content.
- *
- * @param {string} cssSelector The css selector to match.
- * @param {string} searchText The exact text to match or a serialized regex.
- * @param {Element} using The scope of the search.
- *
- * @return {Array.<Element>} An array of matching elements.
- */
- functions.findByCssContainingText = function(cssSelector, searchText, using) {
- using = using || document;
- if (searchText.indexOf('__REGEXP__') === 0) {
- var match = searchText.split('__REGEXP__')[1].match(/\/(.*)\/(.*)?/);
- searchText = new RegExp(match[1], match[2] || '');
- }
- var elements = using.querySelectorAll(cssSelector);
- var matches = [];
- for (var i = 0; i < elements.length; ++i) {
- var element = elements[i];
- var elementText = element.textContent || element.innerText || '';
- var elementMatches = searchText instanceof RegExp ?
- searchText.test(elementText) :
- elementText.indexOf(searchText) > -1;
- if (elementMatches) {
- matches.push(element);
- }
- }
- return matches;
- };
- /**
- * Tests whether the angular global variable is present on a page. Retries
- * in case the page is just loading slowly.
- *
- * Asynchronous.
- *
- * @param {number} attempts Number of times to retry.
- * @param {boolean} ng12Hybrid Flag set if app is a hybrid of angular 1 and 2
- * @param {function({version: ?number, message: ?string})} asyncCallback callback
- *
- */
- functions.testForAngular = function(attempts, ng12Hybrid, asyncCallback) {
- var callback = function(args) {
- setTimeout(function() {
- asyncCallback(args);
- }, 0);
- };
- var definitelyNg1 = !!ng12Hybrid;
- var definitelyNg2OrNewer = false;
- var check = function(n) {
- try {
- /* Figure out which version of angular we're waiting on */
- if (!definitelyNg1 && !definitelyNg2OrNewer) {
- if (window.angular && !(window.angular.version && window.angular.version.major > 1)) {
- definitelyNg1 = true;
- } else if (window.getAllAngularTestabilities) {
- definitelyNg2OrNewer = true;
- }
- }
- /* See if our version of angular is ready */
- if (definitelyNg1) {
- if (window.angular && window.angular.resumeBootstrap) {
- return callback({ver: 1});
- }
- } else if (definitelyNg2OrNewer) {
- if (true /* ng2 has no resumeBootstrap() */) {
- return callback({ver: 2});
- }
- }
- /* Try again (or fail) */
- if (n < 1) {
- if (definitelyNg1 && window.angular) {
- callback({message: 'angular never provided resumeBootstrap'});
- } else if (ng12Hybrid && !window.angular) {
- callback({message: 'angular 1 never loaded' +
- window.getAllAngularTestabilities ? ' (are you sure this app ' +
- 'uses ngUpgrade? Try un-setting ng12Hybrid)' : ''});
- } else {
- callback({message: 'retries looking for angular exceeded'});
- }
- } else {
- window.setTimeout(function() {check(n - 1);}, 1000);
- }
- } catch (e) {
- callback({message: e});
- }
- };
- check(attempts);
- };
- /**
- * Evalute an Angular expression in the context of a given element.
- *
- * @param {Element} element The element in whose scope to evaluate.
- * @param {string} expression The expression to evaluate.
- *
- * @return {?Object} The result of the evaluation.
- */
- functions.evaluate = function(element, expression) {
- return angular.element(element).scope().$eval(expression);
- };
- functions.allowAnimations = function(element, value) {
- var ngElement = angular.element(element);
- if (ngElement.allowAnimations) {
- // AngularDart: $testability API.
- return ngElement.allowAnimations(value);
- } else {
- // AngularJS
- var enabledFn = ngElement.injector().get('$animate').enabled;
- return (value == null) ? enabledFn() : enabledFn(value);
- }
- };
- /**
- * Return the current url using $location.absUrl().
- *
- * @param {string} selector The selector housing an ng-app
- */
- functions.getLocationAbsUrl = function(selector) {
- var hooks = getNg1Hooks(selector);
- if (angular.getTestability) {
- return hooks.$$testability.getLocation();
- }
- return hooks.$injector.get('$location').absUrl();
- };
- /**
- * Browse to another page using in-page navigation.
- *
- * @param {string} selector The selector housing an ng-app
- * @param {string} url In page URL using the same syntax as $location.url(),
- * /path?search=a&b=c#hash
- */
- functions.setLocation = function(selector, url) {
- var hooks = getNg1Hooks(selector);
- if (angular.getTestability) {
- return hooks.$$testability.setLocation(url);
- }
- var $injector = hooks.$injector;
- var $location = $injector.get('$location');
- var $rootScope = $injector.get('$rootScope');
- if (url !== $location.url()) {
- $location.url(url);
- $rootScope.$digest();
- }
- };
- /**
- * Retrieve the pending $http requests.
- *
- * @param {string} selector The selector housing an ng-app
- * @return {!Array<!Object>} An array of pending http requests.
- */
- functions.getPendingHttpRequests = function(selector) {
- var hooks = getNg1Hooks(selector, true);
- var $http = hooks.$injector.get('$http');
- return $http.pendingRequests;
- };
- ['waitForAngular', 'findBindings', 'findByModel', 'getLocationAbsUrl',
- 'setLocation', 'getPendingHttpRequests'].forEach(function(funName) {
- functions[funName] = wrapWithHelpers(functions[funName], getNg1Hooks);
- });
- /* Publish all the functions as strings to pass to WebDriver's
- * exec[Async]Script. In addition, also include a script that will
- * install all the functions on window (for debugging.)
- *
- * We also wrap any exceptions thrown by a clientSideScripts function
- * that is not an instance of the Error type into an Error type. If we
- * don't do so, then the resulting stack trace is completely unhelpful
- * and the exception message is just "unknown error." These types of
- * exceptions are the common case for dart2js code. This wrapping gives
- * us the Dart stack trace and exception message.
- */
- var util = require('util');
- var scriptsList = [];
- var scriptFmt = (
- 'try { return (%s).apply(this, arguments); }\n' +
- 'catch(e) { throw (e instanceof Error) ? e : new Error(e); }');
- for (var fnName in functions) {
- if (functions.hasOwnProperty(fnName)) {
- exports[fnName] = util.format(scriptFmt, functions[fnName]);
- scriptsList.push(util.format('%s: %s', fnName, functions[fnName]));
- }
- }
- exports.installInBrowser = (util.format(
- 'window.clientSideScripts = {%s};', scriptsList.join(', ')));
- /**
- * Automatically installed by Protractor when a page is loaded, this
- * default mock module decorates $timeout to keep track of any
- * outstanding timeouts.
- *
- * @param {boolean} trackOutstandingTimeouts
- */
- exports.protractorBaseModuleFn = function(trackOutstandingTimeouts) {
- var ngMod = angular.module('protractorBaseModule_', []).config([
- '$compileProvider',
- function($compileProvider) {
- if ($compileProvider.debugInfoEnabled) {
- $compileProvider.debugInfoEnabled(true);
- }
- }
- ]);
- if (trackOutstandingTimeouts) {
- ngMod.config([
- '$provide',
- function ($provide) {
- $provide.decorator('$timeout', [
- '$delegate',
- function ($delegate) {
- var $timeout = $delegate;
- var taskId = 0;
- if (!window['NG_PENDING_TIMEOUTS']) {
- window['NG_PENDING_TIMEOUTS'] = {};
- }
- var extendedTimeout= function() {
- var args = Array.prototype.slice.call(arguments);
- if (typeof(args[0]) !== 'function') {
- return $timeout.apply(null, args);
- }
- taskId++;
- var fn = args[0];
- window['NG_PENDING_TIMEOUTS'][taskId] =
- fn.toString();
- var wrappedFn = (function(taskId_) {
- return function() {
- delete window['NG_PENDING_TIMEOUTS'][taskId_];
- return fn.apply(null, arguments);
- };
- })(taskId);
- args[0] = wrappedFn;
- var promise = $timeout.apply(null, args);
- promise.ptorTaskId_ = taskId;
- return promise;
- };
- extendedTimeout.cancel = function() {
- var taskId_ = arguments[0] && arguments[0].ptorTaskId_;
- if (taskId_) {
- delete window['NG_PENDING_TIMEOUTS'][taskId_];
- }
- return $timeout.cancel.apply($timeout, arguments);
- };
- return extendedTimeout;
- }
- ]);
- }
- ]);
- }
- };
|