watir-webdriver 0.0.7 → 0.0.8

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 (111) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +48 -0
  3. data/Rakefile +23 -8
  4. data/VERSION +1 -1
  5. data/lib/watir-webdriver.rb +7 -5
  6. data/lib/watir-webdriver/browser.rb +9 -2
  7. data/lib/watir-webdriver/browserbot.js +5 -2
  8. data/lib/watir-webdriver/container.rb +6 -9
  9. data/lib/watir-webdriver/{collections/element_collection.rb → element_collection.rb} +5 -0
  10. data/lib/watir-webdriver/elements/button.rb +5 -4
  11. data/lib/watir-webdriver/{element.rb → elements/element.rb} +29 -5
  12. data/lib/watir-webdriver/elements/file_field.rb +21 -4
  13. data/lib/watir-webdriver/elements/frame.rb +1 -1
  14. data/lib/watir-webdriver/elements/generated.rb +750 -750
  15. data/lib/watir-webdriver/elements/image.rb +6 -0
  16. data/lib/watir-webdriver/elements/link.rb +0 -4
  17. data/lib/watir-webdriver/elements/select.rb +22 -24
  18. data/lib/watir-webdriver/elements/table_row.rb +11 -1
  19. data/lib/watir-webdriver/elements/text_field.rb +5 -15
  20. data/lib/watir-webdriver/extensions/wait.rb +107 -0
  21. data/lib/watir-webdriver/html/generator.rb +1 -1
  22. data/lib/watir-webdriver/html/util.rb +1 -10
  23. data/lib/watir-webdriver/html/visitor.rb +2 -14
  24. data/lib/watir-webdriver/locators/button_locator.rb +22 -3
  25. data/lib/watir-webdriver/locators/element_locator.rb +54 -28
  26. data/lib/watir-webdriver/locators/table_row_locator.rb +3 -1
  27. data/lib/watir-webdriver/locators/text_field_locator.rb +7 -3
  28. data/lib/watir-webdriver/window_switching.rb +78 -0
  29. data/spec/browser_spec.rb +43 -0
  30. data/spec/container_spec.rb +43 -0
  31. data/spec/element_locator_spec.rb +305 -0
  32. data/spec/element_spec.rb +34 -5
  33. data/spec/html/closeable.html +13 -0
  34. data/spec/html/data_attributes.html +9 -0
  35. data/spec/html/wait.html +27 -0
  36. data/spec/html/window_switching.html +12 -0
  37. data/spec/input_spec.rb +14 -2
  38. data/spec/locator_spec_helper.rb +37 -0
  39. data/spec/spec_helper.rb +3 -0
  40. data/spec/wait_spec.rb +98 -0
  41. data/spec/watirspec/area_spec.rb +4 -4
  42. data/spec/watirspec/browser_spec.rb +37 -35
  43. data/spec/watirspec/button_spec.rb +22 -17
  44. data/spec/watirspec/buttons_spec.rb +1 -1
  45. data/spec/watirspec/checkbox_spec.rb +4 -0
  46. data/spec/watirspec/checkboxes_spec.rb +1 -1
  47. data/spec/watirspec/collections_spec.rb +4 -2
  48. data/spec/watirspec/dd_spec.rb +4 -0
  49. data/spec/watirspec/dds_spec.rb +1 -1
  50. data/spec/watirspec/del_spec.rb +4 -0
  51. data/spec/watirspec/dels_spec.rb +1 -1
  52. data/spec/watirspec/div_spec.rb +38 -12
  53. data/spec/watirspec/divs_spec.rb +1 -1
  54. data/spec/watirspec/dl_spec.rb +20 -6
  55. data/spec/watirspec/dls_spec.rb +1 -1
  56. data/spec/watirspec/dt_spec.rb +4 -0
  57. data/spec/watirspec/dts_spec.rb +1 -1
  58. data/spec/watirspec/em_spec.rb +4 -0
  59. data/spec/watirspec/ems_spec.rb +1 -1
  60. data/spec/watirspec/filefield_spec.rb +37 -2
  61. data/spec/watirspec/filefields_spec.rb +1 -1
  62. data/spec/watirspec/font_spec.rb +4 -0
  63. data/spec/watirspec/form_spec.rb +4 -0
  64. data/spec/watirspec/forms_spec.rb +6 -4
  65. data/spec/watirspec/frame_spec.rb +6 -0
  66. data/spec/watirspec/hidden_spec.rb +5 -4
  67. data/spec/watirspec/hiddens_spec.rb +1 -1
  68. data/spec/watirspec/hn_spec.rb +4 -0
  69. data/spec/watirspec/image_spec.rb +4 -0
  70. data/spec/watirspec/images_spec.rb +1 -1
  71. data/spec/watirspec/ins_spec.rb +4 -0
  72. data/spec/watirspec/inses_spec.rb +1 -1
  73. data/spec/watirspec/label_spec.rb +4 -0
  74. data/spec/watirspec/labels_spec.rb +2 -2
  75. data/spec/watirspec/li_spec.rb +4 -0
  76. data/spec/watirspec/link_spec.rb +15 -10
  77. data/spec/watirspec/links_spec.rb +6 -0
  78. data/spec/watirspec/lis_spec.rb +1 -1
  79. data/spec/watirspec/map_spec.rb +4 -0
  80. data/spec/watirspec/maps_spec.rb +1 -1
  81. data/spec/watirspec/meta_spec.rb +4 -0
  82. data/spec/watirspec/metas_spec.rb +1 -1
  83. data/spec/watirspec/ol_spec.rb +23 -9
  84. data/spec/watirspec/option_spec.rb +19 -7
  85. data/spec/watirspec/p_spec.rb +4 -0
  86. data/spec/watirspec/pre_spec.rb +4 -0
  87. data/spec/watirspec/pres_spec.rb +1 -1
  88. data/spec/watirspec/ps_spec.rb +1 -1
  89. data/spec/watirspec/radio_spec.rb +22 -13
  90. data/spec/watirspec/radios_spec.rb +6 -0
  91. data/spec/watirspec/select_list_spec.rb +29 -17
  92. data/spec/watirspec/select_lists_spec.rb +6 -0
  93. data/spec/watirspec/span_spec.rb +10 -2
  94. data/spec/watirspec/spans_spec.rb +1 -1
  95. data/spec/watirspec/strong_spec.rb +14 -4
  96. data/spec/watirspec/table_body_spec.rb +4 -0
  97. data/spec/watirspec/table_cell_spec.rb +9 -3
  98. data/spec/watirspec/table_cells_spec.rb +0 -1
  99. data/spec/watirspec/table_footer_spec.rb +4 -0
  100. data/spec/watirspec/table_header_spec.rb +4 -0
  101. data/spec/watirspec/table_row_spec.rb +15 -7
  102. data/spec/watirspec/table_rows_spec.rb +2 -2
  103. data/spec/watirspec/table_spec.rb +13 -7
  104. data/spec/watirspec/tables_spec.rb +1 -1
  105. data/spec/watirspec/text_field_spec.rb +21 -12
  106. data/spec/watirspec/text_fields_spec.rb +6 -0
  107. data/spec/watirspec/ul_spec.rb +4 -0
  108. data/spec/window_switching_spec.rb +116 -0
  109. data/watir-webdriver.gemspec +38 -16
  110. metadata +51 -27
  111. data/lib/watir-webdriver/collections/table_row_collection.rb +0 -11
