watir-webdriver 0.7.0 → 0.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +2 -3
  4. data/CHANGES.md +10 -1
  5. data/Gemfile +1 -1
  6. data/README.md +22 -17
  7. data/Rakefile +55 -52
  8. data/lib/watir-webdriver.rb +3 -2
  9. data/lib/watir-webdriver/alert.rb +0 -1
  10. data/lib/watir-webdriver/atoms/README +1 -1
  11. data/lib/watir-webdriver/browser.rb +4 -5
  12. data/lib/watir-webdriver/cell_container.rb +2 -2
  13. data/lib/watir-webdriver/container.rb +2 -3
  14. data/lib/watir-webdriver/cookies.rb +8 -8
  15. data/lib/watir-webdriver/element_collection.rb +3 -4
  16. data/lib/watir-webdriver/elements/area.rb +0 -1
  17. data/lib/watir-webdriver/elements/button.rb +0 -1
  18. data/lib/watir-webdriver/elements/checkbox.rb +2 -4
  19. data/lib/watir-webdriver/elements/element.rb +69 -45
  20. data/lib/watir-webdriver/elements/file_field.rb +2 -3
  21. data/lib/watir-webdriver/elements/font.rb +2 -2
  22. data/lib/watir-webdriver/elements/form.rb +0 -1
  23. data/lib/watir-webdriver/elements/hidden.rb +2 -3
  24. data/lib/watir-webdriver/elements/html_elements.rb +2282 -0
  25. data/lib/watir-webdriver/elements/iframe.rb +6 -7
  26. data/lib/watir-webdriver/elements/image.rb +0 -1
  27. data/lib/watir-webdriver/elements/input.rb +0 -1
  28. data/lib/watir-webdriver/elements/link.rb +0 -1
  29. data/lib/watir-webdriver/elements/option.rb +3 -4
  30. data/lib/watir-webdriver/elements/radio.rb +2 -3
  31. data/lib/watir-webdriver/elements/select.rb +0 -1
  32. data/lib/watir-webdriver/elements/svg_elements.rb +787 -0
  33. data/lib/watir-webdriver/elements/table.rb +0 -2
  34. data/lib/watir-webdriver/elements/table_section.rb +1 -1
  35. data/lib/watir-webdriver/elements/text_field.rb +2 -3
  36. data/lib/watir-webdriver/exception.rb +0 -2
  37. data/lib/watir-webdriver/extensions/nokogiri.rb +1 -1
  38. data/lib/watir-webdriver/generator.rb +3 -0
  39. data/lib/watir-webdriver/generator/base.rb +11 -0
  40. data/lib/watir-webdriver/{html → generator/base}/generator.rb +23 -33
  41. data/lib/watir-webdriver/{html → generator/base}/idl_sorter.rb +3 -5
  42. data/lib/watir-webdriver/{html → generator/base}/spec_extractor.rb +15 -41
  43. data/lib/watir-webdriver/generator/base/util.rb +21 -0
  44. data/lib/watir-webdriver/{html → generator/base}/visitor.rb +15 -15
  45. data/lib/watir-webdriver/{html.rb → generator/html.rb} +3 -11
  46. data/lib/watir-webdriver/generator/html/generator.rb +36 -0
  47. data/lib/watir-webdriver/generator/html/spec_extractor.rb +50 -0
  48. data/lib/watir-webdriver/generator/html/visitor.rb +21 -0
  49. data/lib/watir-webdriver/generator/svg.rb +7 -0
  50. data/lib/watir-webdriver/generator/svg/generator.rb +38 -0
  51. data/lib/watir-webdriver/generator/svg/spec_extractor.rb +46 -0
  52. data/lib/watir-webdriver/generator/svg/visitor.rb +21 -0
  53. data/lib/watir-webdriver/has_window.rb +3 -3
  54. data/lib/watir-webdriver/locators/button_locator.rb +7 -2
  55. data/lib/watir-webdriver/locators/child_cell_locator.rb +1 -1
  56. data/lib/watir-webdriver/locators/child_row_locator.rb +1 -1
  57. data/lib/watir-webdriver/locators/element_locator.rb +44 -9
  58. data/lib/watir-webdriver/locators/text_area_locator.rb +4 -0
  59. data/lib/watir-webdriver/locators/text_field_locator.rb +7 -2
  60. data/lib/watir-webdriver/screenshot.rb +0 -1
  61. data/lib/watir-webdriver/version.rb +1 -1
  62. data/lib/watir-webdriver/wait.rb +10 -11
  63. data/lib/watir-webdriver/window.rb +5 -5
  64. data/lib/watir-webdriver/xpath_support.rb +0 -1
  65. data/spec/always_locate_spec.rb +4 -4
  66. data/spec/browser_spec.rb +3 -3
  67. data/spec/click_spec.rb +11 -4
  68. data/spec/container_spec.rb +2 -2
  69. data/spec/element_locator_spec.rb +145 -84
  70. data/spec/element_spec.rb +29 -16
  71. data/spec/html/clicks.html +3 -0
  72. data/spec/html/removed_element.html +7 -0
  73. data/spec/implementation.rb +5 -5
  74. data/spec/input_spec.rb +1 -1
  75. data/spec/spec_helper.rb +1 -2
  76. data/spec/special_chars_spec.rb +2 -2
  77. data/support/doctest_helper.rb +4 -4
  78. data/support/version_differ.rb +2 -2
  79. data/watir-webdriver.gemspec +3 -3
  80. metadata +27 -17
  81. data/lib/watir-webdriver/elements/generated.rb +0 -3106
  82. data/lib/watir-webdriver/html/util.rb +0 -22
