locators.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. const selenium_webdriver_1 = require("selenium-webdriver");
  4. let clientSideScripts = require('./clientsidescripts');
  5. // Explicitly define webdriver.By.
  6. // We do this because we want to inherit the static methods of webdriver.By, as opposed to
  7. // inheriting from the webdriver.By class itself, which is actually analogous to ProtractorLocator.
  8. class WebdriverBy {
  9. constructor() {
  10. this.className = selenium_webdriver_1.By.className;
  11. this.css = selenium_webdriver_1.By.css;
  12. this.id = selenium_webdriver_1.By.id;
  13. this.linkText = selenium_webdriver_1.By.linkText;
  14. this.js = selenium_webdriver_1.By.js;
  15. this.name = selenium_webdriver_1.By.name;
  16. this.partialLinkText = selenium_webdriver_1.By.partialLinkText;
  17. this.tagName = selenium_webdriver_1.By.tagName;
  18. this.xpath = selenium_webdriver_1.By.xpath;
  19. }
  20. }
  21. exports.WebdriverBy = WebdriverBy;
  22. function isProtractorLocator(x) {
  23. return x && (typeof x.findElementsOverride === 'function');
  24. }
  25. exports.isProtractorLocator = isProtractorLocator;
  26. /**
  27. * The Protractor Locators. These provide ways of finding elements in
  28. * Angular applications by binding, model, etc.
  29. *
  30. * @alias by
  31. * @extends {webdriver.By}
  32. */
  33. class ProtractorBy extends WebdriverBy {
  34. /**
  35. * Add a locator to this instance of ProtractorBy. This locator can then be
  36. * used with element(by.locatorName(args)).
  37. *
  38. * @view
  39. * <button ng-click="doAddition()">Go!</button>
  40. *
  41. * @example
  42. * // Add the custom locator.
  43. * by.addLocator('buttonTextSimple',
  44. * function(buttonText, opt_parentElement, opt_rootSelector) {
  45. * // This function will be serialized as a string and will execute in the
  46. * // browser. The first argument is the text for the button. The second
  47. * // argument is the parent element, if any.
  48. * var using = opt_parentElement || document,
  49. * buttons = using.querySelectorAll('button');
  50. *
  51. * // Return an array of buttons with the text.
  52. * return Array.prototype.filter.call(buttons, function(button) {
  53. * return button.textContent === buttonText;
  54. * });
  55. * });
  56. *
  57. * // Use the custom locator.
  58. * element(by.buttonTextSimple('Go!')).click();
  59. *
  60. * @alias by.addLocator(locatorName, functionOrScript)
  61. * @param {string} name The name of the new locator.
  62. * @param {Function|string} script A script to be run in the context of
  63. * the browser. This script will be passed an array of arguments
  64. * that contains any args passed into the locator followed by the
  65. * element scoping the search and the css selector for the root angular
  66. * element. It should return an array of elements.
  67. */
  68. addLocator(name, script) {
  69. this[name] = (...args) => {
  70. let locatorArguments = args;
  71. return {
  72. findElementsOverride: (driver, using, rootSelector) => {
  73. let findElementArguments = [script];
  74. for (let i = 0; i < locatorArguments.length; i++) {
  75. findElementArguments.push(locatorArguments[i]);
  76. }
  77. findElementArguments.push(using);
  78. findElementArguments.push(rootSelector);
  79. return driver.findElements(selenium_webdriver_1.By.js.apply(selenium_webdriver_1.By, findElementArguments));
  80. },
  81. toString: () => {
  82. return 'by.' + name + '("' + Array.prototype.join.call(locatorArguments, '", "') + '")';
  83. }
  84. };
  85. };
  86. }
  87. ;
  88. /**
  89. * Find an element by text binding. Does a partial match, so any elements
  90. * bound to variables containing the input string will be returned.
  91. *
  92. * Note: For AngularJS version 1.2, the interpolation brackets, (usually
  93. * {{}}), are optionally allowed in the binding description string. For
  94. * Angular version 1.3+, they are not allowed, and no elements will be found
  95. * if they are used.
  96. *
  97. * @view
  98. * <span>{{person.name}}</span>
  99. * <span ng-bind="person.email"></span>
  100. *
  101. * @example
  102. * var span1 = element(by.binding('person.name'));
  103. * expect(span1.getText()).toBe('Foo');
  104. *
  105. * var span2 = element(by.binding('person.email'));
  106. * expect(span2.getText()).toBe('foo@bar.com');
  107. *
  108. * // You can also use a substring for a partial match
  109. * var span1alt = element(by.binding('name'));
  110. * expect(span1alt.getText()).toBe('Foo');
  111. *
  112. * // This works for sites using Angular 1.2 but NOT 1.3
  113. * var deprecatedSyntax = element(by.binding('{{person.name}}'));
  114. *
  115. * @param {string} bindingDescriptor
  116. * @returns {ProtractorLocator} location strategy
  117. */
  118. binding(bindingDescriptor) {
  119. return {
  120. findElementsOverride: (driver, using, rootSelector) => {
  121. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findBindings, bindingDescriptor, false, using, rootSelector));
  122. },
  123. toString: () => {
  124. return 'by.binding("' + bindingDescriptor + '")';
  125. }
  126. };
  127. }
  128. ;
  129. /**
  130. * Find an element by exact binding.
  131. *
  132. * @view
  133. * <span>{{ person.name }}</span>
  134. * <span ng-bind="person-email"></span>
  135. * <span>{{person_phone|uppercase}}</span>
  136. *
  137. * @example
  138. * expect(element(by.exactBinding('person.name')).isPresent()).toBe(true);
  139. * expect(element(by.exactBinding('person-email')).isPresent()).toBe(true);
  140. * expect(element(by.exactBinding('person')).isPresent()).toBe(false);
  141. * expect(element(by.exactBinding('person_phone')).isPresent()).toBe(true);
  142. * expect(element(by.exactBinding('person_phone|uppercase')).isPresent()).toBe(true);
  143. * expect(element(by.exactBinding('phone')).isPresent()).toBe(false);
  144. *
  145. * @param {string} bindingDescriptor
  146. * @returns {ProtractorLocator} location strategy
  147. */
  148. exactBinding(bindingDescriptor) {
  149. return {
  150. findElementsOverride: (driver, using, rootSelector) => {
  151. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findBindings, bindingDescriptor, true, using, rootSelector));
  152. },
  153. toString: () => {
  154. return 'by.exactBinding("' + bindingDescriptor + '")';
  155. }
  156. };
  157. }
  158. ;
  159. /**
  160. * Find an element by ng-model expression.
  161. *
  162. * @alias by.model(modelName)
  163. * @view
  164. * <input type="text" ng-model="person.name">
  165. *
  166. * @example
  167. * var input = element(by.model('person.name'));
  168. * input.sendKeys('123');
  169. * expect(input.getAttribute('value')).toBe('Foo123');
  170. *
  171. * @param {string} model ng-model expression.
  172. * @returns {ProtractorLocator} location strategy
  173. */
  174. model(model) {
  175. return {
  176. findElementsOverride: (driver, using, rootSelector) => {
  177. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findByModel, model, using, rootSelector));
  178. },
  179. toString: () => {
  180. return 'by.model("' + model + '")';
  181. }
  182. };
  183. }
  184. ;
  185. /**
  186. * Find a button by text.
  187. *
  188. * @view
  189. * <button>Save</button>
  190. *
  191. * @example
  192. * element(by.buttonText('Save'));
  193. *
  194. * @param {string} searchText
  195. * @returns {ProtractorLocator} location strategy
  196. */
  197. buttonText(searchText) {
  198. return {
  199. findElementsOverride: (driver, using, rootSelector) => {
  200. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findByButtonText, searchText, using, rootSelector));
  201. },
  202. toString: () => {
  203. return 'by.buttonText("' + searchText + '")';
  204. }
  205. };
  206. }
  207. ;
  208. /**
  209. * Find a button by partial text.
  210. *
  211. * @view
  212. * <button>Save my file</button>
  213. *
  214. * @example
  215. * element(by.partialButtonText('Save'));
  216. *
  217. * @param {string} searchText
  218. * @returns {ProtractorLocator} location strategy
  219. */
  220. partialButtonText(searchText) {
  221. return {
  222. findElementsOverride: (driver, using, rootSelector) => {
  223. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findByPartialButtonText, searchText, using, rootSelector));
  224. },
  225. toString: () => {
  226. return 'by.partialButtonText("' + searchText + '")';
  227. }
  228. };
  229. }
  230. ;
  231. // Generate either by.repeater or by.exactRepeater
  232. byRepeaterInner(exact, repeatDescriptor) {
  233. let name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';
  234. return {
  235. findElementsOverride: (driver, using, rootSelector) => {
  236. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findAllRepeaterRows, repeatDescriptor, exact, using, rootSelector));
  237. },
  238. toString: () => {
  239. return name + '("' + repeatDescriptor + '")';
  240. },
  241. row: (index) => {
  242. return {
  243. findElementsOverride: (driver, using, rootSelector) => {
  244. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findRepeaterRows, repeatDescriptor, exact, index, using, rootSelector));
  245. },
  246. toString: () => {
  247. return name + '(' + repeatDescriptor + '").row("' + index + '")"';
  248. },
  249. column: (binding) => {
  250. return {
  251. findElementsOverride: (driver, using, rootSelector) => {
  252. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, binding, using, rootSelector));
  253. },
  254. toString: () => {
  255. return name + '("' + repeatDescriptor + '").row("' + index + '").column("' +
  256. binding + '")';
  257. }
  258. };
  259. }
  260. };
  261. },
  262. column: (binding) => {
  263. return {
  264. findElementsOverride: (driver, using, rootSelector) => {
  265. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findRepeaterColumn, repeatDescriptor, exact, binding, using, rootSelector));
  266. },
  267. toString: () => {
  268. return name + '("' + repeatDescriptor + '").column("' + binding + '")';
  269. },
  270. row: (index) => {
  271. return {
  272. findElementsOverride: (driver, using, rootSelector) => {
  273. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findRepeaterElement, repeatDescriptor, exact, index, binding, using, rootSelector));
  274. },
  275. toString: () => {
  276. return name + '("' + repeatDescriptor + '").column("' + binding + '").row("' +
  277. index + '")';
  278. }
  279. };
  280. }
  281. };
  282. }
  283. };
  284. }
  285. /**
  286. * Find elements inside an ng-repeat.
  287. *
  288. * @view
  289. * <div ng-repeat="cat in pets">
  290. * <span>{{cat.name}}</span>
  291. * <span>{{cat.age}}</span>
  292. * </div>
  293. *
  294. * <div class="book-img" ng-repeat-start="book in library">
  295. * <span>{{$index}}</span>
  296. * </div>
  297. * <div class="book-info" ng-repeat-end>
  298. * <h4>{{book.name}}</h4>
  299. * <p>{{book.blurb}}</p>
  300. * </div>
  301. *
  302. * @example
  303. * // Returns the DIV for the second cat.
  304. * var secondCat = element(by.repeater('cat in pets').row(1));
  305. *
  306. * // Returns the SPAN for the first cat's name.
  307. * var firstCatName = element(by.repeater('cat in pets').
  308. * row(0).column('cat.name'));
  309. *
  310. * // Returns a promise that resolves to an array of WebElements from a column
  311. * var ages = element.all(
  312. * by.repeater('cat in pets').column('cat.age'));
  313. *
  314. * // Returns a promise that resolves to an array of WebElements containing
  315. * // all top level elements repeated by the repeater. For 2 pets rows
  316. * // resolves to an array of 2 elements.
  317. * var rows = element.all(by.repeater('cat in pets'));
  318. *
  319. * // Returns a promise that resolves to an array of WebElements containing
  320. * // all the elements with a binding to the book's name.
  321. * var divs = element.all(by.repeater('book in library').column('book.name'));
  322. *
  323. * // Returns a promise that resolves to an array of WebElements containing
  324. * // the DIVs for the second book.
  325. * var bookInfo = element.all(by.repeater('book in library').row(1));
  326. *
  327. * // Returns the H4 for the first book's name.
  328. * var firstBookName = element(by.repeater('book in library').
  329. * row(0).column('book.name'));
  330. *
  331. * // Returns a promise that resolves to an array of WebElements containing
  332. * // all top level elements repeated by the repeater. For 2 books divs
  333. * // resolves to an array of 4 elements.
  334. * var divs = element.all(by.repeater('book in library'));
  335. *
  336. * @param {string} repeatDescriptor
  337. * @returns {ProtractorLocator} location strategy
  338. */
  339. repeater(repeatDescriptor) {
  340. return this.byRepeaterInner(false, repeatDescriptor);
  341. }
  342. /**
  343. * Find an element by exact repeater.
  344. *
  345. * @view
  346. * <li ng-repeat="person in peopleWithRedHair"></li>
  347. * <li ng-repeat="car in cars | orderBy:year"></li>
  348. *
  349. * @example
  350. * expect(element(by.exactRepeater('person in
  351. * peopleWithRedHair')).isPresent())
  352. * .toBe(true);
  353. * expect(element(by.exactRepeater('person in
  354. * people')).isPresent()).toBe(false);
  355. * expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true);
  356. *
  357. * @param {string} repeatDescriptor
  358. * @returns {ProtractorLocator} location strategy
  359. */
  360. exactRepeater(repeatDescriptor) {
  361. return this.byRepeaterInner(true, repeatDescriptor);
  362. }
  363. /**
  364. * Find elements by CSS which contain a certain string.
  365. *
  366. * @view
  367. * <ul>
  368. * <li class="pet">Dog</li>
  369. * <li class="pet">Cat</li>
  370. * </ul>
  371. *
  372. * @example
  373. * // Returns the li for the dog, but not cat.
  374. * var dog = element(by.cssContainingText('.pet', 'Dog'));
  375. *
  376. * @param {string} cssSelector css selector
  377. * @param {string|RegExp} searchString text search
  378. * @returns {ProtractorLocator} location strategy
  379. */
  380. cssContainingText(cssSelector, searchText) {
  381. searchText = (searchText instanceof RegExp) ? '__REGEXP__' + searchText.toString() : searchText;
  382. return {
  383. findElementsOverride: (driver, using, rootSelector) => {
  384. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findByCssContainingText, cssSelector, searchText, using, rootSelector));
  385. },
  386. toString: () => {
  387. return 'by.cssContainingText("' + cssSelector + '", "' + searchText + '")';
  388. }
  389. };
  390. }
  391. ;
  392. /**
  393. * Find an element by ng-options expression.
  394. *
  395. * @alias by.options(optionsDescriptor)
  396. * @view
  397. * <select ng-model="color" ng-options="c for c in colors">
  398. * <option value="0" selected="selected">red</option>
  399. * <option value="1">green</option>
  400. * </select>
  401. *
  402. * @example
  403. * var allOptions = element.all(by.options('c for c in colors'));
  404. * expect(allOptions.count()).toEqual(2);
  405. * var firstOption = allOptions.first();
  406. * expect(firstOption.getText()).toEqual('red');
  407. *
  408. * @param {string} optionsDescriptor ng-options expression.
  409. * @returns {ProtractorLocator} location strategy
  410. */
  411. options(optionsDescriptor) {
  412. return {
  413. findElementsOverride: (driver, using, rootSelector) => {
  414. return driver.findElements(selenium_webdriver_1.By.js(clientSideScripts.findByOptions, optionsDescriptor, using, rootSelector));
  415. },
  416. toString: () => {
  417. return 'by.option("' + optionsDescriptor + '")';
  418. }
  419. };
  420. }
  421. ;
  422. /**
  423. * Find an element by css selector within the Shadow DOM.
  424. *
  425. * @alias by.deepCss(selector)
  426. * @view
  427. * <div>
  428. * <span id="outerspan">
  429. * <"shadow tree">
  430. * <span id="span1"></span>
  431. * <"shadow tree">
  432. * <span id="span2"></span>
  433. * </>
  434. * </>
  435. * </div>
  436. * @example
  437. * var spans = element.all(by.deepCss('span'));
  438. * expect(spans.count()).toEqual(3);
  439. *
  440. * @param {string} selector a css selector within the Shadow DOM.
  441. * @returns {Locator} location strategy
  442. */
  443. deepCss(selector) {
  444. // TODO(julie): syntax will change from /deep/ to >>> at some point.
  445. // When that is supported, switch it here.
  446. return selenium_webdriver_1.By.css('* /deep/ ' + selector);
  447. }
  448. ;
  449. }
  450. exports.ProtractorBy = ProtractorBy;
  451. //# sourceMappingURL=locators.js.map