@@ -42,4 +42,10 @@ module Watir
42
42
  end
43
43
 
44
44
  end # Image
45
+
46
+ module Container
47
+ alias_method :image, :img
48
+ alias_method :images, :imgs
49
+ end # Container
50
+
45
51
  end # Watir
@@ -1,9 +1,5 @@
1
1
  # encoding: utf-8
2
2
  module Watir
3
- class Anchor
4
- alias_method :url, :href # deprecate?
5
- end # Anchor
6
-
7
3
  module Container
8
4
  alias_method :link, :a
9
5
  alias_method :links, :as
@@ -56,7 +56,7 @@ module Watir
56
56
  #
57
57
 
58
58
  def select(str_or_rx)
59
- select_by :text, str_or_rx, multiple?
59
+ select_by :text, str_or_rx
60
60
  end
61
61
 
62
62
  #
@@ -70,7 +70,7 @@ module Watir
70
70
  #
71
71
 
72
72
  def select_value(str_or_rx)
73
- select_by :value, str_or_rx, multiple?
73
+ select_by :value, str_or_rx
74
74
  end
75
75
 
76
76
  #
@@ -116,25 +116,25 @@ module Watir
116
116
 
117
117
  private
118
118
 
119
- def select_by(how, str_or_rx, multiple)
119
+ def select_by(how, str_or_rx)
120
+ assert_exists
121
+
120
122
  case str_or_rx
