watir 5.0.0 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.document +5 -0
  3. data/.gitignore +21 -0
  4. data/.gitmodules +3 -0
  5. data/.travis.yml +35 -0
  6. data/CHANGES.md +1756 -0
  7. data/Gemfile +12 -0
  8. data/LICENSE +23 -0
  9. data/README.md +92 -0
  10. data/Rakefile +161 -32
  11. data/lib/watir.rb +127 -1
  12. data/lib/watir/after_hooks.rb +132 -0
  13. data/lib/watir/alert.rb +104 -0
  14. data/lib/watir/aliases.rb +6 -0
  15. data/lib/watir/atoms.rb +24 -0
  16. data/lib/watir/atoms/README +3 -0
  17. data/lib/watir/atoms/fireEvent.js +29 -0
  18. data/lib/watir/atoms/getAttribute.js +18 -0
  19. data/lib/watir/atoms/getInnerHtml.js +18 -0
  20. data/lib/watir/atoms/getOuterHtml.js +18 -0
  21. data/lib/watir/atoms/getParentElement.js +17 -0
  22. data/lib/watir/atoms/selectText.js +61 -0
  23. data/lib/watir/attribute_helper.rb +98 -0
  24. data/lib/watir/browser.rb +346 -0
  25. data/lib/watir/cell_container.rb +25 -0
  26. data/lib/watir/container.rb +51 -0
  27. data/lib/watir/cookies.rb +132 -0
  28. data/lib/watir/element_collection.rb +126 -0
  29. data/lib/watir/elements/area.rb +12 -0
  30. data/lib/watir/elements/button.rb +37 -0
  31. data/lib/watir/elements/cell.rb +17 -0
  32. data/lib/watir/elements/checkbox.rb +54 -0
  33. data/lib/watir/elements/dlist.rb +12 -0
  34. data/lib/watir/elements/element.rb +646 -0
  35. data/lib/watir/elements/file_field.rb +41 -0
  36. data/lib/watir/elements/font.rb +11 -0
  37. data/lib/watir/elements/form.rb +17 -0
  38. data/lib/watir/elements/hidden.rb +20 -0
  39. data/lib/watir/elements/html_elements.rb +2063 -0
  40. data/lib/watir/elements/iframe.rb +163 -0
  41. data/lib/watir/elements/image.rb +62 -0
  42. data/lib/watir/elements/input.rb +7 -0
  43. data/lib/watir/elements/link.rb +18 -0
  44. data/lib/watir/elements/option.rb +74 -0
  45. data/lib/watir/elements/radio.rb +42 -0
  46. data/lib/watir/elements/row.rb +17 -0
  47. data/lib/watir/elements/select.rb +238 -0
  48. data/lib/watir/elements/svg_elements.rb +667 -0
  49. data/lib/watir/elements/table.rb +42 -0
  50. data/lib/watir/elements/table_cell.rb +6 -0
  51. data/lib/watir/elements/table_row.rb +15 -0
  52. data/lib/watir/elements/table_section.rb +15 -0
  53. data/lib/watir/elements/text_area.rb +5 -0
  54. data/lib/watir/elements/text_field.rb +37 -0
  55. data/lib/watir/exception.rb +17 -0
  56. data/lib/watir/extensions/nokogiri.rb +14 -0
  57. data/lib/watir/extensions/select_text.rb +10 -0
  58. data/lib/watir/generator.rb +3 -0
  59. data/lib/watir/generator/base.rb +11 -0
  60. data/lib/watir/generator/base/generator.rb +115 -0
  61. data/lib/watir/generator/base/idl_sorter.rb +47 -0
  62. data/lib/watir/generator/base/spec_extractor.rb +138 -0
  63. data/lib/watir/generator/base/util.rb +21 -0
  64. data/lib/watir/generator/base/visitor.rb +157 -0
  65. data/lib/watir/generator/html.rb +15 -0
  66. data/lib/watir/generator/html/generator.rb +36 -0
  67. data/lib/watir/generator/html/spec_extractor.rb +50 -0
  68. data/lib/watir/generator/html/visitor.rb +21 -0
  69. data/lib/watir/generator/svg.rb +7 -0
  70. data/lib/watir/generator/svg/generator.rb +38 -0
  71. data/lib/watir/generator/svg/spec_extractor.rb +46 -0
  72. data/lib/watir/generator/svg/visitor.rb +21 -0
  73. data/lib/watir/has_window.rb +53 -0
  74. data/lib/watir/locators.rb +22 -0
  75. data/lib/watir/locators/button/locator.rb +38 -0
  76. data/lib/watir/locators/button/selector_builder.rb +27 -0
  77. data/lib/watir/locators/button/selector_builder/xpath.rb +29 -0
  78. data/lib/watir/locators/button/validator.rb +15 -0
  79. data/lib/watir/locators/cell/locator.rb +17 -0
  80. data/lib/watir/locators/cell/selector_builder.rb +24 -0
  81. data/lib/watir/locators/element/locator.rb +249 -0
  82. data/lib/watir/locators/element/selector_builder.rb +147 -0
  83. data/lib/watir/locators/element/selector_builder/css.rb +65 -0
  84. data/lib/watir/locators/element/selector_builder/xpath.rb +72 -0
  85. data/lib/watir/locators/element/validator.rb +23 -0
  86. data/lib/watir/locators/row/locator.rb +17 -0
  87. data/lib/watir/locators/row/selector_builder.rb +29 -0
  88. data/lib/watir/locators/text_area/locator.rb +13 -0
  89. data/lib/watir/locators/text_area/selector_builder.rb +22 -0
  90. data/lib/watir/locators/text_field/locator.rb +44 -0
  91. data/lib/watir/locators/text_field/selector_builder.rb +34 -0
  92. data/lib/watir/locators/text_field/selector_builder/xpath.rb +19 -0
  93. data/lib/watir/locators/text_field/validator.rb +20 -0
  94. data/lib/watir/row_container.rb +36 -0
  95. data/lib/watir/screenshot.rb +50 -0
  96. data/lib/watir/user_editable.rb +38 -0
  97. data/lib/watir/version.rb +3 -3
  98. data/lib/watir/wait.rb +250 -0
  99. data/lib/watir/wait/timer.rb +19 -0
  100. data/lib/watir/window.rb +244 -0
  101. data/lib/watir/xpath_support.rb +20 -0
  102. data/spec/always_locate_spec.rb +43 -0
  103. data/spec/browser_spec.rb +130 -0
  104. data/spec/click_spec.rb +19 -0
  105. data/spec/container_spec.rb +34 -0
  106. data/spec/element_locator_spec.rb +532 -0
  107. data/spec/element_spec.rb +136 -0
  108. data/spec/implementation.rb +216 -0
  109. data/spec/input_spec.rb +14 -0
  110. data/spec/locator_spec_helper.rb +57 -0
  111. data/spec/spec_helper.rb +35 -0
  112. data/spec/special_chars_spec.rb +13 -0
  113. data/support/doctest_helper.rb +78 -0
  114. data/support/travis.sh +44 -0
  115. data/support/version_differ.rb +59 -0
  116. data/watir.gemspec +37 -25
  117. metadata +288 -23
  118. data/lib/watir/loader.rb +0 -64
