watir 7.2.0 → 7.2.1
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/.github/workflows/chrome.yml +1 -1
- data/.github/workflows/edge.yml +1 -1
- data/.github/workflows/firefox.yml +1 -1
- data/.github/workflows/ie.yml +1 -1
- data/.github/workflows/safari.yml +1 -1
- data/.github/workflows/unit.yml +1 -1
- data/.rubocop.yml +25 -12
- data/.rubocop_todo.yml +8 -1
- data/CHANGES.md +6 -1
- data/LICENSE +2 -2
- data/lib/watir/capabilities.rb +1 -1
- data/lib/watir/locators/anchor/selector_builder.rb +3 -0
- data/lib/watir/locators/element/matcher.rb +2 -1
- data/lib/watir/locators/element/selector_builder/regexp_disassembler.rb +2 -1
- data/lib/watir/locators/text_field/matcher.rb +0 -4
- data/lib/watir/version.rb +1 -1
- data/spec/locator_spec_helper.rb +0 -8
- data/spec/unit/capabilities_spec.rb +553 -561
- data/spec/unit/element_locator_spec.rb +89 -78
- data/spec/unit/match_elements/button_spec.rb +69 -62
- data/spec/unit/match_elements/element_spec.rb +303 -309
- data/spec/unit/match_elements/text_field_spec.rb +76 -73
- data/spec/unit/selector_builder/anchor_spec.rb +39 -33
- data/spec/unit/selector_builder/button_spec.rb +209 -202
- data/spec/unit/selector_builder/cell_spec.rb +48 -42
- data/spec/unit/selector_builder/element_spec.rb +634 -627
- data/spec/unit/selector_builder/row_spec.rb +119 -113
- data/spec/unit/selector_builder/text_field_spec.rb +195 -188
- data/spec/unit/selector_builder/textarea_spec.rb +22 -14
- data/spec/unit/wait_spec.rb +85 -81
- data/spec/watirspec/adjacent_spec.rb +249 -247
- data/spec/watirspec/after_hooks_spec.rb +161 -159
- data/spec/watirspec/alert_spec.rb +61 -58
- data/spec/watirspec/browser_spec.rb +409 -409
- data/spec/watirspec/capabilities_spec.rb +89 -99
- data/spec/watirspec/cookies_spec.rb +120 -118
- data/spec/watirspec/drag_and_drop_spec.rb +26 -24
- data/spec/watirspec/element_hidden_spec.rb +66 -60
- data/spec/watirspec/elements/area_spec.rb +49 -47
- data/spec/watirspec/elements/areas_spec.rb +27 -25
- data/spec/watirspec/elements/button_spec.rb +253 -251
- data/spec/watirspec/elements/buttons_spec.rb +36 -34
- data/spec/watirspec/elements/checkbox_spec.rb +48 -36
- data/spec/watirspec/elements/checkboxes_spec.rb +29 -27
- data/spec/watirspec/elements/collections_spec.rb +126 -119
- data/spec/watirspec/elements/date_field_spec.rb +185 -183
- data/spec/watirspec/elements/date_fields_spec.rb +29 -27
- data/spec/watirspec/elements/date_time_field_spec.rb +197 -195
- data/spec/watirspec/elements/date_time_fields_spec.rb +30 -28
- data/spec/watirspec/elements/dd_spec.rb +87 -85
- data/spec/watirspec/elements/dds_spec.rb +27 -25
- data/spec/watirspec/elements/del_spec.rb +106 -104
- data/spec/watirspec/elements/dels_spec.rb +26 -24
- data/spec/watirspec/elements/div_spec.rb +225 -221
- data/spec/watirspec/elements/divs_spec.rb +37 -35
- data/spec/watirspec/elements/dl_spec.rb +110 -108
- data/spec/watirspec/elements/dls_spec.rb +28 -26
- data/spec/watirspec/elements/dt_spec.rb +86 -84
- data/spec/watirspec/elements/dts_spec.rb +27 -25
- data/spec/watirspec/elements/element_spec.rb +794 -775
- data/spec/watirspec/elements/elements_spec.rb +40 -38
- data/spec/watirspec/elements/em_spec.rb +62 -60
- data/spec/watirspec/elements/ems_spec.rb +28 -26
- data/spec/watirspec/elements/filefield_spec.rb +116 -114
- data/spec/watirspec/elements/filefields_spec.rb +28 -26
- data/spec/watirspec/elements/font_spec.rb +20 -18
- data/spec/watirspec/elements/form_spec.rb +51 -49
- data/spec/watirspec/elements/forms_spec.rb +29 -27
- data/spec/watirspec/elements/frame_spec.rb +89 -87
- data/spec/watirspec/elements/frames_spec.rb +27 -25
- data/spec/watirspec/elements/hidden_spec.rb +81 -79
- data/spec/watirspec/elements/hiddens_spec.rb +28 -26
- data/spec/watirspec/elements/hn_spec.rb +59 -57
- data/spec/watirspec/elements/hns_spec.rb +26 -24
- data/spec/watirspec/elements/iframe_spec.rb +195 -191
- data/spec/watirspec/elements/iframes_spec.rb +31 -29
- data/spec/watirspec/elements/image_spec.rb +148 -146
- data/spec/watirspec/elements/images_spec.rb +26 -24
- data/spec/watirspec/elements/input_spec.rb +9 -7
- data/spec/watirspec/elements/ins_spec.rb +106 -104
- data/spec/watirspec/elements/inses_spec.rb +26 -24
- data/spec/watirspec/elements/label_spec.rb +56 -54
- data/spec/watirspec/elements/labels_spec.rb +26 -24
- data/spec/watirspec/elements/li_spec.rb +76 -74
- data/spec/watirspec/elements/link_spec.rb +173 -177
- data/spec/watirspec/elements/links_spec.rb +45 -43
- data/spec/watirspec/elements/lis_spec.rb +27 -25
- data/spec/watirspec/elements/list_spec.rb +44 -42
- data/spec/watirspec/elements/map_spec.rb +57 -55
- data/spec/watirspec/elements/maps_spec.rb +27 -25
- data/spec/watirspec/elements/meta_spec.rb +16 -14
- data/spec/watirspec/elements/metas_spec.rb +26 -24
- data/spec/watirspec/elements/ol_spec.rb +56 -65
- data/spec/watirspec/elements/ols_spec.rb +26 -24
- data/spec/watirspec/elements/option_spec.rb +91 -89
- data/spec/watirspec/elements/p_spec.rb +81 -79
- data/spec/watirspec/elements/pre_spec.rb +77 -75
- data/spec/watirspec/elements/pres_spec.rb +26 -24
- data/spec/watirspec/elements/ps_spec.rb +26 -24
- data/spec/watirspec/elements/radio_spec.rb +251 -249
- data/spec/watirspec/elements/radios_spec.rb +28 -26
- data/spec/watirspec/elements/select_list_spec.rb +537 -526
- data/spec/watirspec/elements/select_lists_spec.rb +32 -30
- data/spec/watirspec/elements/span_spec.rb +112 -110
- data/spec/watirspec/elements/spans_spec.rb +26 -24
- data/spec/watirspec/elements/strong_spec.rb +58 -58
- data/spec/watirspec/elements/strongs_spec.rb +28 -26
- data/spec/watirspec/elements/table_spec.rb +192 -139
- data/spec/watirspec/elements/tables_spec.rb +28 -26
- data/spec/watirspec/elements/tbody_spec.rb +74 -72
- data/spec/watirspec/elements/tbodys_spec.rb +40 -38
- data/spec/watirspec/elements/td_spec.rb +66 -64
- data/spec/watirspec/elements/tds_spec.rb +41 -39
- data/spec/watirspec/elements/text_field_spec.rb +206 -204
- data/spec/watirspec/elements/text_fields_spec.rb +30 -28
- data/spec/watirspec/elements/textarea_spec.rb +20 -18
- data/spec/watirspec/elements/textareas_spec.rb +18 -16
- data/spec/watirspec/elements/tfoot_spec.rb +65 -63
- data/spec/watirspec/elements/tfoots_spec.rb +42 -40
- data/spec/watirspec/elements/thead_spec.rb +65 -63
- data/spec/watirspec/elements/theads_spec.rb +42 -40
- data/spec/watirspec/elements/tr_spec.rb +48 -46
- data/spec/watirspec/elements/trs_spec.rb +42 -40
- data/spec/watirspec/elements/ul_spec.rb +56 -54
- data/spec/watirspec/elements/uls_spec.rb +26 -24
- data/spec/watirspec/radio_set_spec.rb +269 -267
- data/spec/watirspec/screenshot_spec.rb +20 -18
- data/spec/watirspec/scroll_spec.rb +152 -148
- data/spec/watirspec/shadow_root_spec.rb +86 -83
- data/spec/watirspec/user_editable_spec.rb +200 -198
- data/spec/watirspec/wait_spec.rb +289 -295
- data/spec/watirspec/window_switching_spec.rb +422 -434
- data/spec/watirspec_helper.rb +1 -23
- data/watir.gemspec +5 -4
- metadata +44 -33
- data/spec/watirspec/attributes_spec.rb +0 -18
- data/spec/watirspec/elements/table_nesting_spec.rb +0 -51
- data/spec/watirspec/special_chars_spec.rb +0 -23
|
@@ -2,797 +2,804 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative '../unit_helper'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
module Watir
|
|
6
|
+
module Locators
|
|
7
|
+
class Element
|
|
8
|
+
describe SelectorBuilder do
|
|
9
|
+
include LocatorSpecHelper
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
let(:selector_builder) { described_class.new(attributes, query_scope) }
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
describe '#build' do
|
|
14
|
+
it 'without any arguments' do
|
|
15
|
+
selector = {}
|
|
16
|
+
built = {xpath: './/*'}
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
context 'with xpath or css' do
|
|
19
|
-
it 'locates with xpath only' do
|
|
20
|
-
selector = {xpath: './/div'}
|
|
21
|
-
built = selector.dup
|
|
22
|
-
|
|
23
|
-
expect(selector_builder.build(selector)).to eq built
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it 'locates with css only' do
|
|
27
|
-
selector = {css: 'div'}
|
|
28
|
-
built = selector.dup
|
|
29
|
-
|
|
30
|
-
expect(selector_builder.build(selector)).to eq built
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it 'locates when attributes combined with xpath' do
|
|
34
|
-
selector = {xpath: './/div', random: 'foo'}
|
|
35
|
-
built = selector.dup
|
|
36
|
-
|
|
37
|
-
expect(selector_builder.build(selector)).to eq built
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it 'locates when attributes combined with css' do
|
|
41
|
-
selector = {css: 'div', random: 'foo'}
|
|
42
|
-
built = selector.dup
|
|
43
|
-
|
|
44
|
-
expect(selector_builder.build(selector)).to eq built
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it 'raises exception when using xpath & css' do
|
|
48
|
-
selector = {xpath: './/*', css: 'div'}
|
|
49
|
-
msg = 'Can not locate element with [:css, :xpath]'
|
|
50
|
-
|
|
51
|
-
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it 'raises exception when not a String' do
|
|
55
|
-
selector = {xpath: 7}
|
|
56
|
-
msg = /expected one of \[String\], got 7:Integer/
|
|
57
|
-
|
|
58
|
-
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
context 'with tag_name' do
|
|
63
|
-
it 'with String equals' do
|
|
64
|
-
selector = {tag_name: 'div'}
|
|
65
|
-
built = {xpath: ".//*[local-name()='div']"}
|
|
66
|
-
|
|
67
|
-
expect(selector_builder.build(selector)).to eq built
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it 'with simple Regexp contains' do
|
|
71
|
-
selector = {tag_name: /div/}
|
|
72
|
-
built = {xpath: './/*[contains(local-name(), "div")]'}
|
|
73
|
-
|
|
74
|
-
expect(selector_builder.build(selector)).to eq built
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
it 'with Symbol' do
|
|
78
|
-
selector = {tag_name: :div}
|
|
79
|
-
built = {xpath: ".//*[local-name()='div']"}
|
|
80
|
-
|
|
81
|
-
expect(selector_builder.build(selector)).to eq built
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'raises exception when not a String or Regexp' do
|
|
85
|
-
selector = {tag_name: 7}
|
|
86
|
-
msg = /expected one of \[String, Regexp, Symbol\], got 7:Integer/
|
|
87
|
-
|
|
88
|
-
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
context 'with class names' do
|
|
93
|
-
it 'class_name is converted to class' do
|
|
94
|
-
selector = {class_name: 'user'}
|
|
95
|
-
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
|
|
96
|
-
|
|
97
|
-
expect(selector_builder.build(selector)).to eq built
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it 'single String concatenates' do
|
|
101
|
-
selector = {class: 'user'}
|
|
102
|
-
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
|
|
103
|
-
|
|
104
|
-
expect(selector_builder.build(selector)).to eq built
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it 'Array of String concatenates with and' do
|
|
108
|
-
selector = {class: %w[multiple here]}
|
|
109
|
-
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' multiple ') and " \
|
|
110
|
-
"contains(concat(' ', normalize-space(@class), ' '), ' here ')]"}
|
|
111
|
-
|
|
112
|
-
expect(selector_builder.build(selector)).to eq built
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
it 'merges values when class and class_name are both used' do
|
|
116
|
-
selector = {class: 'foo', class_name: 'bar'}
|
|
117
|
-
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' foo ') and " \
|
|
118
|
-
"contains(concat(' ', normalize-space(@class), ' '), ' bar ')]"}
|
|
18
|
+
expect(selector_builder.build(selector)).to eq built
|
|
19
|
+
end
|
|
119
20
|
|
|
120
|
-
|
|
121
|
-
|
|
21
|
+
context 'with xpath or css' do
|
|
22
|
+
it 'locates with xpath only' do
|
|
23
|
+
selector = {xpath: './/div'}
|
|
24
|
+
built = selector.dup
|
|
122
25
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
built = {xpath: './/*[contains(@class, "use")]'}
|
|
26
|
+
expect(selector_builder.build(selector)).to eq built
|
|
27
|
+
end
|
|
126
28
|
|
|
127
|
-
|
|
128
|
-
|
|
29
|
+
it 'locates with css only' do
|
|
30
|
+
selector = {css: 'div'}
|
|
31
|
+
built = selector.dup
|
|
129
32
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
built = {xpath: './/*[contains(@class, "mult") and contains(@class, "her")]'}
|
|
33
|
+
expect(selector_builder.build(selector)).to eq built
|
|
34
|
+
end
|
|
133
35
|
|
|
134
|
-
|
|
135
|
-
|
|
36
|
+
it 'locates when attributes combined with xpath' do
|
|
37
|
+
selector = {xpath: './/div', random: 'foo'}
|
|
38
|
+
built = selector.dup
|
|
136
39
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
built = {xpath: ".//*[not(contains(concat(' ', normalize-space(@class), ' '), ' multiple '))]"}
|
|
140
|
-
|
|
141
|
-
expect(selector_builder.build(selector)).to eq built
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
it 'single Boolean true provides the at' do
|
|
145
|
-
selector = {class: true}
|
|
146
|
-
built = {xpath: './/*[@class]'}
|
|
147
|
-
|
|
148
|
-
expect(selector_builder.build(selector)).to eq built
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
it 'single Boolean false provides the not atat' do
|
|
152
|
-
selector = {class: false}
|
|
153
|
-
built = {xpath: './/*[not(@class)]'}
|
|
154
|
-
|
|
155
|
-
expect(selector_builder.build(selector)).to eq built
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
it 'Array of mixed String, Regexp and Boolean contains and concatenates with and and not' do
|
|
159
|
-
selector = {class: [/mult/, 'classes', '!here']}
|
|
160
|
-
built = {xpath: './/*[contains(@class, "mult") ' \
|
|
161
|
-
"and contains(concat(' ', normalize-space(@class), ' '), ' classes ') " \
|
|
162
|
-
"and not(contains(concat(' ', normalize-space(@class), ' '), ' here '))]"}
|
|
163
|
-
|
|
164
|
-
expect(selector_builder.build(selector)).to eq built
|
|
165
|
-
end
|
|
40
|
+
expect(selector_builder.build(selector)).to eq built
|
|
41
|
+
end
|
|
166
42
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
43
|
+
it 'locates when attributes combined with css' do
|
|
44
|
+
selector = {css: 'div', random: 'foo'}
|
|
45
|
+
built = selector.dup
|
|
170
46
|
|
|
171
|
-
|
|
172
|
-
|
|
47
|
+
expect(selector_builder.build(selector)).to eq built
|
|
48
|
+
end
|
|
173
49
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
50
|
+
it 'raises exception when using xpath & css' do
|
|
51
|
+
selector = {xpath: './/*', css: 'div'}
|
|
52
|
+
msg = 'Can not locate element with [:css, :xpath]'
|
|
177
53
|
|
|
178
|
-
|
|
179
|
-
|
|
54
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
|
55
|
+
end
|
|
180
56
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
57
|
+
it 'raises exception when not a String' do
|
|
58
|
+
selector = {xpath: 7}
|
|
59
|
+
msg = /expected one of \[String\], got 7:Integer/
|
|
184
60
|
|
|
185
|
-
|
|
186
|
-
|
|
61
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
62
|
+
end
|
|
63
|
+
end
|
|
187
64
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
65
|
+
context 'with tag_name' do
|
|
66
|
+
it 'with String equals' do
|
|
67
|
+
selector = {tag_name: 'div'}
|
|
68
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
191
69
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
end
|
|
70
|
+
expect(selector_builder.build(selector)).to eq built
|
|
71
|
+
end
|
|
195
72
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
built = {xpath: ".//*[normalize-space(@href)='watirspec.css']"}
|
|
73
|
+
it 'with simple Regexp contains' do
|
|
74
|
+
selector = {tag_name: /div/}
|
|
75
|
+
built = {xpath: './/*[contains(local-name(), "div")]'}
|
|
200
76
|
|
|
201
|
-
|
|
202
|
-
|
|
77
|
+
expect(selector_builder.build(selector)).to eq built
|
|
78
|
+
end
|
|
203
79
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
80
|
+
it 'with Symbol' do
|
|
81
|
+
selector = {tag_name: :div}
|
|
82
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
207
83
|
|
|
208
|
-
|
|
209
|
-
|
|
84
|
+
expect(selector_builder.build(selector)).to eq built
|
|
85
|
+
end
|
|
210
86
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
87
|
+
it 'raises exception when not a String or Regexp' do
|
|
88
|
+
selector = {tag_name: 7}
|
|
89
|
+
msg = /expected one of \[String, Regexp, Symbol\], got 7:Integer/
|
|
214
90
|
|
|
215
|
-
|
|
216
|
-
|
|
91
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
92
|
+
end
|
|
93
|
+
end
|
|
217
94
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
95
|
+
context 'with class names' do
|
|
96
|
+
it 'class_name is converted to class' do
|
|
97
|
+
selector = {class_name: 'user'}
|
|
98
|
+
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
|
|
221
99
|
|
|
222
|
-
|
|
223
|
-
|
|
100
|
+
expect(selector_builder.build(selector)).to eq built
|
|
101
|
+
end
|
|
224
102
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
103
|
+
it 'single String concatenates' do
|
|
104
|
+
selector = {class: 'user'}
|
|
105
|
+
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' user ')]"}
|
|
228
106
|
|
|
229
|
-
|
|
230
|
-
|
|
107
|
+
expect(selector_builder.build(selector)).to eq built
|
|
108
|
+
end
|
|
231
109
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
110
|
+
it 'Array of String concatenates with and' do
|
|
111
|
+
selector = {class: %w[multiple here]}
|
|
112
|
+
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' multiple ') and " \
|
|
113
|
+
"contains(concat(' ', normalize-space(@class), ' '), ' here ')]"}
|
|
235
114
|
|
|
236
|
-
|
|
237
|
-
|
|
115
|
+
expect(selector_builder.build(selector)).to eq built
|
|
116
|
+
end
|
|
238
117
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
118
|
+
it 'merges values when class and class_name are both used' do
|
|
119
|
+
selector = {class: 'foo', class_name: 'bar'}
|
|
120
|
+
built = {xpath: ".//*[contains(concat(' ', normalize-space(@class), ' '), ' foo ') and " \
|
|
121
|
+
"contains(concat(' ', normalize-space(@class), ' '), ' bar ')]"}
|
|
242
122
|
|
|
243
|
-
|
|
244
|
-
|
|
123
|
+
expect(selector_builder.build(selector)).to eq built
|
|
124
|
+
end
|
|
245
125
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
126
|
+
it 'simple Regexp contains' do
|
|
127
|
+
selector = {class_name: /use/}
|
|
128
|
+
built = {xpath: './/*[contains(@class, "use")]'}
|
|
249
129
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
end
|
|
130
|
+
expect(selector_builder.build(selector)).to eq built
|
|
131
|
+
end
|
|
253
132
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
built = {xpath: './/*[contains(@name, "user")]'}
|
|
133
|
+
it 'Array of Regexp contains with and' do
|
|
134
|
+
selector = {class: [/mult/, /her/]}
|
|
135
|
+
built = {xpath: './/*[contains(@class, "mult") and contains(@class, "her")]'}
|
|
258
136
|
|
|
259
|
-
|
|
260
|
-
|
|
137
|
+
expect(selector_builder.build(selector)).to eq built
|
|
138
|
+
end
|
|
261
139
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
140
|
+
it 'single negated String concatenates with not' do
|
|
141
|
+
selector = {class: '!multiple'}
|
|
142
|
+
built = {xpath: ".//*[not(contains(concat(' ', normalize-space(@class), ' '), ' multiple '))]"}
|
|
265
143
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
end
|
|
144
|
+
expect(selector_builder.build(selector)).to eq built
|
|
145
|
+
end
|
|
269
146
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
built = {xpath: ".//*[normalize-space()='Add user']"}
|
|
147
|
+
it 'single Boolean true provides the at' do
|
|
148
|
+
selector = {class: true}
|
|
149
|
+
built = {xpath: './/*[@class]'}
|
|
274
150
|
|
|
275
|
-
|
|
276
|
-
|
|
151
|
+
expect(selector_builder.build(selector)).to eq built
|
|
152
|
+
end
|
|
277
153
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
154
|
+
it 'single Boolean false provides the not atat' do
|
|
155
|
+
selector = {class: false}
|
|
156
|
+
built = {xpath: './/*[not(@class)]'}
|
|
281
157
|
|
|
282
|
-
|
|
283
|
-
|
|
158
|
+
expect(selector_builder.build(selector)).to eq built
|
|
159
|
+
end
|
|
284
160
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
161
|
+
it 'Array of mixed String, Regexp and Boolean contains and concatenates with and and not' do
|
|
162
|
+
selector = {class: [/mult/, 'classes', '!here']}
|
|
163
|
+
built = {xpath: './/*[contains(@class, "mult") ' \
|
|
164
|
+
"and contains(concat(' ', normalize-space(@class), ' '), ' classes ') " \
|
|
165
|
+
"and not(contains(concat(' ', normalize-space(@class), ' '), ' here '))]"}
|
|
288
166
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
end
|
|
167
|
+
expect(selector_builder.build(selector)).to eq built
|
|
168
|
+
end
|
|
292
169
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
built = {xpath: "(.//*[local-name()='div'])[8]"}
|
|
170
|
+
it 'empty string finds elements without class' do
|
|
171
|
+
selector = {class_name: ''}
|
|
172
|
+
built = {xpath: './/*[not(@class)]'}
|
|
297
173
|
|
|
298
|
-
|
|
299
|
-
|
|
174
|
+
expect(selector_builder.build(selector)).to eq built
|
|
175
|
+
end
|
|
300
176
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
177
|
+
it 'empty Array finds elements without class' do
|
|
178
|
+
selector = {class_name: []}
|
|
179
|
+
built = {xpath: './/*[not(@class)]'}
|
|
304
180
|
|
|
305
|
-
|
|
306
|
-
|
|
181
|
+
expect(selector_builder.build(selector)).to eq built
|
|
182
|
+
end
|
|
307
183
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
184
|
+
it 'raises exception when not a String or Regexp or Array' do
|
|
185
|
+
selector = {class: 7}
|
|
186
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 7:Integer/
|
|
311
187
|
|
|
312
|
-
|
|
313
|
-
|
|
188
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
189
|
+
end
|
|
314
190
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
191
|
+
it 'raises exception when Array values are not a String or Regexp' do
|
|
192
|
+
selector = {class: [7]}
|
|
193
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 7:Integer/
|
|
318
194
|
|
|
319
|
-
|
|
320
|
-
|
|
195
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
196
|
+
end
|
|
197
|
+
end
|
|
321
198
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
199
|
+
context 'with attributes as predicates' do
|
|
200
|
+
it 'with href attribute' do
|
|
201
|
+
selector = {href: 'watirspec.css'}
|
|
202
|
+
built = {xpath: ".//*[normalize-space(@href)='watirspec.css']"}
|
|
325
203
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
end
|
|
204
|
+
expect(selector_builder.build(selector)).to eq built
|
|
205
|
+
end
|
|
329
206
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
built = {xpath: ".//*[@id=//label[normalize-space()='Cars']/@for " \
|
|
334
|
-
"or parent::label[normalize-space()='Cars']]"}
|
|
207
|
+
it 'with String attribute key' do
|
|
208
|
+
selector = {'id' => 'user_new'}
|
|
209
|
+
built = {xpath: ".//*[@id='user_new']"}
|
|
335
210
|
|
|
336
|
-
|
|
337
|
-
|
|
211
|
+
expect(selector_builder.build(selector)).to eq built
|
|
212
|
+
end
|
|
338
213
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
built = {xpath: xpath, label_element: /Ca|rs/}
|
|
214
|
+
it 'with String equals' do
|
|
215
|
+
selector = {id: 'user_new'}
|
|
216
|
+
built = {xpath: ".//*[@id='user_new']"}
|
|
343
217
|
|
|
344
|
-
|
|
345
|
-
|
|
218
|
+
expect(selector_builder.build(selector)).to eq built
|
|
219
|
+
end
|
|
346
220
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
221
|
+
it 'with TrueClass no equals' do
|
|
222
|
+
selector = {tag_name: 'input', id: true}
|
|
223
|
+
built = {xpath: ".//*[local-name()='input'][@id]"}
|
|
350
224
|
|
|
351
|
-
|
|
352
|
-
|
|
225
|
+
expect(selector_builder.build(selector)).to eq built
|
|
226
|
+
end
|
|
353
227
|
|
|
354
|
-
|
|
355
|
-
|
|
228
|
+
it 'with FalseClass not with no equals' do
|
|
229
|
+
selector = {tag_name: 'input', name: false}
|
|
230
|
+
built = {xpath: ".//*[local-name()='input'][not(@name)]"}
|
|
356
231
|
|
|
357
|
-
|
|
358
|
-
|
|
232
|
+
expect(selector_builder.build(selector)).to eq built
|
|
233
|
+
end
|
|
359
234
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
235
|
+
it 'with multiple attributes: no equals and not with no equals and equals' do
|
|
236
|
+
selector = {readonly: true, foo: false, id: 'good_luck'}
|
|
237
|
+
built = {xpath: ".//*[@readonly and not(@foo) and @id='good_luck']"}
|
|
363
238
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
selector = {adjacent: 'foo', index: 0}
|
|
367
|
-
msg = 'expected one of [Symbol], got "foo":String'
|
|
239
|
+
expect(selector_builder.build(selector)).to eq built
|
|
240
|
+
end
|
|
368
241
|
|
|
369
|
-
|
|
370
|
-
|
|
242
|
+
it 'raises exception when attribute value is not a Boolean, String or Regexp' do
|
|
243
|
+
selector = {foo: 7}
|
|
244
|
+
msg = /expected one of \[String, Regexp, TrueClass, FalseClass\], got 7:Integer/
|
|
371
245
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
msg = 'Unable to process adjacent locator with foo'
|
|
246
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
247
|
+
end
|
|
375
248
|
|
|
376
|
-
|
|
377
|
-
|
|
249
|
+
it 'raises exception when attribute key is not a String or Regexp' do
|
|
250
|
+
selector = {7 => 'foo'}
|
|
251
|
+
msg = /Unable to build XPath using 7:Integer/
|
|
378
252
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
built = {xpath: './ancestor::*[1]'}
|
|
253
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
|
254
|
+
end
|
|
255
|
+
end
|
|
383
256
|
|
|
384
|
-
|
|
385
|
-
|
|
257
|
+
context 'with attributes as partials' do
|
|
258
|
+
it 'with Regexp' do
|
|
259
|
+
selector = {name: /user/}
|
|
260
|
+
built = {xpath: './/*[contains(@name, "user")]'}
|
|
386
261
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
built = {xpath: './ancestor::*[3]'}
|
|
262
|
+
expect(selector_builder.build(selector)).to eq built
|
|
263
|
+
end
|
|
390
264
|
|
|
391
|
-
|
|
392
|
-
|
|
265
|
+
it 'with multiple Regexp attributes separated by and' do
|
|
266
|
+
selector = {readonly: /read/, id: /good/}
|
|
267
|
+
built = {xpath: './/*[contains(@readonly, "read") and contains(@id, "good")]'}
|
|
393
268
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
"[contains(concat(' ', normalize-space(@class), ' '), ' ancestor ')][@id][2]"}
|
|
269
|
+
expect(selector_builder.build(selector)).to eq built
|
|
270
|
+
end
|
|
271
|
+
end
|
|
398
272
|
|
|
399
|
-
|
|
400
|
-
|
|
273
|
+
context 'with text' do
|
|
274
|
+
it 'String uses normalize space equals' do
|
|
275
|
+
selector = {text: 'Add user'}
|
|
276
|
+
built = {xpath: ".//*[normalize-space()='Add user']"}
|
|
401
277
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
msg = 'Can not find parent element with text locator'
|
|
405
|
-
expect { selector_builder.build(selector) }
|
|
406
|
-
.to raise_exception Watir::Exception::LocatorException, msg
|
|
407
|
-
end
|
|
408
|
-
end
|
|
278
|
+
expect(selector_builder.build(selector)).to eq built
|
|
279
|
+
end
|
|
409
280
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
built = {xpath: './following-sibling::*[1]'}
|
|
281
|
+
it 'Regexp uses contains normalize space' do
|
|
282
|
+
selector = {text: /Add/}
|
|
283
|
+
built = {xpath: './/*[contains(normalize-space(), "Add")]'}
|
|
414
284
|
|
|
415
|
-
|
|
416
|
-
|
|
285
|
+
expect(selector_builder.build(selector)).to eq built
|
|
286
|
+
end
|
|
417
287
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
288
|
+
it 'raises exception when text is not a String or Regexp' do
|
|
289
|
+
selector = {text: 7}
|
|
290
|
+
msg = /expected one of \[String, Regexp\], got 7:Integer/
|
|
421
291
|
|
|
422
|
-
|
|
423
|
-
|
|
292
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
293
|
+
end
|
|
294
|
+
end
|
|
424
295
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
296
|
+
context 'with index' do
|
|
297
|
+
it 'positive' do
|
|
298
|
+
selector = {tag_name: 'div', index: 7}
|
|
299
|
+
built = {xpath: "(.//*[local-name()='div'])[8]"}
|
|
429
300
|
|
|
430
|
-
|
|
431
|
-
|
|
301
|
+
expect(selector_builder.build(selector)).to eq built
|
|
302
|
+
end
|
|
432
303
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
304
|
+
it 'negative' do
|
|
305
|
+
selector = {tag_name: 'div', index: -7}
|
|
306
|
+
built = {xpath: "(.//*[local-name()='div'])[last()-6]"}
|
|
436
307
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
end
|
|
308
|
+
expect(selector_builder.build(selector)).to eq built
|
|
309
|
+
end
|
|
440
310
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
built = {xpath: './preceding-sibling::*[1]'}
|
|
311
|
+
it 'last' do
|
|
312
|
+
selector = {tag_name: 'div', index: -1}
|
|
313
|
+
built = {xpath: "(.//*[local-name()='div'])[last()]"}
|
|
445
314
|
|
|
446
|
-
|
|
447
|
-
|
|
315
|
+
expect(selector_builder.build(selector)).to eq built
|
|
316
|
+
end
|
|
448
317
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
318
|
+
it 'does not return index if it is zero' do
|
|
319
|
+
selector = {tag_name: 'div', index: 0}
|
|
320
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
452
321
|
|
|
453
|
-
|
|
454
|
-
|
|
322
|
+
expect(selector_builder.build(selector)).to eq built
|
|
323
|
+
end
|
|
455
324
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
"[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
|
|
325
|
+
it 'raises exception when index is not an Integer' do
|
|
326
|
+
selector = {index: 'foo'}
|
|
327
|
+
msg = /expected one of \[(Integer|Fixnum)\], got "foo":String/
|
|
460
328
|
|
|
461
|
-
|
|
462
|
-
|
|
329
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
330
|
+
end
|
|
331
|
+
end
|
|
463
332
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
333
|
+
context 'with labels' do
|
|
334
|
+
it 'locates the element associated with the label element located by the text of the provided label key' do
|
|
335
|
+
selector = {label: 'Cars'}
|
|
336
|
+
built = {xpath: ".//*[@id=//label[normalize-space()='Cars']/@for " \
|
|
337
|
+
"or parent::label[normalize-space()='Cars']]"}
|
|
467
338
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
end
|
|
339
|
+
expect(selector_builder.build(selector)).to eq built
|
|
340
|
+
end
|
|
471
341
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
342
|
+
it 'returns a label_element if complex' do
|
|
343
|
+
selector = {label: /Ca|rs/}
|
|
344
|
+
xpath = './/*[@id=//label[normalize-space()]/@for or parent::label[normalize-space()]]'
|
|
345
|
+
built = {xpath: xpath, label_element: /Ca|rs/}
|
|
476
346
|
|
|
477
|
-
|
|
478
|
-
|
|
347
|
+
expect(selector_builder.build(selector)).to eq built
|
|
348
|
+
end
|
|
479
349
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
350
|
+
it 'returns a visible_label_element if complex' do
|
|
351
|
+
selector = {visible_label: /Ca|rs/}
|
|
352
|
+
built = {xpath: './/*', visible_label_element: /Ca|rs/}
|
|
483
353
|
|
|
484
|
-
|
|
485
|
-
|
|
354
|
+
expect(selector_builder.build(selector)).to eq built
|
|
355
|
+
end
|
|
486
356
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
built = {xpath: "./child::*[local-name()='div']" \
|
|
490
|
-
"[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
|
|
357
|
+
it 'does not use the label element when label is a valid attribute' do
|
|
358
|
+
@attributes ||= Watir::Option.attribute_list
|
|
491
359
|
|
|
492
|
-
|
|
493
|
-
|
|
360
|
+
selector = {tag_name: 'option', label: 'Germany'}
|
|
361
|
+
built = {xpath: ".//*[local-name()='option'][@label='Germany']"}
|
|
494
362
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
363
|
+
expect(selector_builder.build(selector)).to eq built
|
|
364
|
+
end
|
|
365
|
+
end
|
|
498
366
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
367
|
+
context 'with adjacent locators' do
|
|
368
|
+
it 'raises exception when not a Symbol' do
|
|
369
|
+
selector = {adjacent: 'foo', index: 0}
|
|
370
|
+
msg = 'expected one of [Symbol], got "foo":String'
|
|
503
371
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
selector = {tag_name: 'div', class: 'content', contenteditable: 'true', text: 'Foo'}
|
|
507
|
-
built = {xpath: ".//*[local-name()='div'][contains(concat(' ', normalize-space(@class), ' '), ' content ')]" \
|
|
508
|
-
"[normalize-space()='Foo'][@contenteditable='true']"}
|
|
372
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
373
|
+
end
|
|
509
374
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
375
|
+
it 'raises exception when not a valid value' do
|
|
376
|
+
selector = {adjacent: :foo, index: 0}
|
|
377
|
+
msg = 'Unable to process adjacent locator with foo'
|
|
513
378
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
selector = {title: /od Lu/}
|
|
517
|
-
built = {xpath: './/*[contains(@title, "od Lu")]'}
|
|
379
|
+
expect { selector_builder.build(selector) }.to raise_exception Watir::Exception::LocatorException, msg
|
|
380
|
+
end
|
|
518
381
|
|
|
519
|
-
|
|
520
|
-
|
|
382
|
+
describe '#parent' do
|
|
383
|
+
it 'with no other arguments' do
|
|
384
|
+
selector = {adjacent: :ancestor, index: 0}
|
|
385
|
+
built = {xpath: './ancestor::*[1]'}
|
|
521
386
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
built = {xpath: './/*[contains(@src, "ages/but")]'}
|
|
387
|
+
expect(selector_builder.build(selector)).to eq built
|
|
388
|
+
end
|
|
525
389
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
390
|
+
it 'with index' do
|
|
391
|
+
selector = {adjacent: :ancestor, index: 2}
|
|
392
|
+
built = {xpath: './ancestor::*[3]'}
|
|
529
393
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
selector = {src: /ages.*but/}
|
|
533
|
-
built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages.*but/}
|
|
394
|
+
expect(selector_builder.build(selector)).to eq built
|
|
395
|
+
end
|
|
534
396
|
|
|
535
|
-
|
|
536
|
-
|
|
397
|
+
it 'with multiple locators' do
|
|
398
|
+
selector = {adjacent: :ancestor, id: true, tag_name: 'div', class: 'ancestor', index: 1}
|
|
399
|
+
built = {xpath: "./ancestor::*[local-name()='div']" \
|
|
400
|
+
"[contains(concat(' ', normalize-space(@class), ' '), ' ancestor ')][@id][2]"}
|
|
537
401
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages ?but/}
|
|
402
|
+
expect(selector_builder.build(selector)).to eq built
|
|
403
|
+
end
|
|
541
404
|
|
|
542
|
-
|
|
543
|
-
|
|
405
|
+
it 'raises an exception if text locator is used' do
|
|
406
|
+
selector = {adjacent: :ancestor, index: 0, text: 'Foo'}
|
|
407
|
+
msg = 'Can not find parent element with text locator'
|
|
408
|
+
expect { selector_builder.build(selector) }
|
|
409
|
+
.to raise_exception Watir::Exception::LocatorException, msg
|
|
410
|
+
end
|
|
411
|
+
end
|
|
544
412
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
413
|
+
describe '#following_sibling' do
|
|
414
|
+
it 'with no other arguments' do
|
|
415
|
+
selector = {adjacent: :following, index: 0}
|
|
416
|
+
built = {xpath: './following-sibling::*[1]'}
|
|
548
417
|
|
|
549
|
-
|
|
550
|
-
|
|
418
|
+
expect(selector_builder.build(selector)).to eq built
|
|
419
|
+
end
|
|
551
420
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
421
|
+
it 'with index' do
|
|
422
|
+
selector = {adjacent: :following, index: 2}
|
|
423
|
+
built = {xpath: './following-sibling::*[3]'}
|
|
555
424
|
|
|
556
|
-
|
|
557
|
-
|
|
425
|
+
expect(selector_builder.build(selector)).to eq built
|
|
426
|
+
end
|
|
558
427
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
428
|
+
it 'with multiple locators' do
|
|
429
|
+
selector = {adjacent: :following, tag_name: 'div', class: 'b', index: 0, id: true}
|
|
430
|
+
built = {xpath: "./following-sibling::*[local-name()='div']" \
|
|
431
|
+
"[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
|
|
562
432
|
|
|
563
|
-
|
|
564
|
-
|
|
433
|
+
expect(selector_builder.build(selector)).to eq built
|
|
434
|
+
end
|
|
565
435
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
|
570
|
-
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), " \
|
|
571
|
-
'translate("me",' \
|
|
572
|
-
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
|
573
|
-
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'))]"}
|
|
436
|
+
it 'with text' do
|
|
437
|
+
selector = {adjacent: :following, text: 'Third', index: 0}
|
|
438
|
+
built = {xpath: "./following-sibling::*[normalize-space()='Third'][1]"}
|
|
574
439
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
440
|
+
expect(selector_builder.build(selector)).to eq built
|
|
441
|
+
end
|
|
442
|
+
end
|
|
578
443
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
444
|
+
describe '#previous_sibling' do
|
|
445
|
+
it 'with no other arguments' do
|
|
446
|
+
selector = {adjacent: :preceding, index: 0}
|
|
447
|
+
built = {xpath: './preceding-sibling::*[1]'}
|
|
583
448
|
|
|
584
|
-
|
|
585
|
-
|
|
449
|
+
expect(selector_builder.build(selector)).to eq built
|
|
450
|
+
end
|
|
586
451
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
452
|
+
it 'with index' do
|
|
453
|
+
selector = {adjacent: :preceding, index: 2}
|
|
454
|
+
built = {xpath: './preceding-sibling::*[3]'}
|
|
590
455
|
|
|
591
|
-
|
|
592
|
-
|
|
456
|
+
expect(selector_builder.build(selector)).to eq built
|
|
457
|
+
end
|
|
593
458
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
459
|
+
it 'with multiple locators' do
|
|
460
|
+
selector = {adjacent: :preceding, tag_name: 'div', class: 'b', id: true, index: 0}
|
|
461
|
+
built = {xpath: "./preceding-sibling::*[local-name()='div']" \
|
|
462
|
+
"[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
|
|
597
463
|
|
|
598
|
-
|
|
599
|
-
|
|
464
|
+
expect(selector_builder.build(selector)).to eq built
|
|
465
|
+
end
|
|
600
466
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
467
|
+
it 'with text' do
|
|
468
|
+
selector = {adjacent: :preceding, text: 'Second', index: 0}
|
|
469
|
+
built = {xpath: "./preceding-sibling::*[normalize-space()='Second'][1]"}
|
|
604
470
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
471
|
+
expect(selector_builder.build(selector)).to eq built
|
|
472
|
+
end
|
|
473
|
+
end
|
|
608
474
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
475
|
+
describe '#child' do
|
|
476
|
+
it 'with no other arguments' do
|
|
477
|
+
selector = {adjacent: :child, index: 0}
|
|
478
|
+
built = {xpath: './child::*[1]'}
|
|
479
|
+
|
|
480
|
+
expect(selector_builder.build(selector)).to eq built
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
it 'with index' do
|
|
484
|
+
selector = {adjacent: :child, index: 2}
|
|
485
|
+
built = {xpath: './child::*[3]'}
|
|
486
|
+
|
|
487
|
+
expect(selector_builder.build(selector)).to eq built
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
it 'with multiple locators' do
|
|
491
|
+
selector = {adjacent: :child, tag_name: 'div', class: 'b', id: true, index: 0}
|
|
492
|
+
built = {xpath: "./child::*[local-name()='div']" \
|
|
493
|
+
"[contains(concat(' ', normalize-space(@class), ' '), ' b ')][@id][1]"}
|
|
494
|
+
|
|
495
|
+
expect(selector_builder.build(selector)).to eq built
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
it 'with text' do
|
|
499
|
+
selector = {adjacent: :child, text: 'Second', index: 0}
|
|
500
|
+
built = {xpath: "./child::*[normalize-space()='Second'][1]"}
|
|
613
501
|
|
|
614
|
-
|
|
615
|
-
|
|
502
|
+
expect(selector_builder.build(selector)).to eq built
|
|
503
|
+
end
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
context 'with multiple locators' do
|
|
508
|
+
it 'locates using tag name, class, attributes and text' do
|
|
509
|
+
selector = {tag_name: 'div', class: 'content', contenteditable: 'true', text: 'Foo'}
|
|
510
|
+
built = {xpath: ".//*[local-name()='div']" \
|
|
511
|
+
"[contains(concat(' ', normalize-space(@class), ' '), ' content ')]" \
|
|
512
|
+
"[normalize-space()='Foo'][@contenteditable='true']"}
|
|
616
513
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
514
|
+
expect(selector_builder.build(selector)).to eq built
|
|
515
|
+
end
|
|
516
|
+
end
|
|
620
517
|
|
|
621
|
-
|
|
622
|
-
|
|
518
|
+
context 'with simple Regexp' do
|
|
519
|
+
it 'handles spaces' do
|
|
520
|
+
selector = {title: /od Lu/}
|
|
521
|
+
built = {xpath: './/*[contains(@title, "od Lu")]'}
|
|
623
522
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
built = {xpath: ".//*[local-name()='div']", visible: true}
|
|
523
|
+
expect(selector_builder.build(selector)).to eq built
|
|
524
|
+
end
|
|
627
525
|
|
|
628
|
-
|
|
629
|
-
|
|
526
|
+
it 'handles escaped characters' do
|
|
527
|
+
selector = {src: %r{ages/but}}
|
|
528
|
+
built = {xpath: './/*[contains(@src, "ages/but")]'}
|
|
630
529
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
530
|
+
expect(selector_builder.build(selector)).to eq built
|
|
531
|
+
end
|
|
532
|
+
end
|
|
634
533
|
|
|
635
|
-
|
|
636
|
-
|
|
534
|
+
context 'with complex Regexp' do
|
|
535
|
+
it 'handles wildcards' do
|
|
536
|
+
selector = {src: /ages.*but/}
|
|
537
|
+
built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages.*but/}
|
|
637
538
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
built = {xpath: ".//*[local-name()='span']", visible_text: 'foo'}
|
|
539
|
+
expect(selector_builder.build(selector)).to eq built
|
|
540
|
+
end
|
|
641
541
|
|
|
642
|
-
|
|
643
|
-
|
|
542
|
+
it 'handles optional characters' do
|
|
543
|
+
selector = {src: /ages ?but/}
|
|
544
|
+
built = {xpath: './/*[contains(@src, "ages") and contains(@src, "but")]', src: /ages ?but/}
|
|
644
545
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
msg = 'expected one of [TrueClass, FalseClass], got "foo":String'
|
|
546
|
+
expect(selector_builder.build(selector)).to eq built
|
|
547
|
+
end
|
|
648
548
|
|
|
649
|
-
|
|
650
|
-
|
|
549
|
+
it 'handles anchors' do
|
|
550
|
+
selector = {name: /^new_user_image$/}
|
|
551
|
+
built = {xpath: './/*[contains(@name, "new_user_image")]', name: /^new_user_image$/}
|
|
651
552
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
msg = /expected one of \[String, Regexp\], got 7:Integer/
|
|
553
|
+
expect(selector_builder.build(selector)).to eq built
|
|
554
|
+
end
|
|
655
555
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
556
|
+
it 'handles beginning anchor' do
|
|
557
|
+
selector = {src: /^i/}
|
|
558
|
+
built = {xpath: ".//*[starts-with(@src, 'i')]"}
|
|
659
559
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
let(:scope_built) { {xpath: ".//*[local-name()='div'][@id='table-rows-test']"} }
|
|
560
|
+
expect(selector_builder.build(selector)).to eq built
|
|
561
|
+
end
|
|
663
562
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
668
|
-
end
|
|
563
|
+
it 'does not use starts-with if visible locator used' do
|
|
564
|
+
selector = {id: /^vis/, visible_text: 'shown div'}
|
|
565
|
+
built = {xpath: './/*[contains(@id, "vis")]', id: /^vis/, visible_text: 'shown div'}
|
|
669
566
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
built = {xpath: "(#{scope_built[:xpath]})[1]//*[local-name()='div']"}
|
|
567
|
+
expect(selector_builder.build(selector)).to eq built
|
|
568
|
+
end
|
|
673
569
|
|
|
674
|
-
|
|
675
|
-
|
|
570
|
+
it 'handles case insensitive' do
|
|
571
|
+
selector = {action: /ME/i}
|
|
572
|
+
built = {xpath: './/*[contains(translate(@action,' \
|
|
573
|
+
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
|
574
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'), " \
|
|
575
|
+
'translate("me",' \
|
|
576
|
+
"'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'," \
|
|
577
|
+
"'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'))]"}
|
|
676
578
|
|
|
677
|
-
|
|
678
|
-
|
|
579
|
+
expect(selector_builder.build(selector)).to eq built
|
|
580
|
+
end
|
|
581
|
+
end
|
|
679
582
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
583
|
+
context 'with special cased selectors' do
|
|
584
|
+
it 'handles data-* attributes with String' do
|
|
585
|
+
selector = {data_foo: 'user_new'}
|
|
586
|
+
built = {xpath: ".//*[@data-foo='user_new']"}
|
|
684
587
|
|
|
685
|
-
|
|
686
|
-
|
|
588
|
+
expect(selector_builder.build(selector)).to eq built
|
|
589
|
+
end
|
|
687
590
|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
end
|
|
591
|
+
it 'handles aria-* attributes' do
|
|
592
|
+
selector = {aria_foo: 'user_new'}
|
|
593
|
+
built = {xpath: ".//*[@aria-foo='user_new']"}
|
|
692
594
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
built = {xpath: './ancestor::*[1]'}
|
|
595
|
+
expect(selector_builder.build(selector)).to eq built
|
|
596
|
+
end
|
|
696
597
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
end
|
|
701
|
-
end
|
|
598
|
+
it "doesn't modify attribute name when the attribute key is a string" do
|
|
599
|
+
selector = {'_underscore-dash' => 'user_new'}
|
|
600
|
+
built = {xpath: ".//*[@_underscore-dash='user_new']"}
|
|
702
601
|
|
|
703
|
-
|
|
704
|
-
|
|
602
|
+
expect(selector_builder.build(selector)).to eq built
|
|
603
|
+
end
|
|
705
604
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
605
|
+
it 'translates ruby attribute names to content attribute names' do
|
|
606
|
+
selector = {http_equiv: 'foo'}
|
|
607
|
+
built = {xpath: ".//*[@http-equiv='foo']"}
|
|
710
608
|
|
|
711
|
-
|
|
712
|
-
|
|
609
|
+
expect(selector_builder.build(selector)).to eq built
|
|
610
|
+
end
|
|
611
|
+
end
|
|
713
612
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
613
|
+
context 'when can not be directly translated' do
|
|
614
|
+
it 'returns locator from attribute with complicated Regexp at end' do
|
|
615
|
+
selector = {action: /me$/}
|
|
616
|
+
built = {xpath: './/*[contains(@action, "me")]', action: /me$/}
|
|
718
617
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
722
|
-
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
618
|
+
expect(selector_builder.build(selector)).to eq built
|
|
619
|
+
end
|
|
723
620
|
|
|
724
|
-
|
|
725
|
-
|
|
621
|
+
it 'returns locator from class with complicated Regexp' do
|
|
622
|
+
selector = {class: /he?r/}
|
|
623
|
+
built = {xpath: './/*[contains(@class, "h") and contains(@class, "r")]', class: [/he?r/]}
|
|
726
624
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
expect(build_selector).to eq built
|
|
730
|
-
end
|
|
731
|
-
end
|
|
625
|
+
expect(selector_builder.build(selector)).to eq built
|
|
626
|
+
end
|
|
732
627
|
|
|
733
|
-
|
|
734
|
-
|
|
628
|
+
it 'returns locator from visible' do
|
|
629
|
+
selector = {tag_name: 'div', visible: true}
|
|
630
|
+
built = {xpath: ".//*[local-name()='div']", visible: true}
|
|
735
631
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
selector_builder = described_class.new(attributes, query_scope)
|
|
632
|
+
expect(selector_builder.build(selector)).to eq built
|
|
633
|
+
end
|
|
739
634
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
allow(query_scope).to receive(:is_a?).with(Watir::Browser).and_return(false)
|
|
744
|
-
allow(query_scope).to receive(:is_a?).with(Watir::ShadowRoot).and_return(false)
|
|
745
|
-
allow(query_scope).to receive(:is_a?).with(Watir::IFrame).and_return(true)
|
|
635
|
+
it 'returns locator from not visible' do
|
|
636
|
+
selector = {tag_name: 'span', visible: false}
|
|
637
|
+
built = {xpath: ".//*[local-name()='span']", visible: false}
|
|
746
638
|
|
|
747
|
-
|
|
748
|
-
|
|
639
|
+
expect(selector_builder.build(selector)).to eq built
|
|
640
|
+
end
|
|
749
641
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
end
|
|
754
|
-
end
|
|
642
|
+
it 'returns locator from visible text' do
|
|
643
|
+
selector = {tag_name: 'span', visible_text: 'foo'}
|
|
644
|
+
built = {xpath: ".//*[local-name()='span']", visible_text: 'foo'}
|
|
755
645
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
let(:lower) { "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'" }
|
|
646
|
+
expect(selector_builder.build(selector)).to eq built
|
|
647
|
+
end
|
|
759
648
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
end
|
|
649
|
+
it 'raises exception when visible is not boolean' do
|
|
650
|
+
selector = {visible: 'foo'}
|
|
651
|
+
msg = 'expected one of [TrueClass, FalseClass], got "foo":String'
|
|
764
652
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
rhs = "translate('en',#{upper},#{lower})"
|
|
768
|
-
rhs_regex = "translate(\"en\",#{upper},#{lower})"
|
|
769
|
-
expect(selector_builder.build(lang: 'en')).to eq(xpath: ".//*[#{lhs}=#{rhs}]")
|
|
770
|
-
expect(selector_builder.build(lang: /en/)).to eq(xpath: ".//*[contains(#{lhs}, #{rhs_regex})]")
|
|
771
|
-
expect(selector_builder.build(tag_name: /a/, lang: 'en'))
|
|
772
|
-
.to eq(xpath: ".//*[contains(local-name(), \"a\")][#{lhs}=#{rhs}]")
|
|
773
|
-
expect(selector_builder.build(tag_name: /a/, lang: /en/))
|
|
774
|
-
.to eq(xpath: ".//*[contains(local-name(), \"a\")][contains(#{lhs}, #{rhs_regex})]")
|
|
775
|
-
end
|
|
653
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
654
|
+
end
|
|
776
655
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
rhs_regex = "translate(\"en\",#{upper},#{lower})"
|
|
781
|
-
expect(selector_builder.build(tag_name: 'a', hreflang: 'en'))
|
|
782
|
-
.to eq(xpath: ".//*[local-name()='a'][#{lhs}=#{rhs}]")
|
|
783
|
-
expect(selector_builder.build(tag_name: 'a', hreflang: /en/))
|
|
784
|
-
.to eq(xpath: ".//*[local-name()='a'][contains(#{lhs}, #{rhs_regex})]")
|
|
785
|
-
end
|
|
656
|
+
it 'raises exception when visible text is not a String or Regexp' do
|
|
657
|
+
selector = {visible_text: 7}
|
|
658
|
+
msg = /expected one of \[String, Regexp\], got 7:Integer/
|
|
786
659
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
660
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
context 'with generic element scope' do
|
|
665
|
+
let(:query_scope) { instance_double Watir::HTMLElement }
|
|
666
|
+
let(:scope_built) { {xpath: ".//*[local-name()='div'][@id='table-rows-test']"} }
|
|
667
|
+
|
|
668
|
+
before do
|
|
669
|
+
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
670
|
+
allow(query_scope).to receive(:browser).and_return(browser)
|
|
671
|
+
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
it 'uses scope' do
|
|
675
|
+
selector = {tag_name: 'div'}
|
|
676
|
+
built = {xpath: "(#{scope_built[:xpath]})[1]//*[local-name()='div']"}
|
|
677
|
+
|
|
678
|
+
expect(selector_builder.build(selector)).to eq built
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
it 'does not use scope if selector is a CSS' do
|
|
682
|
+
selector = {css: 'div'}
|
|
683
|
+
|
|
684
|
+
build_selector = selector_builder.build(selector)
|
|
685
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
686
|
+
expect(build_selector).to eq selector
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
it 'does not use scope if selector is a XPath' do
|
|
690
|
+
selector = {xpath: './/*'}
|
|
691
|
+
|
|
692
|
+
build_selector = selector_builder.build(selector)
|
|
693
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
694
|
+
expect(build_selector).to eq selector
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
it 'does not use scope if selector has :adjacent' do
|
|
698
|
+
selector = {adjacent: :ancestor, index: 0}
|
|
699
|
+
built = {xpath: './ancestor::*[1]'}
|
|
700
|
+
|
|
701
|
+
build_selector = selector_builder.build(selector)
|
|
702
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
703
|
+
expect(build_selector).to eq built
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
context 'with invalid query scopes' do
|
|
708
|
+
let(:query_scope) { instance_double Watir::HTMLElement }
|
|
709
|
+
|
|
710
|
+
it 'does not use scope if query_scope built has multiple keys' do
|
|
711
|
+
scope_built = {xpath: ".//*[local-name()='div']", visible: true}
|
|
712
|
+
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
713
|
+
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
714
|
+
|
|
715
|
+
selector = {tag_name: 'div'}
|
|
716
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
717
|
+
|
|
718
|
+
build_selector = selector_builder.build(selector)
|
|
719
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
720
|
+
expect(build_selector).to eq built
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
it 'does not use scope if query_scope uses different Selenium Locator' do
|
|
724
|
+
scope_built = {css: '#foo'}
|
|
725
|
+
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
726
|
+
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
727
|
+
|
|
728
|
+
selector = {tag_name: 'div'}
|
|
729
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
730
|
+
|
|
731
|
+
build_selector = selector_builder.build(selector)
|
|
732
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
733
|
+
expect(build_selector).to eq built
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
context 'with specific element scope' do
|
|
738
|
+
let(:scope_built) { {xpath: ".//*[local-name()='iframe'][@id='one']"} }
|
|
739
|
+
|
|
740
|
+
it 'does not use scope if query scope is an IFrame' do
|
|
741
|
+
query_scope = instance_double Watir::IFrame
|
|
742
|
+
selector_builder = described_class.new(attributes, query_scope)
|
|
743
|
+
|
|
744
|
+
allow(selector_builder).to receive(:built).and_return(scope_built)
|
|
745
|
+
allow(query_scope).to receive(:selector_builder).and_return(selector_builder)
|
|
746
|
+
allow(query_scope).to receive(:browser).and_return(browser)
|
|
747
|
+
allow(query_scope).to receive(:is_a?).with(Watir::Browser).and_return(false)
|
|
748
|
+
allow(query_scope).to receive(:is_a?).with(Watir::ShadowRoot).and_return(false)
|
|
749
|
+
allow(query_scope).to receive(:is_a?).with(Watir::IFrame).and_return(true)
|
|
750
|
+
|
|
751
|
+
selector = {tag_name: 'div'}
|
|
752
|
+
built = {xpath: ".//*[local-name()='div']"}
|
|
753
|
+
|
|
754
|
+
build_selector = selector_builder.build(selector)
|
|
755
|
+
expect(build_selector.delete(:scope)).not_to be_nil
|
|
756
|
+
expect(build_selector).to eq built
|
|
757
|
+
end
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
context 'with case-insensitive attributes' do
|
|
761
|
+
let(:upper) { "'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ'" }
|
|
762
|
+
let(:lower) { "'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ'" }
|
|
763
|
+
|
|
764
|
+
it 'respects case when locating uknown element with uknown attribute' do
|
|
765
|
+
expect(selector_builder.build(hreflang: 'en')).to eq(xpath: ".//*[@hreflang='en']")
|
|
766
|
+
expect(selector_builder.build(hreflang: /en/)).to eq(xpath: './/*[contains(@hreflang, "en")]')
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
it 'ignores case when locating uknown element with defined attribute' do
|
|
770
|
+
lhs = "translate(@lang,#{upper},#{lower})"
|
|
771
|
+
rhs = "translate('en',#{upper},#{lower})"
|
|
772
|
+
rhs_regex = "translate(\"en\",#{upper},#{lower})"
|
|
773
|
+
expect(selector_builder.build(lang: 'en')).to eq(xpath: ".//*[#{lhs}=#{rhs}]")
|
|
774
|
+
expect(selector_builder.build(lang: /en/)).to eq(xpath: ".//*[contains(#{lhs}, #{rhs_regex})]")
|
|
775
|
+
expect(selector_builder.build(tag_name: /a/, lang: 'en'))
|
|
776
|
+
.to eq(xpath: ".//*[contains(local-name(), \"a\")][#{lhs}=#{rhs}]")
|
|
777
|
+
expect(selector_builder.build(tag_name: /a/, lang: /en/))
|
|
778
|
+
.to eq(xpath: ".//*[contains(local-name(), \"a\")][contains(#{lhs}, #{rhs_regex})]")
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
it 'ignores case when attribute is defined for element' do
|
|
782
|
+
lhs = "translate(@hreflang,#{upper},#{lower})"
|
|
783
|
+
rhs = "translate('en',#{upper},#{lower})"
|
|
784
|
+
rhs_regex = "translate(\"en\",#{upper},#{lower})"
|
|
785
|
+
expect(selector_builder.build(tag_name: 'a', hreflang: 'en'))
|
|
786
|
+
.to eq(xpath: ".//*[local-name()='a'][#{lhs}=#{rhs}]")
|
|
787
|
+
expect(selector_builder.build(tag_name: 'a', hreflang: /en/))
|
|
788
|
+
.to eq(xpath: ".//*[local-name()='a'][contains(#{lhs}, #{rhs_regex})]")
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
it 'respects case when attribute is not defined for element' do
|
|
792
|
+
expect(selector_builder.build(tag_name: 'table', hreflang: 'en'))
|
|
793
|
+
.to eq(xpath: ".//*[local-name()='table'][@hreflang='en']")
|
|
794
|
+
expect(selector_builder.build(tag_name: 'table', hreflang: /en/))
|
|
795
|
+
.to eq(xpath: ".//*[local-name()='table'][contains(@hreflang, \"en\")]")
|
|
796
|
+
expect(selector_builder.build(tag_name: /a/, hreflang: 'en'))
|
|
797
|
+
.to eq(xpath: ".//*[contains(local-name(), \"a\")][@hreflang='en']")
|
|
798
|
+
expect(selector_builder.build(tag_name: /a/, hreflang: /en/))
|
|
799
|
+
.to eq(xpath: './/*[contains(local-name(), "a")][contains(@hreflang, "en")]')
|
|
800
|
+
end
|
|
801
|
+
end
|
|
802
|
+
end
|
|
796
803
|
end
|
|
797
804
|
end
|
|
798
805
|
end
|