@@ -0,0 +1,50 @@
1
+ module Watir
2
+ module Generator
3
+ class HTML::SpecExtractor < Base::SpecExtractor
4
+
5
+ private
6
+
7
+ def extract_interface_map
8
+ # http://www.whatwg.org/specs/web-apps/current-work/#elements-1
9
+ table = @doc.search("//h3[@id='elements-3']/following-sibling::table[1]").first
10
+ table or raise "could not find elements-3 table"
11
+
12
+ @interface_map = {}
13
+
14
+ parse_table(table).each do |row|
15
+ row['Element'].split(", ").each { |tag| @interface_map[tag] = row['Interface'] }
16
+ end
17
+ end
18
+
19
+ def build_result
20
+ {}.tap do |result|
21
+ @interface_map.each do |tag, interface|
22
+ result[tag] = fetch_interface(interface)
23
+ end
24
+
25
+ # missing from the elements-1 table
26
+ result['frameset'] = fetch_interface('HTMLFrameSetElement')
27
+ end
28
+ end
29
+
30
+ def parse_table(table)
31
+ headers = table.css("thead th").map { |e| e.inner_text.strip }
32
+
33
+ table.css("tbody tr").map do |row|
34
+ result = {}
35
+
36
+ row.css("th, td").each_with_index do |node, idx|
37
+ result[headers[idx]] = node.inner_text.strip
38
+ end
39
+
40
+ result
41
+ end
42
+ end
43
+
44
+ def issued_interfaces
45
+ []
46
+ end
47
+
48
+ end # HTML:;SpecExtractor
49
+ end # Generator
50
+ end # Watir
@@ -0,0 +1,21 @@
1
+ module Watir
2
+ module Generator
3
+ class HTML::Visitor < Base::Visitor
4
+
5
+ def classify_regexp
6
+ /^HTML(.+)Element$/
7
+ end
8
+
9
+ private
10
+
11
+ def interface_regexp
12
+ /^HTML/
13
+ end
14
+
15
+ def force_inheritance
16
+ { 'HTMLElement' => 'Element' }
17
+ end
18
+
19
+ end # HTML::Visitor
20
+ end # Generator
21
+ end # Watir
@@ -0,0 +1,7 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ inflect.plural 'defs', 'defss'
3
+ end
4
+
5
+ require "watir-webdriver/generator/svg/generator"
6
+ require "watir-webdriver/generator/svg/spec_extractor"
7
+ require "watir-webdriver/generator/svg/visitor"
@@ -0,0 +1,38 @@
1
+ module Watir
2
+ module Generator
3
+ class SVG < Base
4
+
5
+ private
6
+
7
+ # fix collisions with HTML
8
+ #
9
+ # TODO: change generator so instead these classes
10
+ # are inherited from HTML ones
11
+
12
+ def ignored_tags
13
+ %w(a audio canvas iframe image script source style text title track video)
14
+ end
15
+
16
+ def ignored_interfaces
17
+ ignored_tags.map { |tag| "SVG#{tag.capitalize}Element" }
18
+ end
19
+
20
+ def ignored_attributes
21
+ []
22
+ end
23
+
24
+ def generator_implementation
25
+ 'SVG'
26
+ end
27
+
28
+ def visitor_class
29
+ SVG::Visitor
30
+ end
31
+
32
+ def extractor_class
33
+ SVG::SpecExtractor
34
+ end
35
+
36
+ end # SVG
37
+ end # Generator
38
+ end # Watir
@@ -0,0 +1,46 @@
1
+ module Watir
2
+ module Generator
3
+ class SVG::SpecExtractor < Base::SpecExtractor
4
+
5
+ private
6
+
7
+ def extract_interface_map
8
+ list = @doc.search("//div[@id='chapter-eltindex']//ul/li")
9
+ list.any? or raise 'could not find elements list'
10
+
11
+ @interface_map = parse_list(list)
12
+ end
13
+
14
+ def build_result
15
+ {}.tap do |result|
16
+ @interface_map.each do |tag, interface|
17
+ result[tag] = fetch_interface(interface)
18
+ end
19
+ end
20
+ end
21
+
22
+ def parse_list(list)
23
+ {}.tap do |result|
24
+ list.each do |node|
25
+ tag_name = node.css('a span').inner_text.strip
26
+ id = node.css('a').attr('href').to_s
27
+
28
+ # Some interfaces are actually defined in different specs
29
+ # (for example, clipPath), so we ignore them for now
30
+ if id =~ /^#.+/
31
+ interface_css = 'div.element-summary a.idlinterface'
32
+ interface_name = @doc.css("#{id} #{interface_css}, #{id} ~ #{interface_css}").first.inner_text.strip
33
+
34
+ result[tag_name] = interface_name
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def issued_interfaces
41
+ %w(SVGHatchElement SVGHatchPathElement SVGSolidColorElement)
42
+ end
43
+
44
+ end # SVG::SpecExtractor
45
+ end # Generator
46
+ end # Watir
@@ -0,0 +1,21 @@
1
+ module Watir
2
+ module Generator
3
+ class SVG::Visitor < Base::Visitor
4
+
5
+ def classify_regexp
6
+ /^SVG(.+)Element$/
7
+ end
8
+
9
+ private
10
+
11
+ def interface_regexp
12
+ /^SVG.*Element$/
13
+ end
14
+
15
+ def force_inheritance
16
+ { 'SVGElement' => 'HTMLElement' }
17
+ end
18
+
19
+ end # SVG::Visitor
20
+ end # Generator
21
+ end # Watir
@@ -5,13 +5,13 @@ module Watir
5
5
  # Returns browser windows array.
