Class: Watir::ElementLocator

Inherits:
Object
  • Object
show all
Includes:
Selenium, Exception
Defined in:
lib/watir-webdriver/locators/element_locator.rb

Direct Known Subclasses

ButtonLocator, TableRowLocator, TextFieldLocator

Constant Summary collapse

WD_FINDERS =
[ 
  :class, 
  :class_name, 
  :css,
  :id, 
  :link,
  :link_text, 
  :name, 
  :partial_link_text, 
  :tag_name, 
  :xpath
]

Instance Method Summary collapse

Constructor Details

#initialize(wd, selector, valid_attributes) ⇒ ElementLocator

Returns a new instance of ElementLocator.



20
21
22
23
24
# File 'lib/watir-webdriver/locators/element_locator.rb', line 20

def initialize(wd, selector, valid_attributes)
  @wd               = wd
  @selector         = selector.dup
  @valid_attributes = valid_attributes
end

Instance Method Details

#assert_valid_as_attribute(attribute) ⇒ Object



204
205
206
207
208
# File 'lib/watir-webdriver/locators/element_locator.rb', line 204

def assert_valid_as_attribute(attribute)
  if @valid_attributes && !@valid_attributes.include?(attribute)
    raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
  end
end

#attribute_expression(selectors) ⇒ Object



252
253
254
255
256
257
258
259
260
# File 'lib/watir-webdriver/locators/element_locator.rb', line 252

def attribute_expression(selectors)
  selectors.map do |key, val|
    if val.kind_of?(Array)
      "( " + val.map { |v| equal_pair(key, v) }.join(" or ") + " )"
    else
      equal_pair(key, val)
    end
  end.join(" and ")
end

#build_xpath(selectors) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/watir-webdriver/locators/element_locator.rb', line 230

def build_xpath(selectors)
  return if selectors.values.any? { |e| e.kind_of? Regexp }

  xpath = ".//"
  xpath << (selectors.delete(:tag_name) || '*').to_s

  idx = selectors.delete(:index)

  # the remaining entries should be attributes
  unless selectors.empty?
    xpath << "[" << attribute_expression(selectors) << "]"
  end

  if idx
    xpath << "[#{idx + 1}]"
  end

  p :xpath => xpath, :selectors => selectors if $DEBUG

  xpath
end

#by_idObject



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/watir-webdriver/locators/element_locator.rb', line 210

def by_id
  selector = @selector.dup
  id       = selector.delete(:id)
  return unless id && id.kind_of?(String)

  tag_name = selector.delete(:tag_name)
  return unless selector.empty? # multiple attributes

  element  = @wd.find_element(:id, id)
  return if tag_name && !tag_name_matches?(element, tag_name)

  element
rescue WebDriver::Error::NoSuchElementError => wde
  nil
end

#check_type(how, what) ⇒ Object



133
134
135
136
137
138
139
140
141
142
# File 'lib/watir-webdriver/locators/element_locator.rb', line 133

def check_type(how, what)
  case how
  when :index
    raise TypeError, "expected Fixnum, got #{what.class}" unless what.kind_of?(Fixnum)
  else
    unless [String, Regexp].any? { |t| what.kind_of? t }
      raise TypeError, "expected String or Regexp, got #{what.inspect}:#{what.class}"
    end
  end
end

#delete_regexps_from(selector) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/watir-webdriver/locators/element_locator.rb', line 192

def delete_regexps_from(selector)
  rx_selector = {}

  selector.dup.each do |how, what|
    next unless what.kind_of?(Regexp)
    rx_selector[how] = what
    selector.delete how
  end

  rx_selector
end

#equal_pair(key, value) ⇒ Object



262
263
264
# File 'lib/watir-webdriver/locators/element_locator.rb', line 262

def equal_pair(key, value)
  "#{lhs_for(key)}='#{value}'"
end

#fetch_value(how, element) ⇒ Object



144
145
146
147
148
149
150
151
152
153
# File 'lib/watir-webdriver/locators/element_locator.rb', line 144

def fetch_value(how, element)
  case how
  when :text
    element.text
  when :tag_name
    element.tag_name
  else
    element.attribute(how)
  end
end

#find_all_by_multipleObject



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/watir-webdriver/locators/element_locator.rb', line 94

def find_all_by_multiple
  selector = normalized_selector

  if selector.has_key? :index
    raise Error, "can't locate all elements by :index"
  end

  xpath = selector[:xpath] || build_xpath(selector)
  if xpath
    @wd.find_elements(:xpath, xpath)
  else
    wd_find_by_regexp_selector(selector, :select)
  end
end

#find_all_by_oneObject



60
61
62
63
64
65
66
67
68
69
# File 'lib/watir-webdriver/locators/element_locator.rb', line 60

