watir 6.7.3 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/CHANGES.md +11 -0
  4. data/lib/watir.rb +4 -1
  5. data/lib/watir/adjacent.rb +1 -1
  6. data/lib/watir/browser.rb +5 -3
  7. data/lib/watir/capabilities.rb +1 -0
  8. data/lib/watir/container.rb +1 -1
  9. data/lib/watir/elements/element.rb +31 -98
  10. data/lib/watir/elements/file_field.rb +1 -1
  11. data/lib/watir/elements/iframe.rb +1 -1
  12. data/lib/watir/elements/image.rb +1 -5
  13. data/lib/watir/elements/input.rb +15 -0
  14. data/lib/watir/elements/radio.rb +17 -0
  15. data/lib/watir/elements/select.rb +67 -20
  16. data/lib/watir/js_execution.rb +141 -0
  17. data/lib/watir/js_snippets.rb +16 -0
  18. data/lib/watir/js_snippets/backgroundColor.js +7 -0
  19. data/lib/watir/{atoms → js_snippets}/fireEvent.js +2 -0
  20. data/lib/watir/js_snippets/focus.js +3 -0
  21. data/lib/watir/{atoms → js_snippets}/getInnerHtml.js +1 -0
  22. data/lib/watir/js_snippets/getInnerText.js +19 -0
  23. data/lib/watir/{atoms → js_snippets}/getOuterHtml.js +2 -0
  24. data/lib/watir/js_snippets/getTextContent.js +19 -0
  25. data/lib/watir/js_snippets/isImageLoaded.js +3 -0
  26. data/lib/watir/js_snippets/selectOptionsLabel.js +10 -0
  27. data/lib/watir/js_snippets/selectOptionsText.js +10 -0
  28. data/lib/watir/js_snippets/selectOptionsValue.js +10 -0
  29. data/lib/watir/{atoms → js_snippets}/selectText.js +3 -0
  30. data/lib/watir/js_snippets/selectedOptions.js +11 -0
  31. data/lib/watir/js_snippets/setValue.js +3 -0
  32. data/lib/watir/locators/element/locator.rb +24 -10
  33. data/lib/watir/radio_set.rb +231 -0
  34. data/lib/watir/user_editable.rb +16 -2
  35. data/lib/watirspec/remote_server.rb +2 -1
  36. data/spec/browser_spec.rb +8 -2
  37. data/spec/spec_helper.rb +1 -0
  38. data/spec/watirspec/elements/div_spec.rb +22 -0
  39. data/spec/watirspec/elements/divs_spec.rb +1 -1
  40. data/spec/watirspec/elements/element_spec.rb +24 -7
  41. data/spec/watirspec/elements/labels_spec.rb +1 -1
  42. data/spec/watirspec/elements/radio_spec.rb +17 -3
  43. data/spec/watirspec/elements/radios_spec.rb +1 -1
  44. data/spec/watirspec/elements/select_list_spec.rb +167 -65
  45. data/spec/watirspec/elements/text_field_spec.rb +36 -0
  46. data/spec/watirspec/html/forms_with_input_elements.html +4 -2
  47. data/spec/watirspec/html/non_control_elements.html +1 -1
  48. data/spec/watirspec/radio_set_spec.rb +340 -0
  49. data/spec/watirspec_helper.rb +14 -1
  50. data/support/doctest_helper.rb +11 -8
  51. data/watir.gemspec +2 -2
  52. metadata +30 -11
  53. data/lib/watir/atoms.rb +0 -22
  54. data/lib/watir/atoms/README +0 -3
  55. data/lib/watir/extensions/select_text.rb +0 -11