6
6
  #
7
7
  # @example
8
- # browser.windows(:title => 'closeable window')
8
+ # browser.windows(title: 'closeable window')
9
9
  #
10
10
  # @return [Array<Window>]
11
11
  #
12
12
 
13
13
  def windows(*args)
14
- all = @driver.window_handles.map { |handle| Window.new(@driver, :handle => handle) }
14
+ all = @driver.window_handles.map { |handle| Window.new(@driver, handle: handle) }
15
15
 
16
16
  if args.empty?
17
17
  all
@@ -24,7 +24,7 @@ module Watir
24
24
  # Returns browser window.
25
25
  #
26
26
  # @example
27
- # browser.window(:title => 'closeable window')
27
+ # browser.window(title: 'closeable window')
28
28
  #
29
29
  # @return [Window]
30
30
  #
@@ -10,7 +10,7 @@ module Watir
10
10
  def wd_find_first_by(how, what)
11
11
  if how == :tag_name
12
12
  how = :xpath
13
- what = ".//button | .//input[#{attribute_expression :type => Button::VALID_TYPES}]"
13
+ what = ".//button | .//input[#{attribute_expression type: Button::VALID_TYPES}]"
14
14
  end
15
15
 
16
16
  super
@@ -33,7 +33,7 @@ module Watir
33
33
  xpath << " | .//input"