121
123
  when String, Numeric
122
- select_by_string(how, str_or_rx.to_s, multiple)
124
+ select_by_string(how, str_or_rx.to_s)
123
125
  when Regexp
124
- select_by_regexp(how, str_or_rx, multiple)
126
+ select_by_regexp(how, str_or_rx)
125
127
  else
126
- raise ArgumentError, "expected String or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}"
128
+ raise TypeError, "expected String or Regexp, got #{str_or_rx.inspect}:#{str_or_rx.class}"
127
129
  end
128
130
  end
129
131
 
130
- def select_by_string(how, string, multiple)
132
+ def select_by_string(how, string)
131
133
  xpath = option_xpath_for(how, string)
132
- if multiple
133
- elements = @element.find_elements(:xpath, xpath)
134
134
 
135
- if elements.empty?
136
- raise NoValueFoundException, "#{string.inspect} not found in select list"
137
- end
135
+ if multiple?
136
+ elements = @element.find_elements(:xpath, xpath)
137
+ no_value_found(string) if elements.empty?
138
138
 
139
139
  elements.each { |e| e.select unless e.selected? }
140
140
  elements.first.text
@@ -142,7 +142,7 @@ module Watir
142
142
  begin
143
143
  e = @element.find_element(:xpath, xpath)
144
144
  rescue WebDriver::Error::NoSuchElementError
145
- raise NoValueFoundException, "#{string.inspect} not found in select list"
145
+ no_value_found(string)
146
146
  end
147
147
 
148
148
  e.select unless e.selected?
@@ -151,29 +151,23 @@ module Watir
151
151
  end
152
152
  end
153
153
 
154
- def select_by_regexp(how, exp, multiple)
154
+ def select_by_regexp(how, exp)
155
155
  elements = @element.find_elements(:tag_name, 'option')
156
- if elements.empty?
157
- raise NoValueFoundException, "no options in select list"
158
- end
156
+ no_value_found(nil, "no options in select list") if elements.empty?
159
157
 
160
- if multiple
158
+ if multiple?
161
159
  found = elements.select do |e|
162
160
  next unless matches_regexp?(how, e, exp)
163
161
  e.select unless e.selected?
164
162
  true
165
163
  end
166
164
 
167
- if found.empty?
168
- raise NoValueFoundException, "#{exp.inspect} not found in select list"
169
- end
165
+ no_value_found(exp) if found.empty?
170
166
 
171
167
  found.first.text
172
168
  else
173
169
  element = elements.find { |e| matches_regexp?(how, e, exp) }
174
- unless element
175
- raise NoValueFoundException, "#{exp.inspect} not found in select list"
176
- end
170
+ no_value_found(exp) unless element
177
171
 
178
172
  element.select unless element.selected?
179
173
 
@@ -210,6 +204,10 @@ module Watir
210
204
 
211
205
  ''
212
206
  end
207
+
208
+ def no_value_found(arg, msg = nil)
209
+ raise NoValueFoundException, msg || "#{arg.inspect} not found in select list"
210
+ end
213
211
  end # Select
214
212
 
215
213
  module Container
@@ -23,4 +23,14 @@ module Watir
23
23
  end
24
24
 
25
25
  end # TableRow