@@ -0,0 +1,141 @@
1
+ module Watir
2
+ module JSExecution
3
+
4
+ #
5
+ # Delegates script execution to Browser or IFrame.
6
+ #
7
+ def execute_script(script, *args)
8
+ @query_scope.execute_script(script, *args)
9
+ end
10
+
11
+ #
12
+ # Simulates JavaScript events on element.
13
+ # Note that you may omit "on" from event name.
14
+ #
15
+ # @example
16
+ # browser.button(name: "new_user_button").fire_event :click
17
+ # browser.button(name: "new_user_button").fire_event "mousemove"
18
+ # browser.button(name: "new_user_button").fire_event "onmouseover"
19
+ #
20
+ # @param [String, Symbol] event_name
21
+ #
22
+
23
+ def fire_event(event_name)
24
+ event_name = event_name.to_s.sub(/^on/, '').downcase
25
+
26
+ element_call { execute_js :fireEvent, @element, event_name }
27
+ end
28
+
29
+ #
30
+ # Flashes (change background color to a new color and back a few times) element.
31
+ #
32
+ # @example
33
+ # browser.li(id: 'non_link_1').flash
34
+ # browser.li(id: 'non_link_1').flash(color: "green", flashes: 3, delay: 0.05)
35
+ # browser.li(id: 'non_link_1').flash(color: "yellow")
36
+ # browser.li(id: 'non_link_1').flash(flashes: 4)
37
+ # browser.li(id: 'non_link_1').flash(delay: 0.1)
38
+ #
39
+ # @param [String] color what color to flash with
40
+ # @param [Integer] flashes number of times element should be flashed
41
+ # @param [Integer, Float] delay how long to wait between flashes
42
+ #
43
+ # @return [Watir::Element]
44
+ #
45
+
46
+ def flash(color: 'red', flashes: 10, delay: 0)
47
+ background_color = style("backgroundColor")
48
+ element_color = element_call { execute_js(:backgroundColor, @element) }.strip
49
+
50
+ flashes.times do |n|
51
+ nextcolor = n.even? ? color : background_color
52
+ element_call { execute_js(:backgroundColor, @element, nextcolor) }
53
+ sleep(delay)
54
+ end
55
+
56
+ element_call { execute_js(:backgroundColor, @element, element_color) }
57
+
58
+ self
59
+ end
60
+
61
+ #
62
+ # Focuses element.
63
+ # Note that Firefox queues focus events until the window actually has focus.
64
+ #
65
+ # @see http://code.google.com/p/selenium/issues/detail?id=157
66
+ #
67
+
68
+ def focus
69
+ element_call { execute_js(:focus, @element) }
70
+ end
71
+
72
+ #
73
+ # Returns inner HTML code of element.
74
+ #
75
+ # @example
76
+ # browser.div(id: 'shown').inner_html
77
+ # #=> "<div id=\"hidden\" style=\"display: none;\">Not shown</div><div>Not hidden</div>"
78
+ #
79
+ # @return [String]
80
+ #
81
+
82
+ def inner_html
83
+ element_call { execute_js(:getInnerHtml, @element) }.strip
84
+ end
85
+
86
+ #
87
+ # Returns inner Text code of element.
88
+ #
89
+ # @example
90
+ # browser.div(id: 'shown').inner_text
91
+ # #=> "Not hidden"
92
+ #
93
+ # @return [String]
94
+ #
95
+
96
+ def inner_text
97
+ element_call { execute_js(:getInnerText, @element) }.strip
98
+ end
99
+
100
+ #
101
+ # Returns outer (inner + element itself) HTML code of element.
102
+ #
103
+ # @example
104
+ # browser.div(id: 'shown').outer_html
105
+ # #=> "<div id=\"shown\"><div id=\"hidden\" style=\"display: none;\">Not shown</div><div>Not hidden</div></div>"
106
+ #
107
+ # @return [String]
108
+ #
109
+
110
+ def outer_html
111
+ element_call { execute_js(:getOuterHtml, @element) }.strip
112
+ end
113
+ alias_method :html, :outer_html
114
+
115
+ #
116
+ # Returns text content of element.
117
+ #
118
+ # @example
119
+ # browser.div(id: 'shown').text_content
120
+ # #=> "Not shownNot hidden"
121
+ #
122
+ # @return [String]
123
+ #
124
+
125
+ def text_content
126
+ element_call { execute_js(:getTextContent, @element) }.strip
127
+ end
128
+
129
+ #
130
+ # Selects text on page (as if dragging clicked mouse across provided text).
131
+ #
132
+ # @example
133
+ # browser.li(id: 'non_link_1').select_text('Non-link')
134
+ #
135
+
136
+ def select_text(str)
137
+ element_call { execute_js :selectText, @element, str }
138
+ end
139
+
140
+ end # JSExecution
141
+ end # Watir
@@ -0,0 +1,16 @@
1
+ module Watir
2
+ module JSSnippets
3
+
4
+ private
5
+
6
+ def execute_js(function_name, *arguments)
7
+ file = File.expand_path("../js_snippets/#{function_name}.js", __FILE__)
8
+ raise Watir::Error, "Can not excute script as #{function_name}.js does not exist" unless File.exist?(file)
9
+
10
+ js = File.read(file)
11
+ script = "return (#{js}).apply(null, arguments)"
12
+ @query_scope.execute_script(script, *arguments)
13
+ end
14
+
15
+ end # JSSnippets
16
+ end # Watir
@@ -0,0 +1,7 @@
1
+ function(){
2
+ if(arguments[1]) {
3
+ arguments[0].style.backgroundColor = arguments[1];
4
+ } else {
5
+ return arguments[0].style.backgroundColor;
6
+ }
7
+ }
@@ -11,6 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ // This code was built by the Selenium code base
15
+ // See https://github.com/SeleniumHQ/selenium/tree/master/javascript/watir-atoms
14
16
 