34
34
  xpath << "[#{input_attr_exp}]"
35
35
 
36
- p :build_wd_selector => xpath if $DEBUG
36
+ p build_wd_selector: xpath if $DEBUG
37
37
 
38
38
  [:xpath, xpath]
39
39
  end
@@ -67,6 +67,11 @@ module Watir
67
67
  end
68
68
  end
69
69
 
70
+ def can_convert_regexp_to_contains?
71
+ # regexp conversion won't work with the complex xpath selector
72
+ false
73
+ end
74
+
70
75
  def tag_name_matches?(tag_name, _)
71
76
  !!(/^(input|button)$/ === tag_name)
72
77
  end
@@ -23,7 +23,7 @@ module Watir
23
23
 
24
24
  xpath = expressions.join(" | ")
25
25
 
26
- p :build_wd_selector => xpath if $DEBUG
26
+ p build_wd_selector: xpath if $DEBUG
27
27
 
28
28
  [:xpath, xpath]
29
29
  end
@@ -28,7 +28,7 @@ module Watir
28
28
 
29
29
  xpath = expressions.join(" | ")
30
30
 
31
- p :build_wd_selector => xpath if $DEBUG
31
+ p build_wd_selector: xpath if $DEBUG
32
32
 
33
33
  [:xpath, xpath]
34
34
  end
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  module Watir
3
2
  class ElementLocator
4
3
  include Watir::Exception
@@ -18,6 +17,17 @@ module Watir
18
17
 
19
18
  WILDCARD_ATTRIBUTE = /^(aria|data)_(.+)$/
20
19
 
20
+ # Regular expressions that can be reliably converted to xpath `contains`
21
+ # expressions in order to optimize the locator.
22
+ #
23
+ CONVERTABLE_REGEXP = %r{
24
+ \A
25
+ ([^\[\]\\^$.|?*+()]*) # leading literal characters
26
+ [^|]*? # do not try to convert expressions with alternates
27
+ ([^\[\]\\^$.|?*+()]*) # trailing literal characters
28
+ \z
29
+ }x
30
+
21
31
  def initialize(wd, selector, valid_attributes)
22
32
  @wd = wd
23
33
  @selector = selector.dup
@@ -100,7 +110,7 @@ module Watir
100
110
  def find_all_by_multiple
101
111
  selector = normalized_selector
102
112
 
103
- if selector.has_key? :index
113
+ if selector.key? :index
104
114
  raise ArgumentError, "can't locate all elements by :index"
105
115
  end
106
116
 
@@ -132,7 +142,7 @@ module Watir
132
142
  parent = @wd
133
143
  rx_selector = delete_regexps_from(selector)
134
144
 
135
- if rx_selector.has_key?(:label) && should_use_label_element?
145
+ if rx_selector.key?(:label) && should_use_label_element?
136
146
  label = label_from_text(rx_selector.delete(:label)) || return
137
147
  if (id = label.attribute(:for))
138
148
  selector[:id] = id
@@ -147,6 +157,15 @@ module Watir
147
157
  raise Error, "internal error: unable to build WebDriver selector from #{selector.inspect}"
148
158
  end
149
159
 
160
+ if how == :xpath && can_convert_regexp_to_contains?
161
+ rx_selector.each do |key, value|
162
+ next if key == :tag_name || key == :text
163
+
164
+ predicates = regexp_selector_to_predicates(key, value)
165
+ what = "(#{what})[#{predicates.join(' and ')}]" unless predicates.empty?
166
+ end
167
+ end
168
+
150
169
  elements = parent.find_elements(how, what)
151
170
  elements.__send__(method) { |el| matches_selector?(el, rx_selector) }
152
171
  end
@@ -169,7 +188,7 @@ module Watir
169
188
  def label_from_text(label_exp)
170
189
  # TODO: this won't work correctly if @wd is a sub-element
