watir 6.14.0 → 6.15.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +24 -6
- data/CHANGES.md +10 -0
- data/Gemfile +0 -2
- data/README.md +1 -1
- data/Rakefile +2 -2
- data/lib/watir.rb +2 -3
- data/lib/watir/adjacent.rb +2 -2
- data/lib/watir/alert.rb +5 -9
- data/lib/watir/attribute_helper.rb +2 -3
- data/lib/watir/browser.rb +5 -17
- data/lib/watir/capabilities.rb +11 -0
- data/lib/watir/cell_container.rb +2 -2
- data/lib/watir/container.rb +7 -8
- data/lib/watir/cookies.rb +2 -12
- data/lib/watir/element_collection.rb +2 -2
- data/lib/watir/elements/button.rb +2 -11
- data/lib/watir/elements/element.rb +43 -25
- data/lib/watir/elements/hidden.rb +1 -1
- data/lib/watir/elements/iframe.rb +7 -5
- data/lib/watir/elements/option.rb +2 -13
- data/lib/watir/elements/select.rb +6 -22
- data/lib/watir/elements/table.rb +2 -2
- data/lib/watir/exception.rb +1 -2
- data/lib/watir/generator/base/idl_sorter.rb +1 -1
- data/lib/watir/generator/base/spec_extractor.rb +2 -2
- data/lib/watir/generator/base/visitor.rb +1 -1
- data/lib/watir/generator/html/generator.rb +0 -1
- data/lib/watir/generator/html/spec_extractor.rb +1 -1
- data/lib/watir/generator/html/visitor.rb +1 -1
- data/lib/watir/generator/svg/spec_extractor.rb +1 -1
- data/lib/watir/generator/svg/visitor.rb +1 -1
- data/lib/watir/js_execution.rb +11 -0
- data/lib/watir/js_snippets.rb +1 -1
- data/lib/watir/js_snippets/elementObscured.js +14 -0
- data/lib/watir/js_snippets/selectedText.js +17 -0
- data/lib/watir/legacy_wait.rb +5 -5
- data/lib/watir/locators.rb +16 -5
- data/lib/watir/locators/anchor/selector_builder.rb +38 -0
- data/lib/watir/locators/button/locator.rb +14 -12
- data/lib/watir/locators/button/selector_builder.rb +0 -22
- data/lib/watir/locators/button/selector_builder/xpath.rb +67 -12
- data/lib/watir/locators/button/validator.rb +2 -1
- data/lib/watir/locators/cell/selector_builder.rb +0 -14
- data/lib/watir/locators/cell/selector_builder/xpath.rb +21 -0
- data/lib/watir/locators/element/locator.rb +60 -153
- data/lib/watir/locators/element/selector_builder.rb +103 -84
- data/lib/watir/locators/element/selector_builder/regexp_disassembler.rb +66 -0
- data/lib/watir/locators/element/selector_builder/xpath.rb +195 -82
- data/lib/watir/locators/element/selector_builder/xpath_support.rb +27 -0
- data/lib/watir/locators/element/validator.rb +2 -9
- data/lib/watir/locators/row/selector_builder.rb +5 -22
- data/lib/watir/locators/row/selector_builder/xpath.rb +53 -0
- data/lib/watir/locators/text_area/selector_builder.rb +1 -1
- data/lib/watir/locators/text_area/selector_builder/xpath.rb +19 -0
- data/lib/watir/locators/text_field/locator.rb +11 -8
- data/lib/watir/locators/text_field/selector_builder.rb +0 -23
- data/lib/watir/locators/text_field/selector_builder/xpath.rb +33 -4
- data/lib/watir/locators/text_field/validator.rb +4 -4
- data/lib/watir/radio_set.rb +5 -5
- data/lib/watir/row_container.rb +2 -2
- data/lib/watir/user_editable.rb +2 -2
- data/lib/watir/version.rb +1 -1
- data/lib/watir/wait.rb +24 -37
- data/lib/watir/window.rb +11 -8
- data/lib/watirspec/remote_server.rb +3 -1
- data/spec/locator_spec_helper.rb +1 -1
- data/spec/spec_helper.rb +25 -1
- data/spec/unit/anchor_locator_spec.rb +68 -0
- data/spec/unit/capabilities_spec.rb +27 -0
- data/spec/unit/element_locator_spec.rb +184 -101
- data/spec/unit/logger_spec.rb +5 -0
- data/spec/watirspec/adjacent_spec.rb +34 -34
- data/spec/watirspec/after_hooks_spec.rb +78 -35
- data/spec/watirspec/alert_spec.rb +10 -0
- data/spec/watirspec/browser_spec.rb +27 -1
- data/spec/watirspec/element_hidden_spec.rb +6 -0
- data/spec/watirspec/elements/button_spec.rb +5 -11
- data/spec/watirspec/elements/buttons_spec.rb +1 -1
- data/spec/watirspec/elements/checkbox_spec.rb +2 -15
- data/spec/watirspec/elements/date_time_field_spec.rb +6 -1
- data/spec/watirspec/elements/dd_spec.rb +0 -17
- data/spec/watirspec/elements/del_spec.rb +0 -14
- data/spec/watirspec/elements/div_spec.rb +0 -18
- data/spec/watirspec/elements/dl_spec.rb +0 -17
- data/spec/watirspec/elements/dt_spec.rb +0 -17
- data/spec/watirspec/elements/element_spec.rb +177 -17
- data/spec/watirspec/elements/elements_spec.rb +7 -6
- data/spec/watirspec/elements/em_spec.rb +0 -13
- data/spec/watirspec/elements/filefield_spec.rb +0 -11
- data/spec/watirspec/elements/form_spec.rb +6 -0
- data/spec/watirspec/elements/hn_spec.rb +0 -14
- data/spec/watirspec/elements/iframe_spec.rb +15 -0
- data/spec/watirspec/elements/ins_spec.rb +0 -14
- data/spec/watirspec/elements/labels_spec.rb +1 -1
- data/spec/watirspec/elements/li_spec.rb +0 -14
- data/spec/watirspec/elements/link_spec.rb +22 -14
- data/spec/watirspec/elements/links_spec.rb +13 -0
- data/spec/watirspec/elements/list_spec.rb +15 -0
- data/spec/watirspec/elements/ol_spec.rb +0 -14
- data/spec/watirspec/elements/option_spec.rb +0 -10
- data/spec/watirspec/elements/p_spec.rb +0 -14
- data/spec/watirspec/elements/pre_spec.rb +0 -14
- data/spec/watirspec/elements/radio_spec.rb +0 -14
- data/spec/watirspec/elements/select_list_spec.rb +0 -10
- data/spec/watirspec/elements/span_spec.rb +4 -15
- data/spec/watirspec/elements/strong_spec.rb +4 -15
- data/spec/watirspec/elements/table_nesting_spec.rb +1 -1
- data/spec/watirspec/elements/table_spec.rb +7 -0
- data/spec/watirspec/elements/text_field_spec.rb +10 -2
- data/spec/watirspec/elements/text_fields_spec.rb +1 -1
- data/spec/watirspec/elements/tr_spec.rb +1 -1
- data/spec/watirspec/elements/ul_spec.rb +0 -14
- data/spec/watirspec/html/closeable.html +8 -0
- data/spec/watirspec/html/forms_with_input_elements.html +28 -23
- data/spec/watirspec/html/nested_elements.html +9 -9
- data/spec/watirspec/html/obscured.html +34 -0
- data/spec/watirspec/html/tables.html +13 -13
- data/spec/watirspec/radio_set_spec.rb +5 -0
- data/spec/watirspec/selector_builder/button_spec.rb +254 -0
- data/spec/watirspec/selector_builder/cell_spec.rb +93 -0
- data/spec/watirspec/selector_builder/element_spec.rb +639 -0
- data/spec/watirspec/selector_builder/row_spec.rb +150 -0
- data/spec/watirspec/selector_builder/text_spec.rb +170 -0
- data/spec/watirspec/support/rspec_matchers.rb +6 -1
- data/spec/watirspec/user_editable_spec.rb +4 -0
- data/spec/watirspec/wait_spec.rb +65 -14
- data/spec/watirspec/window_switching_spec.rb +54 -1
- data/spec/watirspec_helper.rb +2 -0
- data/watir.gemspec +7 -1
- metadata +86 -8
- data/lib/watir/locators/text_area/locator.rb +0 -13
- data/lib/watir/xpath_support.rb +0 -18
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'watirspec_helper'
|
2
|
+
|
3
|
+
describe Watir::Locators::Cell::SelectorBuilder do
|
4
|
+
let(:attributes) { Watir::HTMLElement.attribute_list }
|
5
|
+
let(:selector_builder) { described_class.new(attributes) }
|
6
|
+
|
7
|
+
describe '#build' do
|
8
|
+
after(:each) do |example|
|
9
|
+
next if example.metadata[:skip_after]
|
10
|
+
|
11
|
+
@query_scope ||= browser.element(id: 'gregory').locate
|
12
|
+
|
13
|
+
built = selector_builder.build(@selector)
|
14
|
+
expect(built).to eq [@wd_locator, (@remaining || {})]
|
15
|
+
|
16
|
+
next unless @data_locator || @tag_name
|
17
|
+
|
18
|
+
expect { @located = @query_scope.wd.first(@wd_locator) }.not_to raise_exception
|
19
|
+
|
20
|
+
if @data_locator
|
21
|
+
expect(@located.attribute('data-locator')).to eq(@data_locator)
|
22
|
+
else
|
23
|
+
expect {
|
24
|
+
expect(@located.tag_name).to eq @tag_name
|
25
|
+
}.not_to raise_exception
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'without any arguments' do
|
30
|
+
browser.goto(WatirSpec.url_for('tables.html'))
|
31
|
+
@selector = {}
|
32
|
+
@wd_locator = {xpath: "./*[local-name()='th' or local-name()='td']"}
|
33
|
+
@data_locator = 'first cell'
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with index' do
|
37
|
+
before(:each) do
|
38
|
+
browser.goto(WatirSpec.url_for('tables.html'))
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'positive' do
|
42
|
+
@selector = {index: 3}
|
43
|
+
@wd_locator = {xpath: "(./*[local-name()='th' or local-name()='td'])[4]"}
|
44
|
+
@data_locator = 'after tax'
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'negative' do
|
48
|
+
@selector = {index: -3}
|
49
|
+
@wd_locator = {xpath: "(./*[local-name()='th' or local-name()='td'])[last()-2]"}
|
50
|
+
@data_locator = 'before tax'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'last' do
|
54
|
+
@selector = {index: -1}
|
55
|
+
@wd_locator = {xpath: "(./*[local-name()='th' or local-name()='td'])[last()]"}
|
56
|
+
@data_locator = 'after tax'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'does not return index if it is zero' do
|
60
|
+
@selector = {index: 0}
|
61
|
+
@wd_locator = {xpath: "./*[local-name()='th' or local-name()='td']"}
|
62
|
+
@data_locator = 'first cell'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'raises exception when index is not an Integer', skip_after: true do
|
66
|
+
selector = {index: 'foo'}
|
67
|
+
msg = 'expected Integer, got "foo":String'
|
68
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with multiple locators' do
|
73
|
+
before(:each) do
|
74
|
+
browser.goto(WatirSpec.url_for('tables.html'))
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'attribute and text' do
|
78
|
+
@selector = {headers: /before_tax/, text: '5 934'}
|
79
|
+
@wd_locator = {xpath: "./*[local-name()='th' or local-name()='td']" \
|
80
|
+
"[normalize-space()='5 934'][contains(@headers, 'before_tax')]"}
|
81
|
+
@data_locator = 'before tax'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'delegates adjacent to Element SelectorBuilder' do
|
86
|
+
@query_scope = browser.element(id: 'p3').locate
|
87
|
+
|
88
|
+
@selector = {adjacent: :ancestor, index: 2}
|
89
|
+
@wd_locator = {xpath: './ancestor::*[3]'}
|
90
|
+
@data_locator = 'top table'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,639 @@
|
|
1
|
+
require 'watirspec_helper'
|
2
|
+
|
3
|
+
describe Watir::Locators::Element::SelectorBuilder do
|
4
|
+
let(:selector_builder) { described_class.new(@attributes) }
|
5
|
+
|
6
|
+
describe '#build' do
|
7
|
+
after(:each) do |example|
|
8
|
+
next if example.metadata[:skip_after]
|
9
|
+
|
10
|
+
@attributes ||= Watir::HTMLElement.attribute_list
|
11
|
+
@query_scope ||= browser
|
12
|
+
built = selector_builder.build(@selector)
|
13
|
+
expect(built).to eq [@wd_locator, (@remaining || {})]
|
14
|
+
|
15
|
+
next unless @data_locator || @tag_name
|
16
|
+
|
17
|
+
expect { @located = @query_scope.wd.first(@wd_locator) }.not_to raise_exception
|
18
|
+
|
19
|
+
if @data_locator
|
20
|
+
expect(@located.attribute('data-locator')).to eq(@data_locator)
|
21
|
+
else
|
22
|
+
expect {
|
23
|
+
expect(@located.tag_name).to eq @tag_name
|
24
|
+
}.not_to raise_exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'without any arguments' do
|
29
|
+
@selector = {}
|
30
|
+
@wd_locator = {xpath: './/*'}
|
31
|
+
@tag_name = 'html'
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with xpath or css' do
|
35
|
+
before(:each) do
|
36
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'locates with xpath only' do
|
40
|
+
@selector = {xpath: './/div'}
|
41
|
+
@wd_locator = @selector.dup
|
42
|
+
@data_locator = 'first div'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'locates with css only' do
|
46
|
+
@selector = {css: 'div'}
|
47
|
+
@wd_locator = @selector.dup
|
48
|
+
@data_locator = 'first div'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'raises exception when using xpath & css', skip_after: true do
|
52
|
+
selector = {xpath: './/*', css: 'div'}
|
53
|
+
msg = ':xpath and :css cannot be combined ({:xpath=>".//*", :css=>"div"})'
|
54
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises exception when combining with xpath', skip_after: true do
|
58
|
+
selector = {xpath: './/*', foo: 'div'}
|
59
|
+
msg = 'xpath cannot be combined with all of these locators ({:foo=>"div"})'
|
60
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'raises exception when combining with css', skip_after: true do
|
64
|
+
selector = {css: 'div', foo: 'div'}
|
65
|
+
msg = 'css cannot be combined with all of these locators ({:foo=>"div"})'
|
66
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'raises exception when not a String', skip_after: true do
|
70
|
+
selector = {xpath: 7}
|
71
|
+
msg = /expected String, got 7:(Fixnum|Integer)/
|
72
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
73
|
+
end
|
74
|
+
|
75
|
+
xcontext 'Ideal Behavior' do
|
76
|
+
it 'xpath' do
|
77
|
+
selector = {xpath: ".//*[@id='foo']", tag_name: 'div'}
|
78
|
+
expected = {xpath: ".//*[local-name()='div'][@id='foo']"}
|
79
|
+
|
80
|
+
expect_built(selector, expected)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'css' do
|
84
|
+
selector = {css: '.bar', tag_name: 'div'}
|
85
|
+
expected = {css: 'div.bar'}
|
86
|
+
|
87
|
+
expect_built(selector, expected)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with tag_name' do
|
93
|
+
before(:each) do
|
94
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'with String equals' do
|
98
|
+
@selector = {tag_name: 'div'}
|
99
|
+
@wd_locator = {xpath: ".//*[local-name()='div']"}
|
100
|
+
@data_locator = 'first div'
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'with simple Regexp contains' do
|
104
|
+
@selector = {tag_name: /div/}
|
105
|
+
@wd_locator = {xpath: ".//*[contains(local-name(), 'div')]"}
|
106
|
+
@data_locator = 'first div'
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'with Symbol' do
|
110
|
+
@selector = {tag_name: :div}
|
111
|
+
@wd_locator = {xpath: ".//*[local-name()='div']"}
|
112
|
+
@data_locator = 'first div'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'raises exception when not a String or Regexp', skip_after: true do
|
116
|
+
selector = {tag_name: 7}
|
117
|
+
msg = /expected string_or_regexp_or_symbol, got 7:(Fixnum|Integer)/
|
118
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with class names' do
|
123
|
+
before(:each) do
|
124
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'class_name is converted to class' do
|
128
|
+
@selector = {class_name: 'user'}
|
129
|
+
@wd_locator = {xpath: ".//*[contains(concat(' ', @class, ' '), ' user ')]"}
|
130
|
+
@data_locator = 'form'
|
131
|
+
end
|
132
|
+
|
133
|
+
# TODO: This functionality is deprecated with "class_array"
|
134
|
+
it 'values with spaces' do
|
135
|
+
@selector = {class_name: 'multiple classes here'}
|
136
|
+
@wd_locator = {xpath: ".//*[contains(concat(' ', @class, ' '), ' multiple classes here ')]"}
|
137
|
+
@data_locator = 'first div'
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'single String concatenates' do
|
141
|
+
@selector = {class: 'user'}
|
142
|
+
@wd_locator = {xpath: ".//*[contains(concat(' ', @class, ' '), ' user ')]"}
|
143
|
+
@data_locator = 'form'
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'Array of String concatenates with and' do
|
147
|
+
@selector = {class: %w[multiple here]}
|
148
|
+
@wd_locator = {xpath: ".//*[contains(concat(' ', @class, ' '), ' multiple ') and " \
|
149
|
+
"contains(concat(' ', @class, ' '), ' here ')]"}
|
150
|
+
@data_locator = 'first div'
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'simple Regexp contains' do
|
154
|
+
@selector = {class_name: /use/}
|
155
|
+
@wd_locator = {xpath: ".//*[contains(@class, 'use')]"}
|
156
|
+
@data_locator = 'form'
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'Array of Regexp contains with and' do
|
160
|
+
@selector = {class: [/mult/, /her/]}
|
161
|
+
@wd_locator = {xpath: ".//*[contains(@class, 'mult') and contains(@class, 'her')]"}
|
162
|
+
@data_locator = 'first div'
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'single negated String concatenates with not' do
|
166
|
+
@selector = {class: '!multiple'}
|
167
|
+
@wd_locator = {xpath: ".//*[not(contains(concat(' ', @class, ' '), ' multiple '))]"}
|
168
|
+
@tag_name = 'html'
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'single Boolean true provides the at' do
|
172
|
+
@selector = {class: true}
|
173
|
+
@wd_locator = {xpath: './/*[@class]'}
|
174
|
+
@data_locator = 'first div'
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'single Boolean false provides the not atat' do
|
178
|
+
@selector = {class: false}
|
179
|
+
@wd_locator = {xpath: './/*[not(@class)]'}
|
180
|
+
@tag_name = 'html'
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'Array of mixed String, Regexp and Boolean contains and concatenates with and and not' do
|
184
|
+
@selector = {class: [/mult/, 'classes', '!here']}
|
185
|
+
@wd_locator = {xpath: ".//*[contains(@class, 'mult') and contains(concat(' ', @class, ' '), ' classes ') " \
|
186
|
+
"and not(contains(concat(' ', @class, ' '), ' here '))]"}
|
187
|
+
@data_locator = 'second div'
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'raises exception when not a String or Regexp or Array', skip_after: true do
|
191
|
+
selector = {class: 7}
|
192
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 7:(Fixnum|Integer)/
|
193
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'raises exception when Array values are not a String or Regexp', skip_after: true do
|
197
|
+
selector = {class: [7]}
|
198
|
+
msg = /expected string_or_regexp, got 7:(Fixnum|Integer)/
|
199
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'raises exception when class and class_name are both used', skip_after: true do
|
203
|
+
selector = {class: 'foo', class_name: 'bar'}
|
204
|
+
msg = 'Can not use both :class and :class_name locators'
|
205
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'raises exception when class array is empty', skip_after: true do
|
209
|
+
selector = {class: []}
|
210
|
+
msg = 'Can not locate elements with an empty Array for :class'
|
211
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context 'with attributes as predicates' do
|
216
|
+
before(:each) do
|
217
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'with href attribute' do
|
221
|
+
@selector = {href: 'watirspec.css'}
|
222
|
+
@wd_locator = {xpath: ".//*[normalize-space(@href)='watirspec.css']"}
|
223
|
+
@data_locator = 'link'
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'with string attribute' do
|
227
|
+
@selector = {'name' => 'user_new'}
|
228
|
+
@wd_locator = {xpath: ".//*[@name='user_new']"}
|
229
|
+
@data_locator = 'form'
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'with String equals' do
|
233
|
+
@selector = {name: 'user_new'}
|
234
|
+
@wd_locator = {xpath: ".//*[@name='user_new']"}
|
235
|
+
@data_locator = 'form'
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'with TrueClass no equals' do
|
239
|
+
@selector = {tag_name: 'input', name: true}
|
240
|
+
@wd_locator = {xpath: ".//*[local-name()='input'][@name]"}
|
241
|
+
@data_locator = 'input name'
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'with FalseClass not with no equals' do
|
245
|
+
@selector = {tag_name: 'input', name: false}
|
246
|
+
@wd_locator = {xpath: ".//*[local-name()='input'][not(@name)]"}
|
247
|
+
@data_locator = 'input nameless'
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'with multiple attributes: no equals and not with no equals and equals' do
|
251
|
+
@selector = {readonly: true, foo: false, id: 'good_luck'}
|
252
|
+
@wd_locator = {xpath: ".//*[@readonly and not(@foo) and @id='good_luck']"}
|
253
|
+
@data_locator = 'Good Luck'
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'raises exception when attribute value is not a Boolean, String or Regexp', skip_after: true do
|
257
|
+
selector = {foo: 7}
|
258
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 7:(Fixnum|Integer)/
|
259
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'raises exception when attribute key is not a String or Regexp', skip_after: true do
|
263
|
+
selector = {7 => 'foo'}
|
264
|
+
msg = /Unable to build XPath using 7:(Fixnum|Integer)/
|
265
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context 'with attributes as partials' do
|
270
|
+
before(:each) do
|
271
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'with Regexp' do
|
275
|
+
@selector = {name: /user/}
|
276
|
+
@wd_locator = {xpath: ".//*[contains(@name, 'user')]"}
|
277
|
+
@data_locator = 'form'
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'with multiple Regexp attributes separated by and' do
|
281
|
+
@selector = {readonly: /read/, id: /good/}
|
282
|
+
@wd_locator = {xpath: ".//*[contains(@readonly, 'read') and contains(@id, 'good')]"}
|
283
|
+
@data_locator = 'Good Luck'
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context 'with text' do
|
288
|
+
before(:each) do
|
289
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'String uses normalize space equals' do
|
293
|
+
@selector = {text: 'Add user'}
|
294
|
+
@wd_locator = {xpath: ".//*[normalize-space()='Add user']"}
|
295
|
+
@data_locator = 'add user'
|
296
|
+
end
|
297
|
+
|
298
|
+
# Deprecated with :caption
|
299
|
+
it 'with caption attribute' do
|
300
|
+
@selector = {caption: 'Add user'}
|
301
|
+
@wd_locator = {xpath: ".//*[normalize-space()='Add user']"}
|
302
|
+
@data_locator = 'add user'
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'raises exception when text is not a String or Regexp', skip_after: true do
|
306
|
+
selector = {text: 7}
|
307
|
+
msg = /expected string_or_regexp, got 7:(Fixnum|Integer)/
|
308
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context 'with index' do
|
313
|
+
before(:each) do
|
314
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'positive' do
|
318
|
+
@selector = {tag_name: 'div', index: 7}
|
319
|
+
@wd_locator = {xpath: "(.//*[local-name()='div'])[8]"}
|
320
|
+
@data_locator = 'content'
|
321
|
+
end
|
322
|
+
|
323
|
+
it 'negative' do
|
324
|
+
@selector = {tag_name: 'div', index: -7}
|
325
|
+
@wd_locator = {xpath: "(.//*[local-name()='div'])[last()-6]"}
|
326
|
+
@data_locator = 'second div'
|
327
|
+
end
|
328
|
+
|
329
|
+
it 'last' do
|
330
|
+
@selector = {tag_name: 'div', index: -1}
|
331
|
+
@wd_locator = {xpath: "(.//*[local-name()='div'])[last()]"}
|
332
|
+
@data_locator = 'content'
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'does not return index if it is zero' do
|
336
|
+
@selector = {tag_name: 'div', index: 0}
|
337
|
+
@wd_locator = {xpath: ".//*[local-name()='div']"}
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'raises exception when index is not an Integer', skip_after: true do
|
341
|
+
selector = {index: 'foo'}
|
342
|
+
msg = 'expected Integer, got "foo":String'
|
343
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'with labels' do
|
348
|
+
before(:each) do
|
349
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'locates the element associated with the label element located by the text of the provided label key' do
|
353
|
+
@selector = {label: 'Cars'}
|
354
|
+
@wd_locator = {xpath: ".//*[(@id=//label[normalize-space()='Cars']/@for "\
|
355
|
+
"or parent::label[normalize-space()='Cars'])]"}
|
356
|
+
@data_locator = 'cars'
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'does not use the label element when label is a valid attribute' do
|
360
|
+
@attributes ||= Watir::Option.attribute_list
|
361
|
+
|
362
|
+
@selector = {tag_name: 'option', label: 'Germany'}
|
363
|
+
@wd_locator = {xpath: ".//*[local-name()='option'][@label='Germany']"}
|
364
|
+
@data_locator = 'Berliner'
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'with adjacent locators' do
|
369
|
+
before(:each) do
|
370
|
+
browser.goto(WatirSpec.url_for('nested_elements.html'))
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'raises exception when not a Symbol', skip_after: true do
|
374
|
+
selector = {adjacent: 'foo', index: 0}
|
375
|
+
msg = 'expected Symbol, got "foo":String'
|
376
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'raises exception when not a valid value', skip_after: true do
|
380
|
+
selector = {adjacent: :foo, index: 0}
|
381
|
+
msg = 'Unable to process adjacent locator with foo'
|
382
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
383
|
+
end
|
384
|
+
|
385
|
+
describe '#parent' do
|
386
|
+
it 'with no other arguments' do
|
387
|
+
@query_scope = browser.div(id: 'first_sibling')
|
388
|
+
@selector = {adjacent: :ancestor, index: 0}
|
389
|
+
@wd_locator = {xpath: './ancestor::*[1]'}
|
390
|
+
@data_locator = 'parent'
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'with index' do
|
394
|
+
@query_scope = browser.div(id: 'first_sibling')
|
395
|
+
@selector = {adjacent: :ancestor, index: 2}
|
396
|
+
@wd_locator = {xpath: './ancestor::*[3]'}
|
397
|
+
@data_locator = 'grandparent'
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'with multiple locators' do
|
401
|
+
@query_scope = browser.div(id: 'first_sibling')
|
402
|
+
@selector = {adjacent: :ancestor, id: true, tag_name: 'div', class: 'ancestor', index: 1}
|
403
|
+
@wd_locator = {xpath: "./ancestor::*[local-name()='div']"\
|
404
|
+
"[contains(concat(' ', @class, ' '), ' ancestor ')][@id][2]"}
|
405
|
+
@data_locator = 'grandparent'
|
406
|
+
end
|
407
|
+
|
408
|
+
it 'raises an exception if text locator is used', skip_after: true do
|
409
|
+
selector = {adjacent: :ancestor, index: 0, text: 'Foo'}
|
410
|
+
msg = 'Can not find parent element with text locator'
|
411
|
+
expect { selector_builder.build(selector) }
|
412
|
+
.to raise_exception Watir::Exception::LocatorException, msg
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
describe '#following_sibling' do
|
417
|
+
it 'with no other arguments' do
|
418
|
+
@query_scope = browser.div(id: 'first_sibling')
|
419
|
+
@selector = {adjacent: :following, index: 0}
|
420
|
+
@wd_locator = {xpath: './following-sibling::*[1]'}
|
421
|
+
@data_locator = 'between_siblings1'
|
422
|
+
end
|
423
|
+
|
424
|
+
it 'with index' do
|
425
|
+
@query_scope = browser.div(id: 'first_sibling')
|
426
|
+
@selector = {adjacent: :following, index: 2}
|
427
|
+
@wd_locator = {xpath: './following-sibling::*[3]'}
|
428
|
+
@data_locator = 'between_siblings2'
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'with multiple locators' do
|
432
|
+
@query_scope = browser.div(id: 'first_sibling')
|
433
|
+
@selector = {adjacent: :following, tag_name: 'div', class: 'b', index: 0, id: true}
|
434
|
+
@wd_locator = {xpath: "./following-sibling::*[local-name()='div']"\
|
435
|
+
"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
|
436
|
+
@data_locator = 'second_sibling'
|
437
|
+
end
|
438
|
+
|
439
|
+
it 'with text' do
|
440
|
+
@query_scope = browser.div(id: 'first_sibling')
|
441
|
+
@selector = {adjacent: :following, text: 'Third', index: 0}
|
442
|
+
@wd_locator = {xpath: "./following-sibling::*[normalize-space()='Third'][1]"}
|
443
|
+
@data_locator = 'third_sibling'
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe '#previous_sibling' do
|
448
|
+
it 'with no other arguments' do
|
449
|
+
@query_scope = browser.div(id: 'third_sibling')
|
450
|
+
@selector = {adjacent: :preceding, index: 0}
|
451
|
+
@wd_locator = {xpath: './preceding-sibling::*[1]'}
|
452
|
+
@data_locator = 'between_siblings2'
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'with index' do
|
456
|
+
@query_scope = browser.div(id: 'third_sibling')
|
457
|
+
@selector = {adjacent: :preceding, index: 2}
|
458
|
+
@wd_locator = {xpath: './preceding-sibling::*[3]'}
|
459
|
+
@data_locator = 'between_siblings1'
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'with multiple locators' do
|
463
|
+
@query_scope = browser.div(id: 'third_sibling')
|
464
|
+
@selector = {adjacent: :preceding, tag_name: 'div', class: 'b', id: true, index: 0}
|
465
|
+
@wd_locator = {xpath: "./preceding-sibling::*[local-name()='div']"\
|
466
|
+
"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
|
467
|
+
@data_locator = 'second_sibling'
|
468
|
+
end
|
469
|
+
|
470
|
+
it 'with text' do
|
471
|
+
@query_scope = browser.div(id: 'third_sibling')
|
472
|
+
@selector = {adjacent: :preceding, text: 'Second', index: 0}
|
473
|
+
@wd_locator = {xpath: "./preceding-sibling::*[normalize-space()='Second'][1]"}
|
474
|
+
@data_locator = 'second_sibling'
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
describe '#child' do
|
479
|
+
it 'with no other arguments' do
|
480
|
+
@query_scope = browser.div(id: 'first_sibling')
|
481
|
+
@selector = {adjacent: :child, index: 0}
|
482
|
+
@wd_locator = {xpath: './child::*[1]'}
|
483
|
+
@data_locator = 'child span'
|
484
|
+
end
|
485
|
+
|
486
|
+
it 'with index' do
|
487
|
+
@query_scope = browser.div(id: 'parent')
|
488
|
+
@selector = {adjacent: :child, index: 2}
|
489
|
+
@wd_locator = {xpath: './child::*[3]'}
|
490
|
+
@data_locator = 'second_sibling'
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'with multiple locators' do
|
494
|
+
@query_scope = browser.div(id: 'parent')
|
495
|
+
@selector = {adjacent: :child, tag_name: 'div', class: 'b', id: true, index: 0}
|
496
|
+
@wd_locator = {xpath: "./child::*[local-name()='div']"\
|
497
|
+
"[contains(concat(' ', @class, ' '), ' b ')][@id][1]"}
|
498
|
+
@data_locator = 'second_sibling'
|
499
|
+
end
|
500
|
+
|
501
|
+
it 'with text' do
|
502
|
+
@query_scope = browser.div(id: 'parent')
|
503
|
+
@selector = {adjacent: :child, text: 'Second', index: 0}
|
504
|
+
@wd_locator = {xpath: "./child::*[normalize-space()='Second'][1]"}
|
505
|
+
@data_locator = 'second_sibling'
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
context 'with multiple locators' do
|
511
|
+
before(:each) do
|
512
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
513
|
+
end
|
514
|
+
|
515
|
+
it 'locates using tag name, class, attributes and text' do
|
516
|
+
@selector = {tag_name: 'div', class: 'content', contenteditable: 'true', text: 'Foo'}
|
517
|
+
@wd_locator = {xpath: ".//*[local-name()='div'][contains(concat(' ', @class, ' '), ' content ')]" \
|
518
|
+
"[normalize-space()='Foo'][@contenteditable='true']"}
|
519
|
+
@data_locator = 'content'
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
context 'with simple Regexp' do
|
524
|
+
before(:each) do
|
525
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'handles spaces' do
|
529
|
+
@selector = {title: /od Lu/}
|
530
|
+
@wd_locator = {xpath: ".//*[contains(@title, 'od Lu')]"}
|
531
|
+
@data_locator = 'Good Luck'
|
532
|
+
end
|
533
|
+
|
534
|
+
it 'handles escaped characters' do
|
535
|
+
@selector = {src: /ages\/but/}
|
536
|
+
@wd_locator = {xpath: ".//*[contains(@src, 'ages/but')]"}
|
537
|
+
@data_locator = 'submittable button'
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
context 'with complex Regexp' do
|
542
|
+
before(:each) do
|
543
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
544
|
+
end
|
545
|
+
|
546
|
+
it 'handles wildcards' do
|
547
|
+
@selector = {src: /ages.*but/}
|
548
|
+
@wd_locator = {xpath: ".//*[contains(@src, 'ages') and contains(@src, 'but')]"}
|
549
|
+
@data_locator = 'submittable button'
|
550
|
+
@remaining = {src: /ages.*but/}
|
551
|
+
end
|
552
|
+
|
553
|
+
it 'handles optional characters' do
|
554
|
+
@selector = {src: /ages ?but/}
|
555
|
+
@wd_locator = {xpath: ".//*[contains(@src, 'ages') and contains(@src, 'but')]"}
|
556
|
+
@data_locator = 'submittable button'
|
557
|
+
@remaining = {src: /ages ?but/}
|
558
|
+
end
|
559
|
+
|
560
|
+
it 'handles anchors' do
|
561
|
+
@selector = {name: /^new_user_image$/}
|
562
|
+
@wd_locator = {xpath: ".//*[contains(@name, 'new_user_image')]"}
|
563
|
+
@data_locator = 'submittable button'
|
564
|
+
@remaining = {name: /^new_user_image$/}
|
565
|
+
end
|
566
|
+
|
567
|
+
it 'handles beginning anchor' do
|
568
|
+
@selector = {src: /^i/}
|
569
|
+
@wd_locator = {xpath: ".//*[starts-with(@src, 'i')]"}
|
570
|
+
@data_locator = 'submittable button'
|
571
|
+
end
|
572
|
+
|
573
|
+
it 'does not use starts-with if visible locator used' do
|
574
|
+
@selector = {id: /^vis/, visible_text: 'shown div'}
|
575
|
+
@wd_locator = {xpath: ".//*[contains(@id, 'vis')]"}
|
576
|
+
@remaining = {id: /^vis/, visible_text: 'shown div'}
|
577
|
+
end
|
578
|
+
|
579
|
+
it 'handles case insensitive' do
|
580
|
+
@selector = {action: /me/i}
|
581
|
+
@wd_locator = {xpath: './/*[contains(translate(@action,' \
|
582
|
+
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
583
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), 'me')]"}
|
584
|
+
@data_locator = 'form'
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
# TODO: These can be moved to unit tests since no browser required
|
589
|
+
context 'returns locators that can not be directly translated' do
|
590
|
+
it 'attribute with complicated Regexp at end' do
|
591
|
+
@selector = {action: /me$/}
|
592
|
+
@wd_locator = {xpath: ".//*[contains(@action, 'me')]"}
|
593
|
+
@remaining = {action: /me$/}
|
594
|
+
end
|
595
|
+
|
596
|
+
it 'class with complicated Regexp' do
|
597
|
+
@selector = {class: /he?r/}
|
598
|
+
@wd_locator = {xpath: ".//*[contains(@class, 'h') and contains(@class, 'r')]"}
|
599
|
+
@remaining = {class: [/he?r/]}
|
600
|
+
end
|
601
|
+
|
602
|
+
it 'text with any Regexp' do
|
603
|
+
@selector = {text: /Add/}
|
604
|
+
@wd_locator = {xpath: './/*'}
|
605
|
+
@remaining = {text: /Add/}
|
606
|
+
end
|
607
|
+
|
608
|
+
it 'visible' do
|
609
|
+
@selector = {tag_name: 'div', visible: true}
|
610
|
+
@wd_locator = {xpath: ".//*[local-name()='div']"}
|
611
|
+
@remaining = {visible: true}
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'not visible' do
|
615
|
+
@selector = {tag_name: 'span', visible: false}
|
616
|
+
@wd_locator = {xpath: ".//*[local-name()='span']"}
|
617
|
+
@remaining = {visible: false}
|
618
|
+
end
|
619
|
+
|
620
|
+
it 'visible text' do
|
621
|
+
@selector = {tag_name: 'span', visible_text: 'foo'}
|
622
|
+
@wd_locator = {xpath: ".//*[local-name()='span']"}
|
623
|
+
@remaining = {visible_text: 'foo'}
|
624
|
+
end
|
625
|
+
|
626
|
+
it 'raises exception when visible is not boolean', skip_after: true do
|
627
|
+
selector = {visible: 'foo'}
|
628
|
+
msg = 'expected boolean, got "foo":String'
|
629
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
630
|
+
end
|
631
|
+
|
632
|
+
it 'raises exception when visible text is not a String or Regexp', skip_after: true do
|
633
|
+
selector = {visible_text: 7}
|
634
|
+
msg = /expected string_or_regexp, got 7:(Fixnum|Integer)/
|
635
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|