15
17
  function(){return function(){var l=this;
16
18
  function m(a){var c=typeof a;if(c=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return c;var b=Object.prototype.toString.call(a);if(b=="[object Window]")return"object";if(b=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(b=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if(c==
@@ -0,0 +1,3 @@
1
+ function(){
2
+ return arguments[0].focus();
3
+ }
@@ -11,6 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ // This code is a modification getOuterHtml.js which was built by the Selenium code base
14
15
 
15
16
  function(){return function(){var d=this;function e(a,b){function c(){}c.prototype=b.prototype;a.b=b.prototype;a.prototype=new c};function f(a){this.stack=Error().stack||"";if(a)this.message=String(a)}e(f,Error);function i(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}
16
17
  function j(a,b){var c=0,A=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),B=String(b).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),H=Math.max(A.length,B.length);for(var l=0;c==0&&l<H;l++){var I=A[l]||"",J=B[l]||"",K=RegExp("(\\d*)(\\D*)","g"),L=RegExp("(\\d*)(\\D*)","g");do{var g=K.exec(I)||["","",""],h=L.exec(J)||["","",""];if(g[0].length==0&&h[0].length==0)break;c=k(g[1].length==0?0:parseInt(g[1],10),h[1].length==0?0:parseInt(h[1],10))||k(g[2].length==0,h[2].length==0)||k(g[2],h[2])}while(c==
@@ -0,0 +1,19 @@
1
+ // Copyright 2011 Software Freedom Conservatory
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+
14
+ // This code is a modification getOuterHtml.js which was built by the Selenium code base
15
+
16
+ function(){return function(){var d=this;function e(a,b){function c(){}c.prototype=b.prototype;a.b=b.prototype;a.prototype=new c};function f(a){this.stack=Error().stack||"";if(a)this.message=String(a)}e(f,Error);function i(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}
17
+ function j(a,b){var c=0,A=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),B=String(b).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),H=Math.max(A.length,B.length);for(var l=0;c==0&&l<H;l++){var I=A[l]||"",J=B[l]||"",K=RegExp("(\\d*)(\\D*)","g"),L=RegExp("(\\d*)(\\D*)","g");do{var g=K.exec(I)||["","",""],h=L.exec(J)||["","",""];if(g[0].length==0&&h[0].length==0)break;c=k(g[1].length==0?0:parseInt(g[1],10),h[1].length==0?0:parseInt(h[1],10))||k(g[2].length==0,h[2].length==0)||k(g[2],h[2])}while(c==
18
+ 0)}return c}function k(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};e(function(a,b){b.unshift(a);f.call(this,i.apply(null,b));b.shift();this.a=a},f);var m,n,o,p;function q(){return d.navigator?d.navigator.userAgent:null}p=o=n=m=false;var r;if(r=q()){var s=d.navigator;m=r.indexOf("Opera")==0;n=!m&&r.indexOf("MSIE")!=-1;o=!m&&r.indexOf("WebKit")!=-1;p=!m&&!o&&s.product=="Gecko"}var t=n,u=p,v=o,w;
19
+ a:{var x="",y;if(m&&d.opera){var z=d.opera.version;x=typeof z=="function"?z():z}else{if(u)y=/rv\:([^\);]+)(\)|;)/;else if(t)y=/MSIE\s+([^\);]+)(\)|;)/;else if(v)y=/WebKit\/(\S+)/;if(y){var C=y.exec(q());x=C?C[1]:""}}if(t){var D,E=d.document;D=E?E.documentMode:undefined;if(D>parseFloat(x)){w=String(D);break a}}w=x}var F={};!t||F["9"]||(F["9"]=j(w,"9")>=0);t&&(F["9"]||(F["9"]=j(w,"9")>=0));function G(a){if("innerText"in a)return a.innerText;else{var b=(a.nodeType==9?a:a.ownerDocument||a.document).createElement("div");b.appendChild(a.cloneNode(true));return b.innerText}}var M="_".split("."),N=d;!(M[0]in N)&&N.execScript&&N.execScript("var "+M[0]);for(var O;M.length&&(O=M.shift());)if(!M.length&&G!==undefined)N[O]=G;else N=N[O]?N[O]:N[O]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
@@ -11,6 +11,8 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ // This code was built by the Selenium code base
15
+ // See https://github.com/SeleniumHQ/selenium/tree/master/javascript/watir-atoms
14
16
 
15
17
  function(){return function(){var d=this;function e(a,b){function c(){}c.prototype=b.prototype;a.b=b.prototype;a.prototype=new c};function f(a){this.stack=Error().stack||"";if(a)this.message=String(a)}e(f,Error);function i(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}
16
18
  function j(a,b){var c=0,A=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),B=String(b).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),H=Math.max(A.length,B.length);for(var l=0;c==0&&l<H;l++){var I=A[l]||"",J=B[l]||"",K=RegExp("(\\d*)(\\D*)","g"),L=RegExp("(\\d*)(\\D*)","g");do{var g=K.exec(I)||["","",""],h=L.exec(J)||["","",""];if(g[0].length==0&&h[0].length==0)break;c=k(g[1].length==0?0:parseInt(g[1],10),h[1].length==0?0:parseInt(h[1],10))||k(g[2].length==0,h[2].length==0)||k(g[2],h[2])}while(c==
@@ -0,0 +1,19 @@
1
+ // Copyright 2011 Software Freedom Conservatory
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+
14
+ // This code is a modification getOuterHtml.js which was built by the Selenium code base
15
+
16
+ function(){return function(){var d=this;function e(a,b){function c(){}c.prototype=b.prototype;a.b=b.prototype;a.prototype=new c};function f(a){this.stack=Error().stack||"";if(a)this.message=String(a)}e(f,Error);function i(a){for(var b=1;b<arguments.length;b++){var c=String(arguments[b]).replace(/\$/g,"$$$$");a=a.replace(/\%s/,c)}return a}
17
+ function j(a,b){var c=0,A=String(a).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),B=String(b).replace(/^[\s\xa0]+|[\s\xa0]+$/g,"").split("."),H=Math.max(A.length,B.length);for(var l=0;c==0&&l<H;l++){var I=A[l]||"",J=B[l]||"",K=RegExp("(\\d*)(\\D*)","g"),L=RegExp("(\\d*)(\\D*)","g");do{var g=K.exec(I)||["","",""],h=L.exec(J)||["","",""];if(g[0].length==0&&h[0].length==0)break;c=k(g[1].length==0?0:parseInt(g[1],10),h[1].length==0?0:parseInt(h[1],10))||k(g[2].length==0,h[2].length==0)||k(g[2],h[2])}while(c==
18
+ 0)}return c}function k(a,b){if(a<b)return-1;else if(a>b)return 1;return 0};e(function(a,b){b.unshift(a);f.call(this,i.apply(null,b));b.shift();this.a=a},f);var m,n,o,p;function q(){return d.navigator?d.navigator.userAgent:null}p=o=n=m=false;var r;if(r=q()){var s=d.navigator;m=r.indexOf("Opera")==0;n=!m&&r.indexOf("MSIE")!=-1;o=!m&&r.indexOf("WebKit")!=-1;p=!m&&!o&&s.product=="Gecko"}var t=n,u=p,v=o,w;
19
+ a:{var x="",y;if(m&&d.opera){var z=d.opera.version;x=typeof z=="function"?z():z}else{if(u)y=/rv\:([^\);]+)(\)|;)/;else if(t)y=/MSIE\s+([^\);]+)(\)|;)/;else if(v)y=/WebKit\/(\S+)/;if(y){var C=y.exec(q());x=C?C[1]:""}}if(t){var D,E=d.document;D=E?E.documentMode:undefined;if(D>parseFloat(x)){w=String(D);break a}}w=x}var F={};!t||F["9"]||(F["9"]=j(w,"9")>=0);t&&(F["9"]||(F["9"]=j(w,"9")>=0));function G(a){if("textcontent"in a)return a.textContent;else{var b=(a.nodeType==9?a:a.ownerDocument||a.document).createElement("div");b.appendChild(a.cloneNode(true));return b.textContent}}var M="_".split("."),N=d;!(M[0]in N)&&N.execScript&&N.execScript("var "+M[0]);for(var O;M.length&&(O=M.shift());)if(!M.length&&G!==undefined)N[O]=G;else N=N[O]?N[O]:N[O]={};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null}, arguments);}
@@ -0,0 +1,3 @@
1
+ function(){
2
+ return typeof arguments[0].naturalWidth != "undefined" && arguments[0].naturalWidth > 0
3
+ }
@@ -0,0 +1,10 @@
1
+ function(){
2
+ for(var i=0; i<arguments[0].options.length; i++) {
3
+ if ( arguments[0].options[i].label.match(arguments[1]) ) {
4
+ arguments[0].options[i].selected = true;
5
+ if ( arguments[2] == 'single' ) {
6
+ break;
7
+ }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ function(){
2
+ for(var i=0; i<arguments[0].options.length; i++) {
3
+ if ( arguments[0].options[i].text.match(arguments[1]) ) {
4
+ arguments[0].options[i].selected = true;
5
+ if ( arguments[2] == 'single' ) {
6
+ break;
7
+ }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ function(){
2
+ for(var i=0; i<arguments[0].options.length; i++) {
3
+ if ( arguments[0].options[i].value.match(arguments[1]) ) {
4
+ arguments[0].options[i].selected = true;
5
+ if ( arguments[2] == 'single' ) {
6
+ break;
7
+ }
8
+ }
9
+ }
10
+ }
@@ -11,6 +11,9 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
 
14
+ // This code was built by the Selenium code base
15
+ // See https://github.com/SeleniumHQ/selenium/tree/master/javascript/watir-atoms
16
+
14
17
  function(){return function(){function f(a){throw a;}var h=void 0,l=null;function m(a){return function(){return this[a]}}function o(a){return function(){return a}}var p,r=this;
15
18
  function s(a){var b=typeof a;if(b=="object")if(a){if(a instanceof Array)return"array";else if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(c=="[object Window]")return"object";if(c=="[object Array]"||typeof a.length=="number"&&typeof a.splice!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("splice"))return"array";if(c=="[object Function]"||typeof a.call!="undefined"&&typeof a.propertyIsEnumerable!="undefined"&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
16
19
  else if(b=="function"&&typeof a.call=="undefined")return"object";return b}function t(a){var b=s(a);return b=="array"||b=="object"&&typeof a.length=="number"}function u(a){return typeof a=="string"}function aa(a){a=s(a);return a=="object"||a=="array"||a=="function"}var ba="closure_uid_"+Math.floor(Math.random()*2147483648).toString(36),ca=0,da=Date.now||function(){return+new Date};function v(a,b){function c(){}c.prototype=b.prototype;a.Q=b.prototype;a.prototype=new c};function ea(a){this.stack=Error().stack||"";if(a)this.message=String(a)}v(ea,Error);ea.prototype.name="CustomError";function fa(a){for(var b=1;b<arguments.length;b++)var c=String(arguments[b]).replace(/\$/g,"$$$$"),a=a.replace(/\%s/,c);return a}function w(a){if(!ga.test(a))return a;a.indexOf("&")!=-1&&(a=a.replace(ha,"&amp;"));a.indexOf("<")!=-1&&(a=a.replace(ia,"&lt;"));a.indexOf(">")!=-1&&(a=a.replace(ja,"&gt;"));a.indexOf('"')!=-1&&(a=a.replace(ka,"&quot;"));return a}var ha=/&/g,ia=/</g,ja=/>/g,ka=/\"/g,ga=/[&<>\"]/;function la(a,b){if(a<b)return-1;else if(a>b)return 1;return 0}
@@ -0,0 +1,11 @@
1
+ function(){
2
+ var result = [];
3
+ var options = arguments[0].options;
4
+ for (var i = 0; i < options.length; i++) {
5
+ var option = options[i];
6
+ if (option.selected) {
7
+ result.push(option)
8
+ }
9
+ }
10
+ return result;
11
+ }
@@ -0,0 +1,3 @@
1
+ function(){
2
+ arguments[0].value=arguments[1]
3
+ }
@@ -69,7 +69,7 @@ module Watir
69
69
  tag_name = selector.delete(:tag_name)
70
70
  return unless selector.empty? # multiple attributes
71
71
 
72
- element = @query_scope.wd.find_element(:id, id)
72
+ element = locate_element(:id, id)
73
73
  return if tag_name && !element_validator.validate(element, {tag_name: tag_name})
74
74
 
75
75
  element
@@ -98,11 +98,11 @@ module Watir
98
98
  # could build xpath/css for selector
99
99
  if idx || !visible.nil?
100
100
  idx ||= 0
101
- elements = @query_scope.wd.find_elements(how, what)
101
+ elements = locate_elements(how, what)
102
102
  elements = elements.select { |el| visible == el.displayed? } unless visible.nil?
103
103
  elements[idx] unless elements.nil?
104
104
  else
105
- @query_scope.wd.find_element(how, what)
105
+ locate_element(how, what)
106
106
  end
107
107
  else
108
108
  # can't use xpath, probably a regexp in there
@@ -119,6 +119,7 @@ module Watir
119
119
 
120
120
  def find_all_by_one
121
121
  how, what = @selector.to_a.first
122
+ return [what] if how == :element
122
123
  selector_builder.check_type how, what
123
124
 
124
125
  if WD_FINDERS.include?(how)
@@ -138,7 +139,7 @@ module Watir
138
139
 
139
140
  how, what = selector_builder.build(selector)
140
141
  found = if how
141
- @query_scope.wd.find_elements(how, what)
142
+ locate_elements(how, what)
142
143
  else
143
144
  wd_find_by_regexp_selector(selector, :select)
144
145
  end
@@ -148,7 +149,7 @@ module Watir
148
149
 
149
150
  def wd_find_all_by(how, what)
150
151
  if what.is_a? String
151
- @query_scope.wd.find_elements(how, what)
152
+ locate_elements(how, what)
152
153
  else
153
154
  all_elements.select { |element| fetch_value(element, how) =~ what }
154
155
  end
@@ -168,19 +169,19 @@ module Watir
168
169
  end
169
170
 
170
171
  def all_elements
171
- @query_scope.wd.find_elements(xpath: ".//*")
172
+ locate_elements(:xpath, ".//*")
172
173
  end
173
174
 
174
175
  def wd_find_first_by(how, what)
175
176
  if what.is_a? String
176
- @query_scope.wd.find_element(how, what)
177
+ locate_element(how, what)
177
178
  else
178
179
  all_elements.find { |element| fetch_value(element, how) =~ what }
179
180
  end
180
181
  end
181
182
 
182
183
  def wd_find_by_regexp_selector(selector, method = :find)
183
- query_scope = @query_scope.wd
184
+ query_scope = ensure_scope_context
184
185
  rx_selector = delete_regexps_from(selector)
185
186
 
186
187
  if rx_selector.key?(:label) && selector_builder.should_use_label_element?
@@ -207,7 +208,7 @@ module Watir
207
208
  end
208
209
  end
209
210
 
210
- elements = query_scope.find_elements(how, what)
211
+ elements = locate_elements(how, what, query_scope)
211
212
  elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
212
213
  end
213
214
 
@@ -225,7 +226,7 @@ module Watir
225
226
 
226
227
  def label_from_text(label_exp)
227
228
  # TODO: this won't work correctly if @wd is a sub-element
228
- @query_scope.wd.find_elements(:tag_name, 'label').find do |el|
229
+ locate_elements(:tag_name, 'label').find do |el|
229
230
  matches_selector?(el, text: label_exp)
230
231
  end
231
232
  end
@@ -251,6 +252,19 @@ module Watir
251
252
  "contains(#{lhs}, #{XpathSupport.escape(literals)})"
252
253
  end
253
254
  end
255
+
256
+ def ensure_scope_context
257
+ @query_scope.wd
258
+ end
259
+
260
+ def locate_element(how, what)
261
+ @query_scope.wd.find_element(how, what)
262
+ end
263
+
264
+ def locate_elements(how, what, scope = @query_scope.wd)
265
+ scope.find_elements(how, what)
266
+ end
267
+
254
268
  end
255
269
  end
256
270
  end
@@ -0,0 +1,231 @@
1
+ module Watir
2
+ class RadioSet
3
+ extend Forwardable
4
+ include Watir::Exception
5
+ include Enumerable
6
+
7
+ delegate [:exists?, :present?, :visible?, :browser] => :source
8
+
9
+ attr_reader :source, :frame
10
+
11
+ def initialize(query_scope, selector)
12
+ unless selector.kind_of? Hash
13
+ raise ArgumentError, "invalid argument: #{selector.inspect}"
14
+ end
15
+
16
+ @source = Radio.new(query_scope, selector)
17
+ @frame = @source.parent(tag_name: :form)
18
+ end
19
+
20
+ #
21
+ # Yields each Radio associated with this set.
22
+ #
23
+ # @example
24
+ # radio_set = browser.radio_set
25
+ # radio_set.each do |radio|
26
+ # puts radio.text
27
+ # end
28
+ #
29
+ # @yieldparam [Watir::RadioSet] element iterate through the radio buttons.
30
+ #
31
+
32
+ def each(&block)
33
+ radios.each(&block)
34
+ end
35
+
36
+ #
37
+ # Get the n'th radio button in this set
38
+ #
39
+ # @return Watir::Radio
40
+ #
41
+
42
+ def [](idx)
43
+ radios[idx]
44
+ end
45
+
46
+ #
47
+ # @return Watir::Radio
48
+ #
49
+
50
+ def radio(opt = {})
51
+ n = name
52
+ if !n.empty? && (!opt[:name] || opt[:name] == n)
53
+ frame.radio(opt.merge name: n)
54
+ elsif n.empty?
55
+ return source
56
+ else
57
+ raise Watir::Exception::UnknownObjectException, "#{opt[:name]} does not match name of RadioSet: #{n}"
58
+ end
59
+
60
+ end
61
+
62
+ #
63
+ # @return Watir::RadioCollection
64
+ #
65
+
66
+ def radios(opt = {})
67
+ n = name
68
+ if !n.empty? && (!opt[:name] || opt[:name] == n)
69
+ element_call(:wait_for_present) { frame.radios(opt.merge name: n) }
70
+ elsif n.empty?
71
+ Watir::RadioCollection.new(frame, element: source.wd)
72
+ else
73
+ raise Watir::Exception::UnknownObjectException, "#{opt[:name]} does not match name of RadioSet: #{n}"
74
+ end
75
+ end
76
+
77
+ #
78
+ # Returns true if any radio buttons in the set are enabled.
79
+ #
80
+ # @return [Boolean]
81
+ #
82
+
83
+ def enabled?
84
+ any?(&:enabled?)
85
+ end
86
+
87
+ #
88
+ # Returns true if all radio buttons in the set are disabled.
89
+ #
90
+ # @return [Boolean]
91
+ #
92
+
93
+ def disabled?
94
+ !enabled?
95
+ end
96
+
97
+ #
98
+ # Returns the name attribute for the set.
99
+ #
100
+ # @return [String]
101
+ #
102
+
103
+ def name
104
+ @name ||= source.name
105
+ end
106
+
107
+ #
108
+ # If RadioSet exists, this always returns 'radio'.
109
+ #
110
+ # @return [String]
111
+ #
112
+
113
+ def type
114
+ source.send :assert_exists
115
+ 'radio'
116
+ end
117
+
118
+ #
119
+ # Returns true if the radio set has one or more radio buttons where label matches the given value.
120
+ #
121
+ # @param [String, Regexp] str_or_rx
122
+ # @return [Boolean]
123
+ #
124
+
125
+ def include?(str_or_rx)
126
+ radio(label: str_or_rx).exist?
127
+ end
128
+
129
+ #
130
+ # Select the radio button whose value or label matches the given string.
131
+ #
132
+ # @param [String, Regexp] str_or_rx
133
+ # @raise [Watir::Exception::UnknownObjectException] if the Radio does not exist.
134
+ # @return [String] The value or text of the radio selected.
135
+ #
136
+
137
+ def select(str_or_rx)
138
+ found_by_value = radio(value: str_or_rx)
139
+ found_by_text = radio(label: str_or_rx)
140
+
141
+ if found_by_value.exist?
142
+ found_by_value.click unless found_by_value.selected?
143
+ return found_by_value.value
144
+ elsif found_by_text.exist?
145
+ found_by_text.click unless found_by_text.selected?
146
+ return found_by_text.text
147
+ else
148
+ raise UnknownObjectException, "Unable to locate radio matching #{str_or_rx.inspect}"
149
+ end
150
+ end
151
+
152
+ #
153
+ # Returns true if any of the radio button label matches the given value.
154
+ #
155
+ # @param [String, Regexp] str_or_rx
156
+ # @raise [Watir::Exception::UnknownObjectException] if the options do not exist
157
+ # @return [Boolean]
158
+ #
159
+
160
+ def selected?(str_or_rx)
161
+ found = frame.radio(label: str_or_rx)
162
+
163
+ return found.selected? if found.exist?
164
+ raise UnknownObjectException, "Unable to locate radio matching #{str_or_rx.inspect}"
165
+ end
166
+
167
+ #
168
+ # Returns the value of the selected radio button in the set.
169
+ # Returns nil if no radio is selected.
170
+ #
171
+ # @return [String, nil]
172
+ #
173
+
174
+ def value
175
+ sel = selected
176
+ sel && sel.value
177
+ end
178
+
179
+ #
180
+ # Returns the text of the selected radio button in the set.
181
+ # Returns nil if no option is selected.
182
+ #
183
+ # @return [String, nil]
184
+ #
185
+
186
+ def text
187
+ sel = selected
188
+ sel && sel.text
189
+ end
190
+
191
+ #
192
+ # Returns the selected Radio element.
193
+ # Returns nil if no radio button is selected.
194
+ #
195
+ # @return [Watir::Radio, nil]
196
+ #
197
+
198
+ def selected
199
+ find(&:selected?)
200
+ end
201
+
202
+ #
203
+ # Returns true if two elements are equal.
204
+ #
205
+ # @example
206
+ # browser.radio_set(id: 'new_user_newsletter_yes') == browser.radio_set(id: 'new_user_newsletter_no')
207
+ # #=> true
208
+ #
209
+
210
+ def ==(other)
211
+ return false unless other.kind_of?(self.class)
212
+ radios == other.radios
213
+ end
214
+ alias_method :eql?, :==
215
+
216
+ private
217
+
218
+ def element_call(*args, &blk)
219
+ source.send :element_call, *args, &blk
220
+ end
221
+ end # RadioSet
222
+
223
+ module Container
224
+ def radio_set(*args)
225
+ RadioSet.new(self, extract_selector(args).merge(tag_name: "input", type: "radio"))
226
+ end
227
+
228
+ Watir.tag_to_class[:radio_set] = RadioSet
229
+ end # Container
230
+
231
+ end # Watir