26
- end # Watir
26
+
27
+
28
+ class TableRowCollection < ElementCollection
29
+ private
30
+
31
+ def locator_class
32
+ @parent.kind_of?(Watir::Table) ? TableRowLocator : super
33
+ end
34
+
35
+ end # TableRowCollection
36
+ end # Watir
@@ -3,6 +3,7 @@ module Watir
3
3
  class TextField < Input
4
4
 
5
5
  attributes Watir::TextArea.typed_attributes
6
+ remove_method :type # we want Input#type here, which was overriden by TextArea's attributes
6
7
 
7
8
  def self.from(parent, element)
8
9
  type = element.attribute(:type)
@@ -14,14 +15,6 @@ module Watir
14
15
  super
15
16
  end
16
17
 
17
- # hacky, but we want Input#type here, which was overriden by TextArea's attributes
18
- # so we're *overwriting* that method definition here
19
- def type; super; end
20
-
21
- def inspect
22
- '#<%s:0x%x located=%s selector=%s>' % [self.class, hash*2, !!@element, selector_string]
23
- end
24
-
25
18
  #
26
19
  # Clear the element, the type in the given value.
27
20
  #
@@ -73,13 +66,10 @@ module Watir
73
66
  end
74
67
 
75
68
  def selector_string
76
- selector_without_type.inspect
77
- end
78
-
79
- def selector_without_type
80
- s = @selector.dup
81
- s[:type] = '(any text type)'
82
- s
69
+ selector = @selector.dup
70
+ selector[:type] = '(any text type)'
71
+ selector[:tag_name] = "input or textarea"
72
+ selector.inspect
83
73
  end
84
74
  end
85
75
 
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ module Watir
3
+ module Wait
4
+ module_function
5
+
6
+ class TimeoutError < StandardError
7
+ end
8
+
9
+ #
10
+ # Wait until the block evaluates to true or times out.
11
+ #
12
+
13
+ def until(timeout = 30, &block)
14
+ end_time = ::Time.now + timeout
15
+
16
+ until ::Time.now > end_time
17
+ result = yield(self)
18
+ return result if result
19
+ sleep 0.5
20
+ end
21
+
22
+ raise TimeoutError, "timed out after #{timeout} seconds"
23
+ end
24
+
25
+ #
26
+ # Wait while the block evaluates to true or times out.
27
+ #
28
+
29
+ def while(timeout = 30, &block)
30
+ end_time = ::Time.now + timeout
31
+
32
+ until ::Time.now > end_time
33
+ return unless yield(self)
34
+ sleep 0.5
35
+ end
36
+
37
+ raise TimeoutError, "timed out after #{timeout} seconds"
38
+ end
39
+
40
+ end # Wait
41
+
42
+ #
43
+ # Wraps an Element so that any subsequent method calls are
44
+ # put on hold until the element is present (exists and is visible) on the page.
45
+ #
46
+
47
+ class WhenPresentDecorator
48
+ def initialize(element, timeout)
49
+ @element = element
50
+ @timeout = timeout
51
+ end
52
+
53
+ def method_missing(m, *args, &block)
54
+ unless @element.respond_to?(m)
55
+ raise NoMethodError, "undefined method `#{m}' for #{@element.inspect}:#{@element.class}"
56
+ end
57
+
58
+ Watir::Wait.until(@timeout) { @element.present? }
59
+
60
+ @element.__send__(m, *args, &block)
61
+ end
62
+ end # WhenPresentDecorator
63
+
64
+ class Element
65
+
66
+ #
67
+ # Returns true if the element exists and is visible on the page
68
+ #
69
+
70
+ def present?
71
+ exists? && visible?
72
+ end
73
+
74
+ #
75
+ # Waits until the element is present.
76
+ #
77
+ # Optional argument:
78
+ #
79
+ # timeout - seconds to wait before timing out (default: 30)
80
+ #
81
+ # browser.button(:id, 'foo').when_present.click
82
+ # browser.div(:id, 'bar').when_present { |div| ... }
83
+ # browser.p(:id, 'baz').when_present(60).text
84
+ #
85
+
86
+ def when_present(timeout = 30)
87
+ if block_given?
88
+ Watir::Wait.until(timeout) { self.present? }
89
+ yield self
90
+ else
91
+ WhenPresentDecorator.new(self, timeout)
92
+ end
93
+ end
94
+
95
+ def wait_until_present(timeout = 30)
96
+ Watir::Wait.until(timeout) { self.present? }
97
+ end
98
+
99
+ def wait_while_present(timeout = 30)
100
+ Watir::Wait.while(timeout) { self.present? }
101
+ rescue Selenium::WebDriver::Error::ObsoleteElementError
102
+ # it's not present
103
+ end
104
+ end # Element
105
+
106
+
107
+ end # Watir
@@ -104,7 +104,7 @@ CODE
104
104
 