171
190
  @wd.find_elements(:tag_name, 'label').find do |el|
172
- matches_selector?(el, :text => label_exp)
191
+ matches_selector?(el, text: label_exp)
173
192
  end
174
193
  end
175
194
 
@@ -233,6 +252,22 @@ module Watir
233
252
  rx_selector
234
253
  end
235
254
 
255
+ def regexp_selector_to_predicates(key, re)
256
+ return [] if re.casefold?
257
+
258
+ match = re.source.match(CONVERTABLE_REGEXP)
259
+ return [] unless match
260
+
261
+ lhs = lhs_for(key)
262
+ match.captures.reject(&:empty?).map do |literals|
263
+ "contains(#{lhs}, #{XpathSupport.escape(literals)})"
264
+ end
265
+ end
266
+
267
+ def can_convert_regexp_to_contains?
268
+ true
269
+ end
270
+
236
271
  def assert_valid_as_attribute(attribute)
237
272
  unless valid_attribute? attribute or attribute.to_s =~ WILDCARD_ATTRIBUTE
238
273
  raise MissingWayOfFindingObjectException, "invalid attribute: #{attribute.inspect}"
@@ -255,7 +290,7 @@ module Watir
255
290
  end
256
291
 
257
292
  def all_elements
258
- @wd.find_elements(:xpath => ".//*")
293
+ @wd.find_elements(xpath: ".//*")
259
294
  end
260
295
 
261
296
  def tag_name_matches?(element_tag_name, tag_name)
@@ -287,7 +322,7 @@ module Watir
287
322
  xpath << "[" << attribute_expression(selectors) << "]"
288
323
  end
289
324
 
290
- p :xpath => xpath, :selectors => selectors if $DEBUG
325
+ p xpath: xpath, selectors: selectors if $DEBUG
291
326
 
292
327
  [:xpath, xpath]
293
328
  end
@@ -327,15 +362,15 @@ module Watir
327
362
  def use_css?(selectors)
328
363
  return false unless Watir.prefer_css?
329
364
 
330
- if selectors.has_key?(:text) || selectors.has_key?(:label) || selectors.has_key?(:index)
365
+ if selectors.key?(:text) || selectors.key?(:label) || selectors.key?(:index)
331
366
  return false
332
367
  end
333
368
 
334
- if selectors[:tag_name] == 'input' && selectors.has_key?(:type)
369
+ if selectors[:tag_name] == 'input' && selectors.key?(:type)
335
370
  return false
336
371
  end
337
372
 
338
- if selectors.has_key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
373
+ if selectors.key?(:class) && selectors[:class] !~ /^[\w-]+$/ui
339
374
  return false
340
375
  end
341
376
 
@@ -3,6 +3,10 @@ module Watir
3
3
 
4
4
  private
5
5
 
6
+ def can_convert_regexp_to_contains?
7
+ false
8
+ end
9
+
6
10
  def normalize_selector(how, what)
7
11
  # We need to iterate through located elements and fetch
8
12
  # attribute value using WebDriver because XPath doesn't understand
@@ -5,6 +5,11 @@ module Watir
5
5
  # TODO: better way of finding input text fields?
6
6
  NEGATIVE_TYPE_EXPR = NON_TEXT_TYPES.map { |type| "%s!=%s" % [XpathSupport.downcase('@type'), type.inspect] }.join(' and ')
7
7
 
8
+ def wd_find_first_by(how, what)
9
+ how, what = build_wd_selector(how => what) if how == :tag_name
10
+ super
11
+ end
12
+
8
13
  def locate_all
9
14
  find_all_by_multiple
10
15
  end
@@ -28,7 +33,7 @@ module Watir
28
33
  xpath << "| .//textarea"
29
34
  xpath << "[#{textarea_attr_exp}]" unless textarea_attr_exp.empty?
30
35
 
31
- p :build_wd_selector => xpath if $DEBUG
36
+ p build_wd_selector: xpath if $DEBUG
32
37
 
33
38
  [:xpath, xpath]
34
39
  end
@@ -49,7 +54,7 @@ module Watir
49
54
  tag_name = element.tag_name.downcase