def find_all_by_one
  how, what = @selector.shift
  check_type how, what

  if WD_FINDERS.include?(how)
    wd_find_all_by(how, what)
  else
    raise NotImplementedError, "find all by attribute/other"
  end
end

#find_first_by_multipleObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/watir-webdriver/locators/element_locator.rb', line 71

def find_first_by_multiple
  selector = normalized_selector

  idx   = selector.delete(:index)
  xpath = selector[:xpath] || build_xpath(selector)

  if xpath
    # could build xpath for selector
    if idx
      @wd.find_elements(:xpath, xpath)[idx]
    else
      @wd.find_element(:xpath, xpath)
    end
  else
    # can't use xpath, probably a regexp in there
    if idx
      wd_find_by_regexp_selector(selector, :select)[idx]
    else
      wd_find_by_regexp_selector(selector, :find)
    end
  end
end

#find_first_by_oneObject



49
50
51
52
53
54
55
56
57
58
# File 'lib/watir-webdriver/locators/element_locator.rb', line 49

def find_first_by_one
  how, what = @selector.shift
  check_type how, what

  if WD_FINDERS.include?(how)
    wd_find_first_by(how, what)
  else
    raise NotImplementedError, "find first by attribute/other"
  end
end

#lhs_for(key) ⇒ Object



266
267
268
269
270
271
272
273
274
275
276
# File 'lib/watir-webdriver/locators/element_locator.rb', line 266

def lhs_for(key)
  case key
  when :text, 'text'
    'normalize-space()'
  when :href
    # TODO: change this behaviour?
    'normalize-space(@href)'
  else
    "@#{key.to_s.gsub("_", "-")}"
  end
end

#locateObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/watir-webdriver/locators/element_locator.rb', line 26

def locate
  # short-circuit if :id is given
  if e = by_id
    return e
  end

  if @selector.size == 1
    find_first_by_one
  else
    find_first_by_multiple
  end
rescue WebDriver::Error::NoSuchElementError => wde
  nil
end

#locate_allObject



41
42
43
44
45
46
47
# File 'lib/watir-webdriver/locators/element_locator.rb', line 41

def locate_all
  if @selector.size == 1
    find_all_by_one
  else
    find_all_by_multiple
  end
end

#matches_selector?(selector, element) ⇒ Boolean

Returns:

  • (Boolean)


155
156
157
158
159
160
161
# File 'lib/watir-webdriver/locators/element_locator.rb', line 155

def matches_selector?(selector, element)
  # p :start => selector
  selector.all? do |how, what|
    # p :comparing => [how, what], :to => fetch_value(how, element)
    what === fetch_value(how, element)
  end
end

#normalize_selector(how, what) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/watir-webdriver/locators/element_locator.rb', line 176

def normalize_selector(how, what)
  case how
  when :url
    [:href, what]
  when :caption
    [:text, what]
  when :class_name
    [:class, what]
  when :tag_name, :text, :xpath, :index, :class # include class since the attribute method is 'class_name'
    [how, what]
  else
    assert_valid_as_attribute how
    [how, what]
  end
end

#normalized_selectorObject



163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/watir-webdriver/locators/element_locator.rb', line 163

def normalized_selector
  selector = {}

  @selector.each do |how, what|
    check_type(how, what)

    how, what = normalize_selector(how, what)
    selector[how] = what
  end

  selector
end

#tag_name_matches?(element, tag_name) ⇒ Boolean

Returns:

  • (Boolean)


226
227
228
# File 'lib/watir-webdriver/locators/element_locator.rb', line 226

def tag_name_matches?(element, tag_name)
  tag_name === element.tag_name
end

#wd_find_all_by(how, what) ⇒ Object



117
118
119
120
121
122
123
# File 'lib/watir-webdriver/locators/element_locator.rb', line 117

def wd_find_all_by(how, what)
  if what.kind_of? String
    @wd.find_elements(how, what)
  else
    all_elements.select { |e| fetch_value(how, e) =~ what }
  end
end

#wd_find_by_regexp_selector(selector, method = :find) ⇒ Object



125
126
127
128
129
130
131
# File 'lib/watir-webdriver/locators/element_locator.rb', line 125

def wd_find_by_regexp_selector(selector, method = :find)
  rx_selector = delete_regexps_from(selector)
  xpath = build_xpath(selector) || raise("internal error: unable to build xpath from #{@selector.inspect}")

  elements = @wd.find_elements(:xpath, xpath)
  elements.send(method) { |e| matches_selector?(rx_selector, e) }
end

#wd_find_first_by(how, what) ⇒ Object



109
110
111
112
113
114
115
# File 'lib/watir-webdriver/locators/element_locator.rb', line 109

def wd_find_first_by(how, what)
  if what.kind_of? String
    @wd.find_element(how, what)
  else
    all_elements.find { |e| fetch_value(how, e) =~ what }
  end
end