105
105
  def indent(code, indent = 1)
106
106
  indent_string = " "*indent
107
- code.split("\n").map { |line| indent_string + line }.join("\n")
107
+ code.split("\n").map { |line| line.empty? ? line : indent_string + line }.join("\n")
108
108
  end
109
109
 
110
110
  end # Generator
@@ -4,7 +4,6 @@ module Watir
4
4
 
5
5
  module_function
6
6
 
7
-
8
7
  def classify(name)
9
8
  if name =~ /^HTML(.+)Element$/
10
9
  $1
@@ -13,16 +12,8 @@ module Watir
13
12
  end
14
13
  end
15
14
 
16
- SPECIALS = {
17
- 'img' => 'image'
18
- }
19
-
20
15
  def paramify(str)
21
- if SPECIALS.has_key?(str)
22
- SPECIALS[str]
23
- else
24
- classify(str).snake_case
25
- end
16
+ classify(str).snake_case
26
17
  end
27
18
 
28
19
  end # Util
@@ -111,22 +111,10 @@ module Watir
111
111
 
112
112
  attrs = Hash.new { |hash, key| hash[key] = [] }
113
113
  attributes.each do |a|
114
- attrs[ruby_type_for(a.type)] << a.name.snake_case
114
+ attrs[ruby_type_for(a.type)] << a.name.snake_case.to_sym
115
115
  end
116
116
 
117
- call :attributes, [[:hash] + attrs.map { |type, names| [[:lit, type], literal_array(names)] }.flatten(1)]
118
- end
119
-
120
- def identifier_call(tag_name)
121
- call :identifier, [literal_hash(:tag_name => tag_name)]
122
- end
123
-
124
- def container_call(name)
125
- call :container_method, [[:lit, Util.paramify(name).to_sym]]
126
- end
127
-
128
- def collection_call(name)
129
- call :collection_method, [[:lit, Util.paramify(name).pluralize.to_sym]]
117
+ call :attributes, [literal_hash(attrs)]
130
118
  end
131
119
 
132
120
  def literal_hash(hash)
@@ -1,10 +1,23 @@
1
1
  module Watir
2
2
  class ButtonLocator < ElementLocator
3
3
 
4
+ VALID_TYPES = %w[button reset submit image]
5
+
4
6
  def locate_all
5
7
  find_all_by_multiple
6
8
  end
7
9
 
10
+ private
11
+
12
+ def wd_find_first_by(how, what)
13
+ if how == :tag_name
14
+ how = :xpath
15
+ what = ".//button | .//input[#{attribute_expression :type => VALID_TYPES}]"
16
+ end
17
+
18
+ super
19
+ end
20
+
8
21
  def build_xpath(selectors)
9
22
  return if selectors.values.any? { |e| e.kind_of? Regexp }
10
23
 
@@ -14,7 +27,7 @@ module Watir
14
27
  button_attr_exp = attribute_expression(selectors)
15
28
 
16
29
  @building = :input
17
- selectors[:type] = %w[button reset submit image]
30
+ selectors[:type] = VALID_TYPES
18
31
  input_attr_exp = attribute_expression(selectors)
19
32
 
20
33
  xpath = ".//button"