50
55
 
51
56
  [:text, :value, :label].each do |key|
52
- if rx_selector.has_key?(key)
57
+ if rx_selector.key?(key)
53
58
  correct_key = tag_name == 'input' ? :value : :text
54
59
  rx_selector[correct_key] = rx_selector.delete(key)
55
60
  end
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  module Watir
3
2
  class Screenshot
4
3
 
@@ -1,3 +1,3 @@
1
1
  module Watir
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.0'
3
3
  end
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'forwardable'
3
2
  require 'watir-webdriver/wait/timer'
4
3
 
@@ -29,7 +28,7 @@ module Watir
29
28
  # Waits until the block evaluates to true or times out.
30
29
  #
31
30
  # @example
32
- # Watir::Wait.until { browser.text_field(:name => "new_user_first_name").visible? }
31
+ # Watir::Wait.until { browser.text_field(name: "new_user_first_name").visible? }
33
32
  #
34
33
  # @param [Fixnum] timeout How long to wait in seconds
35
34
  # @param [String] message Message to raise if timeout is exceeded
@@ -37,6 +36,7 @@ module Watir
37
36
  #
38
37
 
39
38
  def until(timeout = nil, message = nil)
39
+ timeout ||= Watir.default_timeout
40
40
  run_with_timer(timeout) { return true if yield(self) }
41
41
  raise TimeoutError, message_for(timeout, message)
42
42
  end
@@ -45,7 +45,7 @@ module Watir
45
45
  # Wait while the block evaluates to true or times out.
46
46
  #
47
47
  # @example
48
- # Watir::Wait.while { browser.text_field(:name => "abrakadbra").present? }
48
+ # Watir::Wait.while { browser.text_field(name: "abrakadbra").present? }
49
49
  #
50
50
  # @param [Fixnum] timeout How long to wait in seconds
51
51
  # @param [String] message Message to raise if timeout is exceeded
@@ -53,6 +53,7 @@ module Watir
53
53
  #
54
54
 
55
55
  def while(timeout = nil, message = nil)
56
+ timeout ||= Watir.default_timeout
56
57
  run_with_timer(timeout) { return unless yield(self) }
57
58
  raise TimeoutError, message_for(timeout, message)
58
59
  end
@@ -66,9 +67,7 @@ module Watir
66
67
  err
67
68
  end
68
69
 
69
- def run_with_timer(timeout = nil, &block)
70
- timeout ||= Watir.default_timeout
71
-
70
+ def run_with_timer(timeout, &block)
72
71
  if timeout == 0
73
72
  block.call
74
73
  else
@@ -134,9 +133,9 @@ module Watir
134
133
  # Waits until the element is present.
135
134
  #
136
135
  # @example
137
- # browser.text_field(:name => "new_user_first_name").when_present.click
138
- # browser.text_field(:name => "new_user_first_name").when_present { |field| field.set "Watir" }
139
- # browser.text_field(:name => "new_user_first_name").when_present(60).text
136
+ # browser.text_field(name: "new_user_first_name").when_present.click
137
+ # browser.text_field(name: "new_user_first_name").when_present { |field| field.set "Watir" }
138
+ # browser.text_field(name: "new_user_first_name").when_present(60).text
140
139
  #
141
140
  # @param [Fixnum] timeout seconds to wait before timing out
142
141
  #
@@ -160,7 +159,7 @@ module Watir
160
159
  # Waits until the element is present.
161
160
  #
162
161
  # @example
163
- # browser.text_field(:name => "new_user_first_name").wait_until_present
162
+ # browser.text_field(name: "new_user_first_name").wait_until_present
164
163
  #
165
164
  # @param [Fixnum] timeout seconds to wait before timing out
166
165
  #
@@ -178,7 +177,7 @@ module Watir
178
177
  # Waits while the element is present.
179
178
  #
180
179
  # @example
181
- # browser.text_field(:name => "abrakadbra").wait_while_present
180
+ # browser.text_field(name: "abrakadbra").wait_while_present
182
181
  #
183
182
  # @param [Fixnum] timeout seconds to wait before timing out
184
183
  #