element-commands.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. var util = require('util');
  2. var events = require('events');
  3. var Logger = require('../util/logger.js');
  4. var Utils = require('../util/utils.js');
  5. module.exports = function(client) {
  6. var Protocol = require('./protocol.js')(client);
  7. var returnValue = {};
  8. var elementCommands = {};
  9. /**
  10. * Simulates a click event on the given DOM element. Uses `elementIdClick` protocol command.
  11. *
  12. * ```
  13. * this.demoTest = function (client) {
  14. * client.click("#main ul li a.first");
  15. * };
  16. * ```
  17. *
  18. * @method click
  19. * @param {string} selector The CSS/Xpath selector used to locate the element.
  20. * @param {function} [callback] Optional callback function to be called when the command finishes.
  21. * @see elementIdClick
  22. * @api commands
  23. */
  24. elementCommands.click = 'elementIdClick';
  25. /**
  26. * Clear a textarea or a text input element's value. Uses `elementIdValue` protocol command.
  27. *
  28. * ```
  29. * this.demoTest = function (client) {
  30. * client.clearValue('input[type=text]');
  31. * };
  32. * ```
  33. *
  34. * @method clearValue
  35. * @param {string} selector The CSS/Xpath selector used to locate the element.
  36. * @param {function} [callback] Optional callback function to be called when the command finishes.
  37. * @see elementIdClear
  38. * @api commands
  39. */
  40. elementCommands.clearValue = 'elementIdClear';
  41. /**
  42. * Retrieve the value of an attribute for a given DOM element. Uses `elementIdAttribute` protocol command.
  43. *
  44. * ```
  45. * this.demoTest = function (client) {
  46. * client.getAttribute("#main ul li a.first", "href", function(result) {
  47. * this.assert.equal(typeof result, "object");
  48. * this.assert.equal(result.status, 0);
  49. * this.assert.equal(result.value, "#home");
  50. * });
  51. * };
  52. * ```
  53. *
  54. * @method getAttribute
  55. * @param {string} selector The CSS/Xpath selector used to locate the element.
  56. * @param {string} attribute The attribute name to inspect.
  57. * @param {function} [callback] Optional callback function to be called when the command finishes.
  58. * @see elementIdAttribute
  59. * @returns {*} The value of the attribute
  60. * @api commands
  61. */
  62. elementCommands.getAttribute = ['elementIdAttribute', 1];
  63. /**
  64. * Retrieve the value of a css property for a given DOM element. Uses `elementIdCssProperty` protocol command.
  65. *
  66. * ```
  67. * this.demoTest = function (client) {
  68. * client.getCssProperty("#main ul li a.first", "display", function(result) {
  69. * this.assert.equal(typeof result, "object");
  70. * this.assert.equal(result.status, 0);
  71. * this.assert.equal(result.value, 'inline');
  72. * });
  73. * };
  74. * ```
  75. *
  76. * @method getCssProperty
  77. * @param {string} selector The CSS/Xpath selector used to locate the element.
  78. * @param {string} cssProperty The CSS property to inspect.
  79. * @param {function} [callback] Optional callback function to be called when the command finishes.
  80. * @see elementIdCssProperty
  81. * @returns {*} The value of the css property
  82. * @api commands
  83. */
  84. elementCommands.getCssProperty = ['elementIdCssProperty', 1];
  85. /**
  86. * Determine an element's size in pixels. Uses `elementIdSize` protocol command.
  87. *
  88. * ```
  89. * this.demoTest = function (client) {
  90. * client.getElementSize("#main ul li a.first", function(result) {
  91. * this.assert.equal(typeof result, "object");
  92. * this.assert.equal(result.status, 0);
  93. * this.assert.equal(result.value.width, 500);
  94. * this.assert.equal(result.value.height, 20);
  95. * });
  96. * };
  97. * ```
  98. *
  99. * @method getElementSize
  100. * @param {string} selector The CSS/Xpath selector used to locate the element.
  101. * @param {function} [callback] Optional callback function to be called when the command finishes.
  102. * @see elementIdSize
  103. * @returns {{width: number, height: number}} The width and height of the element in pixels
  104. * @api commands
  105. */
  106. elementCommands.getElementSize = 'elementIdSize';
  107. /**
  108. * Determine an element's location on the page. The point (0, 0) refers to the upper-left corner of the page.
  109. *
  110. * The element's coordinates are returned as a JSON object with x and y properties. Uses `elementIdLocation` protocol command.
  111. *
  112. * ```
  113. * this.demoTest = function (client) {
  114. * client.getLocation("#main ul li a.first", function(result) {
  115. * this.assert.equal(typeof result, "object");
  116. * this.assert.equal(result.status, 0);
  117. * this.assert.equal(result.value.x, 200);
  118. * this.assert.equal(result.value.y, 200);
  119. * });
  120. * };
  121. * ```
  122. *
  123. * @method getLocation
  124. * @param {string} selector The CSS/Xpath selector used to locate the element.
  125. * @param {function} [callback] Optional callback function to be called when the command finishes.
  126. * @see elementIdLocation
  127. * @returns {x:number, y:number} The X and Y coordinates for the element on the page.
  128. * @api commands
  129. */
  130. elementCommands.getLocation = 'elementIdLocation';
  131. /**
  132. * Determine an element's location on the screen once it has been scrolled into view. Uses `elementIdLocationInView` protocol command.
  133. *
  134. * ```
  135. * this.demoTest = function (browser) {
  136. * browser.getLocationInView("#main ul li a.first", function(result) {
  137. * this.assert.equal(typeof result, "object");
  138. * this.assert.equal(result.status, 0);
  139. * this.assert.equal(result.value.x, 200);
  140. * this.assert.equal(result.value.y, 200);
  141. * });
  142. * };
  143. * ```
  144. *
  145. * @method getLocationInView
  146. * @param {string} selector The CSS/Xpath selector used to locate the element.
  147. * @param {function} [callback] Optional callback function to be called when the command finishes.
  148. * @see elementIdLocationInView
  149. * @returns {x: number, y: number} The X and Y coordinates for the element on the page.
  150. * @api commands
  151. */
  152. elementCommands.getLocationInView = 'elementIdLocationInView';
  153. /**
  154. * Query for an element's tag name. Uses `elementIdName` protocol command.
  155. *
  156. * ```
  157. * this.demoTest = function (client) {
  158. * client.getTagName("#main ul li .first", function(result) {
  159. * this.assert.equal(typeof result, "object");
  160. * this.assert.equal(result.status, 0);
  161. * this.assert.equal(result.value, "a");
  162. * });
  163. * };
  164. * ```
  165. *
  166. * @method getTagName
  167. * @param {string} selector The CSS/Xpath selector used to locate the element.
  168. * @param {function} [callback] Optional callback function to be called when the command finishes.
  169. * @see elementIdName
  170. * @returns {number} The element's tag name, as a lowercase string.
  171. * @api commands
  172. */
  173. elementCommands.getTagName = 'elementIdName';
  174. /**
  175. * Returns the visible text for the element. Uses `elementIdText` protocol command.
  176. *
  177. * ```
  178. * this.demoTest = function (browser) {
  179. * browser.getText("#main ul li a.first", function(result) {
  180. * this.assert.equal(typeof result, "object");
  181. * this.assert.equal(result.status, 0);
  182. * this.assert.equal(result.value, "nightwatchjs.org");
  183. * });
  184. * };
  185. * ```
  186. *
  187. * @method getText
  188. * @param {string} selector The CSS/Xpath selector used to locate the element.
  189. * @param {function} [callback] Optional callback function to be called when the command finishes.
  190. * @see elementIdText
  191. * @returns {string} The element's visible text.
  192. * @api commands
  193. */
  194. elementCommands.getText = 'elementIdText';
  195. /**
  196. * Returns a form element current value. Uses `elementIdValue` protocol command.
  197. *
  198. * ```
  199. * this.demoTest = function (browser) {
  200. * browser.getValue("form.login input[type=text]", function(result) {
  201. * this.assert.equal(typeof result, "object");
  202. * this.assert.equal(result.status, 0);
  203. * this.assert.equal(result.value, "enter username");
  204. * });
  205. * };
  206. * ```
  207. *
  208. * @method getValue
  209. * @param {string} selector The CSS/Xpath selector used to locate the element.
  210. * @param {function} [callback] Optional callback function to be called when the command finishes.
  211. * @see elementIdValue
  212. * @returns {string} The element's value.
  213. * @api commands
  214. */
  215. elementCommands.getValue = 'elementIdValue';
  216. /**
  217. * Determine if an element is currently displayed. Uses `elementIdDisplayed` protocol command.
  218. *
  219. * ```
  220. * this.demoTest = function (browser) {
  221. * browser.isVisible('#main', function(result) {
  222. * this.assert.equal(typeof result, "object");
  223. * this.assert.equal(result.status, 0);
  224. * this.assert.equal(result.value, true);
  225. * });
  226. * };
  227. * ```
  228. *
  229. * @method isVisible
  230. * @param {string} selector The CSS/Xpath selector used to locate the element.
  231. * @param {function} [callback] Optional callback function to be called when the command finishes.
  232. * @see elementIdDisplayed
  233. * @api commands
  234. */
  235. elementCommands.isVisible = 'elementIdDisplayed';
  236. /**
  237. * Move the mouse by an offset of the specified element. Uses `moveTo` protocol command.
  238. *
  239. * ```
  240. * this.demoTest = function (browser) {
  241. * browser.moveToElement('#main', 10, 10);
  242. * };
  243. * ```
  244. *
  245. * @method moveToElement
  246. * @param {string} selector The CSS/Xpath selector used to locate the element.
  247. * @param {number} xoffset X offset to move to, relative to the top-left corner of the element.
  248. * @param {number} yoffset Y offset to move to, relative to the top-left corner of the element.
  249. * @param {function} [callback] Optional callback function to be called when the command finishes.
  250. * @see moveTo
  251. * @api commands
  252. */
  253. elementCommands.moveToElement = ['moveTo', 2];
  254. /**
  255. * Sends some text to an element. Can be used to set the value of a form element or to send a sequence of key strokes to an element. Any UTF-8 character may be specified.
  256. *
  257. * An object map with available keys and their respective UTF-8 characters, as defined on [W3C WebDriver draft spec](http://www.w3.org/TR/webdriver/#character-types), is loaded onto the main Nightwatch instance as `client.Keys`.
  258. *
  259. * ```
  260. * // send some simple text to an input
  261. * this.demoTest = function (browser) {
  262. * browser.setValue('input[type=text]', 'nightwatch');
  263. * };
  264. * //
  265. * // send some text to an input and hit enter.
  266. * this.demoTest = function (browser) {
  267. * browser.setValue('input[type=text]', ['nightwatch', browser.Keys.ENTER]);
  268. * };
  269. * ```
  270. *
  271. * @link /session/:sessionId/element/:id/value
  272. * @method setValue
  273. * @param {string} selector The CSS/Xpath selector used to locate the element.
  274. * @param {string|array} value The text to send to the element or key strokes.
  275. * @param {function} [callback] Optional callback function to be called when the command finishes.
  276. * @see elementIdValue
  277. * @api commands
  278. */
  279. elementCommands.setValue = ['elementIdValue', 1];
  280. /**
  281. * Submit a FORM element. The submit command may also be applied to any element that is a descendant of a FORM element. Uses `submit` protocol command.
  282. *
  283. * ```
  284. * this.demoTest = function (browser) {
  285. * browser.submitForm('form.login');
  286. * };
  287. * ```
  288. *
  289. * @method submitForm
  290. * @param {string} selector The CSS/Xpath selector used to locate the element.
  291. * @param {function} [callback] Optional callback function to be called when the command finishes.
  292. * @see submit
  293. * @api commands
  294. */
  295. elementCommands.submitForm = 'submit';
  296. function addElementCommand(protocolAction, extraArgs) {
  297. extraArgs = extraArgs || 0;
  298. var expectedArgs = 3 + extraArgs;
  299. return function commandActionFn() {
  300. var originalStackTrace = commandActionFn.stackTrace;
  301. var noopFn = function() {};
  302. var args = Array.prototype.slice.call(arguments, 0);
  303. if (typeof args[args.length-1] !== 'function') {
  304. args.push(noopFn);
  305. }
  306. var defaultUsing = client.locateStrategy || 'css selector';
  307. if (expectedArgs - args.length === 1) {
  308. args.unshift(defaultUsing);
  309. }
  310. if (args.length < expectedArgs - 1 || args.length > expectedArgs) {
  311. throw new Error(protocolAction + ' method expects ' + (expectedArgs - 1) + ' or ' + expectedArgs + ' arguments - ' + args.length + ' given.');
  312. }
  313. var using = args.shift();
  314. var value = args.shift();
  315. var callback = args.pop();
  316. return new CommandAction(using, value, protocolAction, args, callback, originalStackTrace);
  317. };
  318. }
  319. function CommandAction(using, value, protocolAction, args, callback, originalStackTrace) {
  320. events.EventEmitter.call(this);
  321. var $this = this;
  322. var el = Protocol.element(using, value, function(result) {
  323. if (result.status !== 0) {
  324. callback.call(client.api, result);
  325. var errorMessage = 'ERROR: Unable to locate element: "' + value + '" using: ' + using;
  326. var stack = originalStackTrace.split('\n');
  327. stack.shift();
  328. Utils.showStackTraceWithHeadline(errorMessage, stack);
  329. client.results.errors++;
  330. client.errors.push(errorMessage + '\n' + stack.join('\n'));
  331. $this.emit('complete', el, $this);
  332. } else {
  333. result = result.value.ELEMENT;
  334. args.push(function(r) {
  335. callback.call(client.api, r);
  336. });
  337. args.unshift(result);
  338. var c = Protocol[protocolAction].apply(Protocol, args).once('complete', function() {
  339. $this.emit('complete', c, $this);
  340. });
  341. }
  342. });
  343. }
  344. util.inherits(CommandAction, events.EventEmitter);
  345. Object.keys(elementCommands).forEach(function(commandName) {
  346. var args = elementCommands[commandName];
  347. if (!Array.isArray(args)) {
  348. args = [args];
  349. }
  350. returnValue[commandName] = addElementCommand.apply(client.api, args);
  351. });
  352. // alias
  353. returnValue.sendKeys = returnValue.setValue;
  354. return returnValue;
  355. };