@@ -37,7 +50,7 @@ module Watir
37
50
  end
38
51
  end
39
52
 
40
- def matches_selector?(rx_selector, element)
53
+ def matches_selector?(element, rx_selector)
41
54
  rx_selector = rx_selector.dup
42
55
 
43
56
  [:value, :caption].each do |key|
@@ -53,5 +66,11 @@ module Watir
53
66
  def tag_name_matches?(element, _)
54
67
  !!(/^(input|button)$/ === element.tag_name)
55
68
  end
69
+
70
+ def validate_element(element)
71
+ return if element.tag_name == "input" && !VALID_TYPES.include?(element.attribute(:type))
72
+ super
73
+ end
74
+
56
75
  end # ButtonLocator
57
- end # Watir
76
+ end # Watir
@@ -30,10 +30,14 @@ module Watir
30
30
  end
31
31
 
32
32
  if @selector.size == 1
33
- find_first_by_one
33
+ element = find_first_by_one
34
34
  else
35
- find_first_by_multiple
35
+ element = find_first_by_multiple
36
36
  end
37
+
38
+ # this actually only applies when finding by xpath - browser.text_field(:xpath, "//input[@type='radio']")
39
+ # we don't need to validate the element if we built the xpath ourselves.
40
+ validate_element(element) if element
37
41
  rescue WebDriver::Error::NoSuchElementError => wde
38
42
  nil
39
43
  end
@@ -46,25 +50,27 @@ module Watir
46
50
  end
47
51
  end
48
52
 
53
+ private
54
+
49
55
  def find_first_by_one
50
- how, what = @selector.shift
56
+ how, what = @selector.to_a.first
51
57
  check_type how, what
52
58
 
53
59
  if WD_FINDERS.include?(how)
54
60
  wd_find_first_by(how, what)
55
61
  else
56
- raise NotImplementedError, "find first by attribute/other"
62
+ find_first_by_multiple
57
63
  end
58
64
  end
59
65
 
60
66
  def find_all_by_one
61
- how, what = @selector.shift
67
+ how, what = @selector.to_a.first
62
68
  check_type how, what
63
69
 
64
70
  if WD_FINDERS.include?(how)
65
71
  wd_find_all_by(how, what)
66
72
  else
67
- raise NotImplementedError, "find all by attribute/other"
73
+ find_all_by_multiple
68
74
  end
69
75
  end
70
76
 
@@ -95,7 +101,7 @@ module Watir
95
101
  selector = normalized_selector
96
102
 
97
103
  if selector.has_key? :index
98
- raise Error, "can't locate all elements by :index"
104
+ raise ArgumentError, "can't locate all elements by :index"
99
105
  end
100
106
 
101
107
  xpath = selector[:xpath] || build_xpath(selector)
@@ -110,7 +116,7 @@ module Watir
110
116
  if what.kind_of? String
111
117
  @wd.find_element(how, what)
112
118
  else
113
- all_elements.find { |e| fetch_value(how, e) =~ what }
119
+ all_elements.find { |element| fetch_value(element, how) =~ what }
114
120
  end
115
121
  end
116
122
 
@@ -118,7 +124,7 @@ module Watir
118
124
  if what.kind_of? String
119
125
  @wd.find_elements(how, what)
120
126
  else
121
- all_elements.select { |e| fetch_value(how, e) =~ what }
127
+ all_elements.select { |element| fetch_value(element, how) =~ what }
122
128
  end
123
129
  end
124
130
 
@@ -126,45 +132,55 @@ module Watir
126
132
  rx_selector = delete_regexps_from(selector)
127
133
 
128
134
  if rx_selector.has_key?(:label) && should_use_label_element?
129
- label_exp = rx_selector.delete(:label)
130
- label = @wd.find_elements(:tag_name, 'label').find { |e| matches_selector?({:text => label_exp}, e) } || return
131
-
132
- selector[:id] = label.attribute(:for)
135
+ selector[:id] = id_from_label(rx_selector.delete(:label)) || return
133
136
  end
134
137
 
