Merge pull request #469 from hexid/master
#455 - Add support for getElementsAttribute() and getElementsInfo()
Showing
6 changed files
with
202 additions
and
24 deletions
... | @@ -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,31 +1043,70 @@ It returns an array of objects with four keys: ``top``, ``left``, ``width`` and | ... | @@ -1024,31 +1043,70 @@ 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": { |
1054 | "align": "left", | ||
1036 | "dir": "ltr", | 1055 | "dir": "ltr", |
1037 | "title": "Google", | 1056 | "id": "hplogo", |
1057 | "onload": "window.lol&&lol()", | ||
1058 | "style": "height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat", | ||
1059 | "title": "Google" | ||
1060 | }, | ||
1061 | "height": 110, | ||
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&&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>", | ||
1065 | "text": "France\n", | ||
1066 | "visible": true, | ||
1067 | "width": 276, | ||
1068 | "x": 62, | ||
1069 | "y": 76 | ||
1070 | } | ||
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": { | ||
1038 | "align": "left", | 1092 | "align": "left", |
1093 | "dir": "ltr", | ||
1039 | "id": "hplogo", | 1094 | "id": "hplogo", |
1040 | "onload": "window.lol&&lol()", | 1095 | "onload": "window.lol&&lol()", |
1041 | "style": "background:url(images/srpr/logo3w.png) no-repeat;background-size:275px 95px;height:95px;width:275px" | 1096 | "style": "height:110px;width:276px;background:url(/images/srpr/logo1w.png) no-repeat", |
1097 | "title": "Google" | ||
1042 | }, | 1098 | }, |
1043 | "tag": "<div dir=\"ltr\" title=\"Google\" align=\"left\" id=\"hplogo\" onload=\"window.lol&&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>", | 1099 | "height": 110, |
1044 | "html": "<div nowrap=\"nowrap\" style=\"color:#777;font-size:16px;font-weight:bold;position:relative;left:214px;top:70px\">France</div>", | 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&&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", | 1103 | "text": "France\n", |
1046 | "x": 582.5, | 1104 | "visible": true, |
1047 | "y": 192, | 1105 | "width": 276, |
1048 | "width": 275, | 1106 | "x": 62, |
1049 | "height": 95, | 1107 | "y": 76 |
1050 | "visible": true | ||
1051 | } | 1108 | } |
1109 | ] | ||
1052 | 1110 | ||
1053 | .. index:: Form | 1111 | .. index:: Form |
1054 | 1112 | ... | ... |
... | @@ -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 | }); | ... | ... |
-
Please register or sign in to post a comment