@@ -0,0 +1,20 @@
1
+ module Watir
2
+ module XpathSupport
3
+
4
+ def self.escape(value)
5
+ if value.include? "'"
6
+ parts = value.split("'", -1).map { |part| "'#{part}'" }
7
+ string = parts.join(%{,"'",})
8
+
9
+ "concat(#{string})"
10
+ else
11
+ "'#{value}'"
12
+ end
13
+ end
14
+
15
+ def self.downcase(value)
16
+ "translate(#{value},'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"
17
+ end
18
+
19
+ end # XpathSupport
20
+ end # Watir
@@ -0,0 +1,43 @@
1
+ require File.expand_path('watirspec/spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'Watir' do
4
+ describe '#always_locate?' do
5
+
6
+ before do
7
+ browser.goto WatirSpec.url_for('removed_element.html')
8
+ end
9
+
10
+ it 'determines whether #exist? returns false for stale element' do
11
+ element = browser.div(id: "text")
12
+ expect(element.exists?).to be true
13
+
14
+ browser.refresh
15
+
16
+ expect(element.exists?).to be Watir.always_locate?
17
+ end
18
+
19
+ it 'allows using cached elements regardless of setting, when element is not stale' do
20
+ element = browser.div(id: "text")
21
+ expect(element.exists?).to be true
22
+
23
+ # exception raised if element is re-looked up
24
+ allow(browser.driver).to receive(:find_element).with(:id, 'text') { raise }
25
+
26
+ expect { element.exists? }.to_not raise_error
27
+ end
28
+
29
+ it 'determines whether an exception is raised when taking an action on a stale element' do
30
+ element = browser.div(id: "text")
31
+ expect(element.exists?).to be true
32
+
33
+ browser.refresh
34
+ browser.div(id: "text").wait_until_present
35
+
36
+ if Watir.always_locate?
37
+ expect { element.text }.to_not raise_error
38
+ else
39
+ expect { element.text }.to raise_error Watir::Exception::UnknownObjectException
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,130 @@
1
+ require File.expand_path('watirspec/spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Watir::Browser do
4
+
5
+ describe ".new" do
6
+ it "passes the args to Selenium" do
7
+ expect(Selenium::WebDriver).to receive(:for).with(:firefox, :foo).and_return(nil)
8
+ Watir::Browser.new(:firefox, :foo)
9
+ end
10
+
11
+ it "takes a Driver instance as argument" do
12
+ mock_driver = double(Selenium::WebDriver::Driver)
13
+ expect(Selenium::WebDriver::Driver).to receive(:===).with(mock_driver).and_return(true)
14
+ expect { Watir::Browser.new(mock_driver) }.to_not raise_error
15
+ end
16
+
17
+ it "raises ArgumentError for invalid args" do
18
+ expect { Watir::Browser.new(Object.new) }.to raise_error(ArgumentError)
19
+ end
20
+ end
21
+
22
+ describe "#execute_script" do
23
+ before { browser.goto WatirSpec.url_for("definition_lists.html") }
24
+
25
+ it "wraps elements as Watir objects" do
26
+ returned = browser.execute_script("return document.body")
27
+ expect(returned).to be_kind_of(Watir::Body)
28
+ end
29
+
30
+ it "wraps elements in an array" do
31
+ list = browser.execute_script("return [document.body];")
32
+ expect(list.size).to eq 1
33
+ expect(list.first).to be_kind_of(Watir::Body)
34
+ end
35
+
36
+ it "wraps elements in a Hash" do
37
+ hash = browser.execute_script("return {element: document.body};")
38
+ expect(hash['element']).to be_kind_of(Watir::Body)
39
+ end
40
+
41
+ it "wraps elements in a deep object" do
42
+ hash = browser.execute_script("return {elements: [document.body], body: {element: document.body }}")
43
+
44
+ expect(hash['elements'].first).to be_kind_of(Watir::Body)
45
+ expect(hash['body']['element']).to be_kind_of(Watir::Body)
46
+ end
47
+ end
48
+
49
+ bug "https://bugzilla.mozilla.org/show_bug.cgi?id=1260233", :firefox do
50
+ describe "#send_key{,s}" do
51
+ it "sends keystrokes to the active element" do
52
+ browser.goto WatirSpec.url_for "forms_with_input_elements.html"
53
+
54
+ browser.send_keys "hello"
55
+ expect(browser.text_field(id: "new_user_first_name").value).to eq "hello"
56
+ end
57
+
58
+ it "sends keys to a frame" do
59
+ browser.goto WatirSpec.url_for "frames.html"
60
+ tf = browser.frame.text_field(id: "senderElement")
61
+ tf.clear
62
+
63
+ browser.frame.send_keys "hello"
64
+
65
+ expect(tf.value).to eq "hello"
66
+ end
67
+ end
68
+ end
69
+
70
+ bug "https://bugzilla.mozilla.org/show_bug.cgi?id=1290814", :firefox do
71
+ it "raises an error when trying to interact with a closed browser" do
72
+ b = WatirSpec.new_browser
73
+ b.goto WatirSpec.url_for "definition_lists.html"
74
+ b.close
75
+
76
+ expect { b.dl(id: "experience-list").id }.to raise_error(Watir::Exception::Error, "browser was closed")
77
+ end
78
+ end
79
+
80
+ describe "#wait_while" do
81
+ it "delegates to the Wait module" do
82
+ expect(Wait).to receive(:while).with(3, "foo").and_yield
83
+
84
+ called = false
85
+ browser.wait_while(3, "foo") { called = true }
86
+
87
+ expect(called).to be true
88
+ end
89
+ end
90
+
91
+ describe "#wait_until" do
92
+ it "delegates to the Wait module" do
93
+ expect(Wait).to receive(:until).with(3, "foo").and_yield
94
+
95
+ called = false
96
+ browser.wait_until(3, "foo") { called = true }
97
+
98
+ expect(called).to be true
99
+ end
100
+ end
101
+
102
+ describe "#wait" do
103
+ it "waits until document.readyState == 'complete'" do
104
+ expect(browser).to receive(:ready_state).and_return('incomplete')
105
+ expect(browser).to receive(:ready_state).and_return('complete')
106
+
107
+ browser.wait
108
+ end
109
+ end
110
+
111
+ describe "#ready_state" do
112
+ it "gets the document's readyState property" do
113
+ expect(browser).to receive(:execute_script).with('return document.readyState')
114
+ browser.ready_state
115
+ end
116
+ end
117
+
118
+ describe "#inspect" do
119
+ it "works even if browser is closed" do
120
+ expect(browser).to receive(:url).and_raise(Errno::ECONNREFUSED)
121
+ expect { browser.inspect }.to_not raise_error
122
+ end
123
+ end
124
+
125
+ describe '#screenshot' do
126
+ it 'returns an instance of of Watir::Screenshot' do
127
+ expect(browser.screenshot).to be_kind_of(Watir::Screenshot)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../watirspec/spec_helper', __FILE__)
2
+
3
+ describe Watir::Element do
4
+ describe "#click" do
5
+ before {
6
+ browser.goto WatirSpec.url_for('clicks.html')
7
+ }
8
+
9
+ let(:clicker) { browser.element(id: "click-logger") }
10
+ let(:log) { browser.element(id: "log").ps.map { |e| e.text } }
11
+
12
+ bug "https://github.com/watir/watir/issues/343", :webdriver do
13
+ it "clicks an element with text in nested text node using text selector" do
14
+ browser.element(text: "Can You Click This?").click
15
+ expect(browser.element(text: "You Clicked It!")).to exist
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path('watirspec/spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Watir::Container do
4
+ before { @container = Object.new.extend(Watir::Container) }
5
+
6
+ describe "#extract_selector" do
7
+ before do
8
+ def @container.public_extract_selector(*args)
9
+ extract_selector(*args)
10
+ end
11
+ end
12
+
13
+ it "converts 2-arg selector into a hash" do
14
+ expect(@container.public_extract_selector([:how, 'what'])).to eq Hash[how: 'what']
15
+ end
16
+
17
+ it "returns the hash given" do
18
+ expect(@container.public_extract_selector([how: "what"])).to eq Hash[how: "what"]
19
+ end
20
+
21
+ it "returns an empty hash if given no args" do
22
+ expect(@container.public_extract_selector([])).to eq Hash[]
23
+ end
24
+
25
+ it "raises ArgumentError if given 1 arg which is not a Hash" do
26
+ expect {@container.public_extract_selector([:how])}.to raise_error(ArgumentError)
27
+ end
28
+
29
+ it "raises ArgumentError if given > 2 args" do
30
+ expect {@container.public_extract_selector([:how, 'what', 'value'])}.to raise_error(ArgumentError)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,532 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe Watir::Locators::Element::Locator do
4
+ include LocatorSpecHelper
5
+
6
+ describe "finds a single element" do
7
+ describe "by delegating to Selenium" do
8
+ SELENIUM_SELECTORS.each do |loc|
9
+ it "delegates to Selenium's #{loc} locator" do
10
+ expect_one(loc, "bar").and_return(element(tag_name: "div"))
11
+ locate_one loc => "bar"
12
+ end
13
+ end
14
+ end
15
+
16
+ describe "with selectors not supported by Selenium" do
17
+ it "handles selector with tag name and a single attribute" do
18
+ if Watir.prefer_css?
19
+ expect_one :css, 'div[title="foo"]'
20
+ else
21
+ expect_one :xpath, ".//div[@title='foo']"
22
+ end
23
+
24
+ locate_one tag_name: "div",
25
+ title: "foo"
26
+ end
27
+
28
+ it "handles selector with no tag name and and a single attribute" do
29
+ if Watir.prefer_css?
30
+ expect_one :css, '[title="foo"]'
31
+ else
32
+ expect_one :xpath, ".//*[@title='foo']"
33
+ end
34
+
35
+ locate_one title: "foo"
36
+ end
37
+
38
+ it "handles single quotes in the attribute string" do
39
+ if Watir.prefer_css?
40
+ expect_one :css, %{[title="foo and 'bar'"]}
41
+ else
42
+ expect_one :xpath, %{.//*[@title=concat('foo and ',"'",'bar',"'",'')]}
43
+ end
44
+
45
+ locate_one title: "foo and 'bar'"
46
+ end
47
+
48
+ it "handles selector with tag name and multiple attributes" do
49
+ if Watir.prefer_css?
50
+ expect_one :css, 'div[title="foo"][dir="bar"]'
51
+ else
52
+ expect_one :xpath, ".//div[@title='foo' and @dir='bar']"
53
+ end
54
+
55
+ locate_one [:tag_name, "div",
56
+ :title , "foo",
57
+ :dir , 'bar']
58
+ end
59
+
60
+ it "handles selector with no tag name and multiple attributes" do
61
+ if Watir.prefer_css?
62
+ expect_one :css, '[dir="foo"][title="bar"]'
63
+ else
64
+ expect_one :xpath, ".//*[@dir='foo' and @title='bar']"
65
+ end
66
+
67
+ locate_one [:dir, "foo",
68
+ :title, "bar"]
69
+ end
70
+ end
71
+
72
+ describe "with special cased selectors" do
73
+ it "normalizes space for :text" do
74
+ expect_one :xpath, ".//div[normalize-space()='foo']"
75
+ locate_one tag_name: "div",
76
+ text: "foo"
77
+ end
78
+
79
+ it "translates :caption to :text" do
80
+ expect_one :xpath, ".//div[normalize-space()='foo']"
81
+
82
+ locate_one tag_name: "div",
83
+ caption: "foo"
84
+ end
85
+
86
+ it "translates :class_name to :class" do
87
+ if Watir.prefer_css?
88
+ expect_one :css, "div.foo"
89
+ else
90
+ expect_one :xpath, ".//div[contains(concat(' ', @class, ' '), ' foo ')]"
91
+ end
92
+
93
+ locate_one tag_name: "div",
94
+ class_name: "foo"
95
+ end
96
+
97
+ it "handles data-* attributes" do
98
+ if Watir.prefer_css?
99
+ expect_one :css, 'div[data-name="foo"]'
100
+ else
101
+ expect_one :xpath, ".//div[@data-name='foo']"
102
+ end
103
+
104
+ locate_one tag_name: "div",
105
+ data_name: "foo"
106
+ end
107
+
108
+ it "handles aria-* attributes" do
109
+ if Watir.prefer_css?
110
+ expect_one :css, 'div[aria-label="foo"]'
111
+ else
112
+ expect_one :xpath, ".//div[@aria-label='foo']"
113
+ end
114
+
115
+ locate_one tag_name: "div",
116
+ aria_label: "foo"
117
+ end
118
+
119
+ it "normalizes space for the :href attribute" do
120
+ if Watir.prefer_css?
121
+ expect_one :css, 'a[href~="foo"]'
122
+ else
123
+ expect_one :xpath, ".//a[normalize-space(@href)='foo']"
124
+ end
125
+
126
+ selector = {
127
+ tag_name: "a",
128
+ href: "foo"
129
+ }
130
+
131
+ locate_one selector, Watir::Anchor.attributes
132
+ end
133
+
134
+ it "wraps :type attribute with translate() for upper case values" do
135
+ translated_type = "translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"
136
+ expect_one :xpath, ".//input[#{translated_type}='file']"
137
+
138
+ selector = [
139
+ :tag_name, "input",
140
+ :type , "file",
141
+ ]
142
+
143
+ locate_one selector, Watir::Input.attributes
144
+ end
145
+
146
+ it "uses the corresponding <label>'s @for attribute or parent::label when locating by label" do
147
+ translated_type = "translate(@type,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"
148
+ expect_one :xpath, ".//input[#{translated_type}='text' and (@id=//label[normalize-space()='foo']/@for or parent::label[normalize-space()='foo'])]"
149
+
150
+ selector = [
151
+ :tag_name, "input",
152
+ :type , "text",
153
+ :label , "foo"
154
+ ]
155
+
156
+ locate_one selector, Watir::Input.attributes
157
+ end
158
+
159
+ it "uses label attribute if it is valid for element" do
160
+ expect_one :xpath, ".//option[@label='foo']"
161
+
162
+ selector = { tag_name: "option", label: "foo" }
163
+ locate_one selector, Watir::Option.attributes
164
+ end
165
+
166
+ it "translates ruby attribute names to content attribute names" do
167
+ if Watir.prefer_css?
168
+ expect_one :css, 'meta[http-equiv="foo"]'
169
+ else
170
+ expect_one :xpath, ".//meta[@http-equiv='foo']"
171
+ end
172
+
173
+ selector = {
174
+ tag_name: "meta",
175
+ http_equiv: "foo"
176
+ }
177
+
178
+ locate_one selector, Watir::Meta.attributes
179
+
180
+ # TODO: check edge cases
181
+ end
182
+ end
183
+
184
+ describe "with regexp selectors" do
185
+ it "handles selector with tag name and a single regexp attribute" do
186
+ elements = [
187
+ element(tag_name: "div", attributes: { class: "foo" }),
188
+ element(tag_name: "div", attributes: { class: "foob"})
189
+ ]
190
+
191
+ if Watir.prefer_css?
192
+ expect_all(:css, "div").and_return(elements)
193
+ else
194
+ expect_all(:xpath, "(.//div)[contains(@class, 'oob')]").and_return(elements)
195
+ end
196
+
197
+ expect(locate_one(tag_name: "div", class: /oob/)).to eq elements[1]
198
+ end
199
+
200
+ it "handles :tag_name, :index and a single regexp attribute" do
201
+ elements = [
202
+ element(tag_name: "div", attributes: { class: "foo" }),
203
+ element(tag_name: "div", attributes: { class: "foo" })
204
+ ]
205
+
206
+ if Watir.prefer_css?
207
+ expect_all(:css, "div").and_return(elements)
208
+ else
209
+ expect_all(:xpath, "(.//div)[contains(@class, 'foo')]").and_return(elements)
210
+ end
211
+
212
+ selector = {
213
+ tag_name: "div",
214
+ class: /foo/,
215
+ index: 1
216
+ }
217
+
218
+ expect(locate_one(selector)).to eq elements[1]
219
+ end
220
+
221
+ it "handles :xpath and :index selectors" do
222
+ elements = [
223
+ element(tag_name: "div", attributes: { class: "foo" }),
224
+ element(tag_name: "div", attributes: { class: "foo" })
225
+ ]
226
+
227
+ expect_all(:xpath, './/div[@class="foo"]').and_return(elements)
228
+
229
+ selector = {
230
+ xpath: './/div[@class="foo"]',
231
+ index: 1
232
+ }
233
+
234
+ expect(locate_one(selector)).to eq elements[1]
235
+ end
236
+
237
+ it "handles :css and :index selectors" do
238
+ elements = [
239
+ element(tag_name: "div", attributes: { class: "foo" }),
240
+ element(tag_name: "div", attributes: { class: "foo" })
241
+ ]
242
+
243
+ expect_all(:css, 'div[class="foo"]').and_return(elements)
244
+
245
+ selector = {
246
+ css: 'div[class="foo"]',
247
+ index: 1
248
+ }
249
+
250
+ expect(locate_one(selector)).to eq elements[1]
251
+ end
252
+
253
+ it "handles mix of string and regexp attributes" do
254
+ elements = [
255
+ element(tag_name: "div", attributes: { dir: "foo", title: "bar" }),
256
+ element(tag_name: "div", attributes: { dir: "foo", title: "baz" })
257
+ ]
258
+
259
+ if Watir.prefer_css?
260
+ expect_all(:css, 'div[dir="foo"]').and_return(elements)
261
+ else
262
+ expect_all(:xpath, "(.//div[@dir='foo'])[contains(@title, 'baz')]").and_return(elements)
263
+ end
264
+
265
+ selector = {
266
+ tag_name: "div",
267
+ dir: "foo",
268
+ title: /baz/
269
+ }
270
+
271
+ expect(locate_one(selector)).to eq elements[1]
272
+ end
273
+
274
+ it "handles data-* attributes with regexp" do
275
+ elements = [
276
+ element(tag_name: "div", attributes: { :'data-automation-id' => "foo" }),
277
+ element(tag_name: "div", attributes: { :'data-automation-id' => "bar" })
278
+ ]
279
+
280
+ if Watir.prefer_css?
281
+ expect_all(:css, 'div').and_return(elements)
282
+ else
283
+ expect_all(:xpath, "(.//div)[contains(@data-automation-id, 'bar')]").and_return(elements)
284
+ end
285
+
286
+
287
+ selector = {
288
+ tag_name: "div",
289
+ data_automation_id: /bar/
290
+ }
291
+
292
+ expect(locate_one(selector)).to eq elements[1]
293
+ end
294
+
295
+ it "handles :label => /regexp/ selector" do
296
+ label_elements = [
297
+ element(tag_name: "label", text: "foo", attributes: { for: "bar"}),
298
+ element(tag_name: "label", text: "foob", attributes: { for: "baz"})
299
+ ]
300
+ div_elements = [element(tag_name: "div")]
301
+
302
+ expect_all(:tag_name, "label").ordered.and_return(label_elements)
303
+
304
+ if Watir.prefer_css?
305
+ expect_all(:css, 'div[id="baz"]').ordered.and_return(div_elements)
306
+ else
307
+ expect_all(:xpath, ".//div[@id='baz']").ordered.and_return(div_elements)
308
+ end
309
+
310
+ expect(locate_one(tag_name: "div", label: /oob/)).to eq div_elements.first
311
+ end
312
+
313
+ it "returns nil when no label matching the regexp is found" do
314
+ expect_all(:tag_name, "label").and_return([])
315
+ expect(locate_one(tag_name: "div", label: /foo/)).to be_nil
316
+ end
317
+ end
318
+
319
+ it "finds all if :index is given" do
320
+ # or could we use XPath indeces reliably instead?
321
+ elements = [
322
+ element(tag_name: "div"),
323
+ element(tag_name: "div")
324
+ ]
325
+
326
+ if Watir.prefer_css?
327
+ expect_all(:css, 'div[dir="foo"]').and_return(elements)
328
+ else
329
+ expect_all(:xpath, ".//div[@dir='foo']").and_return(elements)
330
+ end
331
+
332
+ selector = {
333
+ tag_name: "div",
334
+ dir: "foo",
335
+ index: 1
336
+ }
337
+
338
+ expect(locate_one(selector)).to eq elements[1]
339
+ end
340
+
341
+ it "returns nil if found element didn't match the selector tag_name" do
342
+ expect_one(:xpath, "//div").and_return(element(tag_name: "div"))
343
+
344
+ selector = {
345
+ tag_name: "input",
346
+ xpath: "//div"
347
+ }
348
+
349
+ expect(locate_one(selector, Watir::Input.attributes)).to be_nil
350
+ end
351
+
352
+ describe "errors" do
353
+ it "raises a TypeError if :index is not a Fixnum" do
354
+ expect { locate_one(tag_name: "div", index: "bar") }.to \
355
+ raise_error(TypeError, %[expected Fixnum, got "bar":String])
356
+ end
357
+
358
+ it "raises a TypeError if selector value is not a String or Regexp" do
359
+ expect { locate_one(tag_name: 123) }.to \
360
+ raise_error(TypeError, %[expected one of [String, Regexp], got 123:Fixnum])
361
+ end
362
+
363
+ it "raises a MissingWayOfFindingObjectException if the attribute is not valid" do
364
+ bad_selector = {tag_name: "input", href: "foo"}
365
+ valid_attributes = Watir::Input.attributes
366
+
367
+ expect { locate_one(bad_selector, valid_attributes) }.to \
368
+ raise_error(Watir::Exception::MissingWayOfFindingObjectException, "invalid attribute: :href")
369
+ end
370
+ end
371
+ end
372
+
373
+ describe "finds several elements" do
374
+ describe "by delegating to Selenium" do
375
+ SELENIUM_SELECTORS.each do |loc|
376
+ it "delegates to Selenium's #{loc} locator" do
377
+ expect_all(loc, "bar").and_return([element(tag_name: "div")])
378
+ locate_all(loc => "bar")
379
+ end
380
+ end
381
+ end
382
+
383
+ describe "with an empty selector" do
384
+ it "finds all when an empty selctor is given" do
385
+ if Watir.prefer_css?
386
+ expect_all :css, '*'
387
+ else
388
+ expect_all :xpath, './/*'
389
+ end
390
+
391
+ locate_all({})
392
+ end
393
+ end
394
+
395
+ describe "with selectors not supported by Selenium" do
396
+ it "handles selector with tag name and a single attribute" do
397
+ if Watir.prefer_css?
398
+ expect_all :css, 'div[dir="foo"]'
399
+ else
400
+ expect_all :xpath, ".//div[@dir='foo']"
401
+ end
402
+
403
+ locate_all tag_name: "div",
404
+ dir: "foo"
405
+ end
406
+
407
+ it "handles selector with tag name and multiple attributes" do
408
+ if Watir.prefer_css?
409
+ expect_all :css, 'div[dir="foo"][title="bar"]'
410
+ else
411
+ expect_all :xpath, ".//div[@dir='foo' and @title='bar']"
412
+ end
413
+
414
+ locate_all [:tag_name, "div",
415
+ :dir , "foo",
416
+ :title , 'bar']
417
+ end
418
+ end
419
+
420
+ describe "with regexp selectors" do
421
+ it "handles selector with tag name and a single regexp attribute" do
422
+ elements = [
423
+ element(tag_name: "div", attributes: { class: "foo" }),
424
+ element(tag_name: "div", attributes: { class: "foob"}),
425
+ element(tag_name: "div", attributes: { class: "doob"}),
426
+ element(tag_name: "div", attributes: { class: "noob"})
427
+ ]
428
+
429
+ if Watir.prefer_css?
430
+ expect_all(:css, "div").and_return(elements)
431
+ else
432
+ expect_all(:xpath, "(.//div)[contains(@class, 'oob')]").and_return(elements)
433
+ end
434
+
435
+ expect(locate_all(tag_name: "div", class: /oob/)).to eq elements.last(3)
436
+ end
437
+
438
+ it "handles mix of string and regexp attributes" do
439
+ elements = [
440
+ element(tag_name: "div", attributes: { dir: "foo", title: "bar" }),
441
+ element(tag_name: "div", attributes: { dir: "foo", title: "baz" }),
442
+ element(tag_name: "div", attributes: { dir: "foo", title: "bazt"})
443
+ ]
444
+
445
+ if Watir.prefer_css?
446
+ expect_all(:css, 'div[dir="foo"]').and_return(elements)
447
+ else
448
+ expect_all(:xpath, "(.//div[@dir='foo'])[contains(@title, 'baz')]").and_return(elements)
449
+ end
450
+
451
+ selector = {
452
+ tag_name: "div",
453
+ dir: "foo",
454
+ title: /baz/
455
+ }
456
+
457
+ expect(locate_all(selector)).to eq elements.last(2)
458
+ end
459
+
460
+ context "and xpath" do
461
+ before do
462
+ expect(Watir).to receive(:prefer_css?).and_return(false)
463
+ end
464
+
465
+ it "converts a leading run of regexp literals to a contains() expression" do
466
+ elements = [
467
+ element(tag_name: "div", attributes: { class: "foo" }),
468
+ element(tag_name: "div", attributes: { class: "foob" }),
469
+ element(tag_name: "div", attributes: { class: "bar" })
470
+ ]
471
+
472
+ expect_all(:xpath, "(.//div)[contains(@class, 'fo')]").and_return(elements.first(2))
473
+
474
+ expect(locate_one(tag_name: "div", class: /fo.b$/)).to eq elements[1]
475
+ end
476
+
477
+ it "converts a trailing run of regexp literals to a contains() expression" do
478
+ elements = [
479
+ element(tag_name: "div", attributes: { class: "foo" }),
480
+ element(tag_name: "div", attributes: { class: "foob" })
481
+ ]
482
+
483
+ expect_all(:xpath, "(.//div)[contains(@class, 'b')]").and_return(elements.last(1))
484
+
485
+ expect(locate_one(tag_name: "div", class: /^fo.b/)).to eq elements[1]
486
+ end
487
+
488
+ it "converts a leading and a trailing run of regexp literals to a contains() expression" do
489
+ elements = [
490
+ element(tag_name: "div", attributes: { class: "foo" }),
491
+ element(tag_name: "div", attributes: { class: "foob" })
492
+ ]
493
+
494
+ expect_all(:xpath, "(.//div)[contains(@class, 'fo') and contains(@class, 'b')]").
495
+ and_return(elements.last(1))
496
+
497
+ expect(locate_one(tag_name: "div", class: /fo.b/)).to eq elements[1]
498
+ end
499
+
500
+ it "does not try to convert case insensitive expressions" do
501
+ elements = [
502
+ element(tag_name: "div", attributes: { class: "foo" }),
503
+ element(tag_name: "div", attributes: { class: "foob"})
504
+ ]
505
+
506
+ expect_all(:xpath, ".//div").and_return(elements.last(1))
507
+
508
+ expect(locate_one(tag_name: "div", class: /FOOB/i)).to eq elements[1]
509
+ end
510
+
511
+ it "does not try to convert expressions containing '|'" do
512
+ elements = [
513
+ element(tag_name: "div", attributes: { class: "foo" }),
514
+ element(tag_name: "div", attributes: { class: "foob"})
515
+ ]
516
+
517
+ expect_all(:xpath, ".//div").and_return(elements.last(1))
518
+
519
+ expect(locate_one(tag_name: "div", class: /x|b/)).to eq elements[1]
520
+ end
521
+ end
522
+ end
523
+
524
+ describe "errors" do
525
+ it "raises ArgumentError if :index is given" do
526
+ expect { locate_all(tag_name: "div", index: 1) }.to \
527
+ raise_error(ArgumentError, "can't locate all elements by :index")
528
+ end
529
+ end
530
+ end
531
+
532
+ end