135
- xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{@selector.inspect}")
138
+ xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{selector.inspect}")
136
139
 
137
140
  elements = @wd.find_elements(:xpath, xpath)
138
- elements.send(method) { |e| matches_selector?(rx_selector, e) }
141
+ elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
139
142
  end
140
143
 
144
+ VALID_WHATS = [String, Regexp]
145
+
141
146
  def check_type(how, what)
142
147
  case how
143
148
  when :index
144
- raise TypeError, "expected Fixnum, got #{what.class}" unless what.kind_of?(Fixnum)
149
+ unless what.kind_of?(Fixnum)
150
+ raise TypeError, "expected Fixnum, got #{what.inspect}:#{what.class}"
151
+ end
145
152
  else
146
- unless [String, Regexp].any? { |t| what.kind_of? t }
147
- raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
153
+ unless VALID_WHATS.any? { |t| what.kind_of? t }
154
+ raise TypeError, "expected one of #{VALID_WHATS.inspect}, got #{what.inspect}:#{what.class}"
148
155
  end
149
156
  end
150
157
  end
151
158
 
152
- def fetch_value(how, element)
159
+ def id_from_label(label_exp)
160
+ # TODO: this won't work correctly if @wd is a sub-element
161
+ label = @wd.find_elements(:tag_name, 'label').find do |el|
162
+ matches_selector?(el, :text => label_exp)
163
+ end
164
+
165
+ label.attribute(:for) if label
166
+ end
167
+
168
+ def fetch_value(element, how)
153
169
  case how
154
170
  when :text
155
171
  element.text
156
172
  when :tag_name
157
173
  element.tag_name
174
+ when :href
175
+ (href = element.attribute(:href)) && href.strip
158
176
  else
159
177
  element.attribute(how)
160
178
  end
161
179
  end
162
180
 
163
- def matches_selector?(selector, element)
164
- # p :start => selector
181
+ def matches_selector?(element, selector)
165
182
  selector.all? do |how, what|
166
- # p :comparing => [how, what], :to => fetch_value(how, element)
167
- what === fetch_value(how, element)
183
+ what === fetch_value(element, how)
168
184
  end
169
185
  end
170
186
 
@@ -183,13 +199,12 @@ module Watir
183
199
 
184
200
  def normalize_selector(how, what)
185
201
  case how
186
- when :url
187
- [:href, what]
188
202
  when :caption
189
203
  [:text, what]
190
204
  when :class_name
191
205
  [:class, what]
192
- when :tag_name, :text, :xpath, :index, :class # include class since the attribute method is 'class_name'
206
+ when :tag_name, :text, :xpath, :index, :class
207
+ # include class since the valid attribute is 'class_name'
193
208
  [how, what]
194
209
  else
195
210
  assert_valid_as_attribute how
@@ -210,7 +225,7 @@ module Watir
210
225
  end
211
226
 
212
227
  def assert_valid_as_attribute(attribute)
213
- unless valid_attribute? attribute
228
+ unless valid_attribute? attribute or attribute.to_s =~ /^data_.+$/
214
229
  raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
215
230
  end
216
231
  end
@@ -272,7 +287,7 @@ module Watir
272
287
  def attribute_expression(selectors)
273
288
  selectors.map do |key, val|
274
289
  if val.kind_of?(Array)
275
- "( " + val.map { |v| equal_pair(key, v) }.join(" or ") + " )"
290
+ "(" + val.map { |v| equal_pair(key, v) }.join(" or ") + ")"
276
291
  else
277
292
  equal_pair(key, val)
278
293
  end
@@ -300,5 +315,16 @@ module Watir
300
315
  end
301
316
  end
302
317
 
318
+ def validate_element(element)
319
+ tn = @selector[:tag_name]
320
+ return if tn && !tag_name_matches?(element, tn)
321
+
322
+ if element.tag_name == 'input'
323
+ return if @selector[:type] && @selector[:type] != element.attribute(:type)
324
+ end
325
+
326
+ element
327
+ end
328
+
303
329
  end # ElementLocator
304
330
  end # Watir