Commit 65eb8709 65eb8709bf3dfe70c7dd5251ab395983266ff235 by Nicolas Perriault

Merge pull request #469 from hexid/master

#455 - Add support for getElementsAttribute() and getElementsInfo()
2 parents 02e4bbf7 acd7739f
...@@ -972,6 +972,25 @@ Retrieves the value of an attribute on the first element matching the provided : ...@@ -972,6 +972,25 @@ Retrieves the value of an attribute on the first element matching the provided :
972 972
973 .. index:: DOM 973 .. index:: DOM
974 974
975 ``getElementsAttribute()``
976 -------------------------------------------------------------------------------
977
978 **Signature:** ``getElementsAttribute(String selector, String attribute)``
979
980 .. versionadded:: 1.1
981
982 Retrieves the values of an attribute on each element matching the provided :doc:`selector <../selectors>`::
983
984 var casper = require('casper').create();
985
986 casper.start('http://www.google.fr/', function() {
987 require('utils').dump(this.getElementsAttribute('div[title="Google"]', 'title')); // "['Google']"
988 });
989
990 casper.run();
991
992 .. index:: DOM
993
975 ``getElementBounds()`` 994 ``getElementBounds()``
976 ------------------------------------------------------------------------------- 995 -------------------------------------------------------------------------------
977 996
...@@ -1024,32 +1043,71 @@ It returns an array of objects with four keys: ``top``, ``left``, ``width`` and ...@@ -1024,32 +1043,71 @@ It returns an array of objects with four keys: ``top``, ``left``, ``width`` and
1024 1043
1025 Retrieves information about the first element matching the provided :doc:`selector <../selectors>`:: 1044 Retrieves information about the first element matching the provided :doc:`selector <../selectors>`::
1026 1045
1027 casper.start('http://google.com/', function() { 1046 casper.start('http://google.fr/', function() {
1028 require('utils').dump(this.getElementInfo('#hplogo')); 1047 require('utils').dump(this.getElementInfo('#hplogo'));
1029 }); 1048 });
1030 1049
1031 Gives something like:: 1050 Gives something like::
1032 1051
1033 { 1052 {
1034 "nodeName": "div",
1035 "attributes": { 1053 "attributes": {
1036 "dir": "ltr",
1037 "title": "Google",
1038 "align": "left", 1054 "align": "left",
1055 "dir": "ltr",
1039 "id": "hplogo", 1056 "id": "hplogo",
1040 "onload": "window.lol&&lol()", 1057 "onload": "window.lol&&lol()",
1041 "style": "background:url(images/srpr/logo3w.png) no-repeat;background-size:275px 95px;height:95px;width:275px" 1058 "style": "height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat",
1059 "title": "Google"
1042 }, 1060 },
1043 "tag": "<div dir=\"ltr\" title=\"Google\" align=\"left\" id=\"hplogo\" onload=\"window.lol&amp;&amp;lol()\" style=\"background:url(images/srpr/logo3w.png) no-repeat;background-size:275px 95px;height:95px;width:275px\"><div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div></div>", 1061 "height": 110,
1044 "html": "<div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div>", 1062 "html": "<div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div>",
1063 "nodeName": "div",
1064 "tag": "<div dir=\"ltr\" title=\"Google\" align=\"left\" id=\"hplogo\" onload=\"window.lol&amp;&amp;lol()\" style=\"height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat\"><div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div></div>",
1045 "text": "France\n", 1065 "text": "France\n",
1046 "x": 582.5, 1066 "visible": true,
1047 "y": 192, 1067 "width": 276,
1048 "width": 275, 1068 "x": 62,
1049 "height": 95, 1069 "y": 76
1050 "visible": true
1051 } 1070 }
1052 1071
1072 .. index:: DOM
1073
1074 ``getElementsInfo()``
1075 -------------------------------------------------------------------------------
1076
1077 **Signature:** ``getElementsInfo(String selector)``
1078
1079 .. versionadded:: 1.1
1080
1081 Retrieves information about all elements matching the provided :doc:`selector <../selectors>`::
1082
1083 casper.start('http://google.fr/', function() {
1084 require('utils').dump(this.getElementsInfo('#hplogo'));
1085 });
1086
1087 Gives something like::
1088
1089 [
1090 {
1091 "attributes": {
1092 "align": "left",
1093 "dir": "ltr",
1094 "id": "hplogo",
1095 "onload": "window.lol&&lol()",
1096 "style": "height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat",
1097 "title": "Google"
1098 },
1099 "height": 110,
1100 "html": "<div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div>",
1101 "nodeName": "div",
1102 "tag": "<div dir=\"ltr\" title=\"Google\" align=\"left\" id=\"hplogo\" onload=\"window.lol&amp;&amp;lol()\" style=\"height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat\"><div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div></div>",
1103 "text": "France\n",
1104 "visible": true,
1105 "width": 276,
1106 "x": 62,
1107 "y": 76
1108 }
1109 ]
1110
1053 .. index:: Form 1111 .. index:: Form
1054 1112
1055 ``getFormValues()`` 1113 ``getFormValues()``
......
...@@ -963,6 +963,25 @@ Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) { ...@@ -963,6 +963,25 @@ Casper.prototype.getElementAttr = function getElementAttr(selector, attribute) {
963 }; 963 };
964 964
965 /** 965 /**
966 * Retrieves the value of an attribute for each element matching the provided
967 * DOM CSS3/XPath selector.
968 *
969 * @param String selector A DOM CSS3/XPath selector
970 * @param String attribute The attribute name to lookup
971 * @return Array
972 */
973 Casper.prototype.getElementsAttribute =
974 Casper.prototype.getElementsAttr = function getElementsAttr(selector, attribute) {
975 "use strict";
976 this.checkStarted();
977 return this.evaluate(function _evaluate(selector, attribute) {
978 return [].map.call(__utils__.findAll(selector), function(element) {
979 return element.getAttribute(attribute);
980 });
981 }, selector, attribute);
982 }
983
984 /**
966 * Retrieves boundaries for a DOM element matching the provided DOM CSS3/XPath selector. 985 * Retrieves boundaries for a DOM element matching the provided DOM CSS3/XPath selector.
967 * 986 *
968 * @param String selector A DOM CSS3/XPath selector 987 * @param String selector A DOM CSS3/XPath selector
...@@ -1001,6 +1020,23 @@ Casper.prototype.getElementInfo = function getElementInfo(selector) { ...@@ -1001,6 +1020,23 @@ Casper.prototype.getElementInfo = function getElementInfo(selector) {
1001 }; 1020 };
1002 1021
1003 /** 1022 /**
1023 * Retrieves information about the nodes matching the provided selector.
1024 *
1025 * @param String|Objects selector CSS3/XPath selector
1026 * @return Array
1027 */
1028 Casper.prototype.getElementsInfo = function getElementsInfo(selector) {
1029 "use strict";
1030 this.checkStarted();
1031 if (!this.exists(selector)) {
1032 throw new CasperError(f("Cannot get information from %s: no elements found.", selector));
1033 }
1034 return this.evaluate(function(selector) {
1035 return __utils__.getElementsInfo(selector);
1036 }, selector);
1037 };
1038
1039 /**
1004 * Retrieves boundaries for all the DOM elements matching the provided DOM CSS3/XPath selector. 1040 * Retrieves boundaries for all the DOM elements matching the provided DOM CSS3/XPath selector.
1005 * 1041 *
1006 * @param String selector A DOM CSS3/XPath selector 1042 * @param String selector A DOM CSS3/XPath selector
......
...@@ -128,6 +128,24 @@ ...@@ -128,6 +128,24 @@
128 }; 128 };
129 129
130 /** 130 /**
131 * Checks if a given DOM element is visible in remove page.
132 *
133 * @param Object element DOM element
134 * @return Boolean
135 */
136 this.elementVisible = function elementVisible(elem) {
137 try {
138 var comp = window.getComputedStyle(elem, null);
139 return comp.visibility !== 'hidden' &&
140 comp.display !== 'none' &&
141 elem.offsetHeight > 0 &&
142 elem.offsetWidth > 0;
143 } catch (e) {
144 return false;
145 }
146 }
147
148 /**
131 * Base64 encodes a string, even binary ones. Succeeds where 149 * Base64 encodes a string, even binary ones. Succeeds where
132 * window.btoa() fails. 150 * window.btoa() fails.
133 * 151 *
...@@ -437,6 +455,35 @@ ...@@ -437,6 +455,35 @@
437 }; 455 };
438 456
439 /** 457 /**
458 * Retrieves information about the nodes matching the provided selector.
459 *
460 * @param String|Object selector CSS3/XPath selector
461 * @return Array
462 */
463 this.getElementsInfo = function getElementsInfo(selector) {
464 var bounds = this.getElementsBounds(selector);
465 var eleVisible = this.elementVisible;
466 return [].map.call(this.findAll(selector), function(element, index) {
467 var attributes = {};
468 [].forEach.call(element.attributes, function(attr) {
469 attributes[attr.name.toLowerCase()] = attr.value;
470 });
471 return {
472 nodeName: element.nodeName.toLowerCase(),
473 attributes: attributes,
474 tag: element.outerHTML,
475 html: element.innerHTML,
476 text: element.innerText,
477 x: bounds[index].left,
478 y: bounds[index].top,
479 width: bounds[index].width,
480 height: bounds[index].height,
481 visible: eleVisible(element)
482 };
483 });
484 };
485
486 /**
440 * Retrieves a single DOM element matching a given XPath expression. 487 * Retrieves a single DOM element matching a given XPath expression.
441 * 488 *
442 * @param String expression The XPath expression 489 * @param String expression The XPath expression
...@@ -796,24 +843,13 @@ ...@@ -796,24 +843,13 @@
796 }; 843 };
797 844
798 /** 845 /**
799 * Checks if a given DOM element is visible in remote page. 846 * Checks if any element matching a given selector is visible in remote page.
800 * 847 *
801 * @param String selector CSS3 selector 848 * @param String selector CSS3 selector
802 * @return Boolean 849 * @return Boolean
803 */ 850 */
804 this.visible = function visible(selector) { 851 this.visible = function visible(selector) {
805 try { 852 return [].some.call(this.findAll(selector), this.elementVisible);
806 var elems = this.findAll(selector);
807 return Array.prototype.some.call(elems, function(el) {
808 var comp = window.getComputedStyle(el, null);
809 return comp.visibility !== 'hidden' &&
810 comp.display !== 'none' &&
811 el.offsetHeight > 0 &&
812 el.offsetWidth > 0;
813 });
814 } catch (e) {
815 return false;
816 }
817 }; 853 };
818 }; 854 };
819 })(typeof exports === "object" ? exports : window); 855 })(typeof exports === "object" ? exports : window);
......
...@@ -2,5 +2,6 @@ ...@@ -2,5 +2,6 @@
2 <html> 2 <html>
3 <body> 3 <body>
4 <div class="tester testo testme" data-stuff="beautiful string"></div> 4 <div class="tester testo testme" data-stuff="beautiful string"></div>
5 <div class="tester testo testme" data-stuff="not as beautiful string"></div>
5 </body> 6 </body>
6 </html> 7 </html>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -2,12 +2,19 @@ ...@@ -2,12 +2,19 @@
2 /*jshint strict:false*/ 2 /*jshint strict:false*/
3 var x = require('casper').selectXPath; 3 var x = require('casper').selectXPath;
4 4
5 casper.test.begin('getElementAttribute() tests', 2, function(test) { 5 casper.test.begin('getElementAttribute() tests', 4, function(test) {
6 casper.start('tests/site/elementattribute.html', function() { 6 casper.start('tests/site/elementattribute.html', function() {
7 test.assertEquals(this.getElementAttribute('.testo', 'data-stuff'), 7 test.assertEquals(this.getElementAttribute('.testo', 'data-stuff'),
8 'beautiful string', 'Casper.getElementAttribute() works with a CSS selector'); 8 'beautiful string', 'Casper.getElementAttribute() works with a CSS selector');
9 test.assertEquals(this.getElementAttribute(x('//div[@class]'), 'data-stuff'), 9 test.assertEquals(this.getElementAttribute(x('//div[@class]'), 'data-stuff'),
10 'beautiful string', 'Casper.getElementAttribute() works with a XPath selector'); 10 'beautiful string', 'Casper.getElementAttribute() works with a XPath selector');
11 }).then(function() {
12 test.assertEquals(this.getElementsAttribute('.testo', 'data-stuff'),
13 ['beautiful string', 'not as beautiful string'],
14 'Casper.getElementsAttribute() works with a CSS selector');
15 test.assertEquals(this.getElementsAttribute(x('//div[@class]'), 'data-stuff'),
16 ['beautiful string', 'not as beautiful string'],
17 'Casper.getElementsAttribute() works with a XPath selector');
11 }).run(function() { 18 }).run(function() {
12 test.done(); 19 test.done();
13 }); 20 });
......
...@@ -161,3 +161,43 @@ casper.test.begin('ClientUtils.getElementInfo() tests', 10, function(test) { ...@@ -161,3 +161,43 @@ casper.test.begin('ClientUtils.getElementInfo() tests', 10, function(test) {
161 'ClientUtils.getElementInfo() retrieves element whole tag contents'); 161 'ClientUtils.getElementInfo() retrieves element whole tag contents');
162 test.done(); 162 test.done();
163 }); 163 });
164
165 casper.test.begin('ClientUtils.getElementsInfo() first element tests', 10, function(test) {
166 casper.page.content = '<a href="plop" class="plip plup"><i>paf</i></a><a href="plap" class="plip plup"><i>puf</i></a>';
167 var info = casper.getElementsInfo('a.plip');
168 test.assertEquals(info[0].nodeName, 'a', 'ClientUtils.getElementsInfo() retrieves first element name');
169 test.assertEquals(info[0].attributes, {
170 'href': 'plop',
171 'class': 'plip plup'
172 }, 'ClientUtils.getElementsInfo() retrieves first element attributes');
173 test.assertEquals(info[0].html, '<i>paf</i>', 'ClientUtils.getElementsInfo() retrieves first element html content');
174 test.assertEquals(info[0].text, 'paf', 'ClientUtils.getElementsInfo() retrieves first element text');
175 test.assert(info[0].x > 0, 'ClientUtils.getElementsInfo() retrieves first element x pos');
176 test.assert(info[0].y > 0, 'ClientUtils.getElementsInfo() retrieves first element y pos');
177 test.assert(info[0].width > 0, 'ClientUtils.getElementsInfo() retrieves first element width');
178 test.assert(info[0].height > 0, 'ClientUtils.getElementsInfo() retrieves first element height');
179 test.assert(info[0].visible, 'ClientUtils.getElementsInfo() retrieves first element visibility');
180 test.assertEquals(info[0].tag, '<a href="plop" class="plip plup"><i>paf</i></a>',
181 'ClientUtils.getElementsInfo() retrieves first element whole tag contents');
182 test.done();
183 });
184
185 casper.test.begin('ClientUtils.getElementsInfo() second element tests', 10, function(test) {
186 casper.page.content = '<a href="plop" class="plip plup"><i>paf</i></a><a href="plap" class="plip plup"><i>puf</i></a>';
187 var info = casper.getElementsInfo('a.plip');
188 test.assertEquals(info[1].nodeName, 'a', 'ClientUtils.getElementsInfo() retrieves second element name');
189 test.assertEquals(info[1].attributes, {
190 'href': 'plap',
191 'class': 'plip plup'
192 }, 'ClientUtils.getElementsInfo() retrieves second element attributes');
193 test.assertEquals(info[1].html, '<i>puf</i>', 'ClientUtils.getElementsInfo() retrieves second element html content');
194 test.assertEquals(info[1].text, 'puf', 'ClientUtils.getElementsInfo() retrieves second element text');
195 test.assert(info[1].x > 0, 'ClientUtils.getElementsInfo() retrieves second element x pos');
196 test.assert(info[1].y > 0, 'ClientUtils.getElementsInfo() retrieves second element y pos');
197 test.assert(info[1].width > 0, 'ClientUtils.getElementsInfo() retrieves second element width');
198 test.assert(info[1].height > 0, 'ClientUtils.getElementsInfo() retrieves second element height');
199 test.assert(info[1].visible, 'ClientUtils.getElementsInfo() retrieves second element visibility');
200 test.assertEquals(info[1].tag, '<a href="plap" class="plip plup"><i>puf</i></a>',
201 'ClientUtils.getElementsInfo() retrieves second element whole tag contents');
202 test.done();
203 });
......