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
@@ -12,10 +12,9 @@ describe 'TextField' do
|
|
12
12
|
expect(browser.text_field(id: /new_user_email/)).to exist
|
13
13
|
expect(browser.text_field(name: 'new_user_email')).to exist
|
14
14
|
expect(browser.text_field(name: /new_user_email/)).to exist
|
15
|
+
expect(browser.text_field(name: /new_user_occupation/i)).to exist
|
15
16
|
expect(browser.text_field(value: 'Developer')).to exist
|
16
17
|
expect(browser.text_field(value: /Developer/)).to exist
|
17
|
-
expect(browser.text_field(text: 'Developer')).to exist
|
18
|
-
expect(browser.text_field(text: /Developer/)).to exist
|
19
18
|
expect(browser.text_field(class: 'name')).to exist
|
20
19
|
expect(browser.text_field(class: /name/)).to exist
|
21
20
|
expect(browser.text_field(index: 0)).to exist
|
@@ -37,6 +36,15 @@ describe 'TextField' do
|
|
37
36
|
expect(browser.text_field(visible_label: /With hidden text/)).not_to exist
|
38
37
|
end
|
39
38
|
|
39
|
+
it 'locates value of text_field using text locators' do
|
40
|
+
browser.text_field(id: 'new_user_occupation').set 'Firefighter'
|
41
|
+
|
42
|
+
expect(browser.text_field(text: 'Firefighter')).to exist
|
43
|
+
expect(browser.text_field(text: /Fire/)).to exist
|
44
|
+
expect(browser.text_field(visible_text: 'Firefighter')).to exist
|
45
|
+
expect(browser.text_field(visible_text: /Fire/)).to exist
|
46
|
+
end
|
47
|
+
|
40
48
|
it 'returns the first text field if given no args' do
|
41
49
|
expect(browser.text_field).to exist
|
42
50
|
end
|
@@ -68,7 +68,7 @@ describe 'TableRow' do
|
|
68
68
|
end
|
69
69
|
|
70
70
|
it 'iterates correctly through the cells of the row' do
|
71
|
-
browser.table(id: 'outer').
|
71
|
+
browser.table(id: 'outer').tr(index: 1).each_with_index do |cell, idx|
|
72
72
|
expect(cell.id).to eq "t1_r2_c#{idx + 1}"
|
73
73
|
end
|
74
74
|
end
|
@@ -35,20 +35,6 @@ describe 'Ul' do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Attribute methods
|
38
|
-
describe '#class_name' do
|
39
|
-
it 'returns the class attribute' do
|
40
|
-
expect(browser.ul(id: 'navbar').class_name).to eq 'navigation'
|
41
|
-
end
|
42
|
-
|
43
|
-
it "returns an empty string if the element exists and the attribute doesn't" do
|
44
|
-
expect(browser.ul(index: 1).class_name).to eq ''
|
45
|
-
end
|
46
|
-
|
47
|
-
it "raises UnknownObjectException if the ul doesn't exist" do
|
48
|
-
expect { browser.ul(id: 'no_such_id').class_name }.to raise_unknown_object_exception
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
38
|
describe '#id' do
|
53
39
|
it 'returns the id attribute' do
|
54
40
|
expect(browser.ul(class: 'navigation').id).to eq 'navbar'
|
@@ -2,11 +2,19 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<title>closeable window</title>
|
5
|
+
<script>
|
6
|
+
function closeDelayed(timeout) {
|
7
|
+
setTimeout(function() {
|
8
|
+
window.close();
|
9
|
+
}, timeout);
|
10
|
+
}
|
11
|
+
</script>
|
5
12
|
</head>
|
6
13
|
|
7
14
|
<body>
|
8
15
|
<p>
|
9
16
|
Click <a id="close" href="#" onclick="window.close()">here</a> to close this window.
|
17
|
+
Click <a id="close-delay" href="#" onclick="closeDelayed(500)">here</a> to close this window with a delay.
|
10
18
|
</p>
|
11
19
|
</body>
|
12
20
|
|
@@ -4,23 +4,25 @@
|
|
4
4
|
<title>Forms with input elements</title>
|
5
5
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
6
6
|
<meta name="description" content="this is a test page for forms with input elements" />
|
7
|
-
<link rel="stylesheet" href="watirspec.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
7
|
+
<link rel="stylesheet" href="watirspec.css" type="text/css" media="screen" title="no title" charset="utf-8" data-locator="link">
|
8
8
|
<script src="javascript/helpers.js" type="text/javascript" charset="utf-8"></script>
|
9
9
|
</head>
|
10
|
-
<body onload="document.user_new.new_user_first_name.focus()">
|
11
|
-
|
10
|
+
<body data-locator="body" onload="document.user_new.new_user_first_name.focus()">
|
11
|
+
<div id="messages" class="multiple classes here" random="foo" data-locator="first div"></div>
|
12
|
+
<div id="there" class="multiple classes there" data-locator="second div"></div>
|
12
13
|
<h1><a href="">User administration</a></h1>
|
13
|
-
<h2 style="background-color: gray;">Add user</h2>
|
14
|
-
<form enctype="multipart/form-data" action="post_to_me" method="post" name="user_new" id="new_user" class="user"
|
14
|
+
<h2 data-locator="add user" style="background-color: gray;">Add user</h2>
|
15
|
+
<form enctype="multipart/form-data" action="post_to_me" method="post" name="user_new" id="new_user" class="user"
|
16
|
+
data-locator="form" onsubmit="WatirSpec.addMessage('submit'); return false;">
|
15
17
|
<fieldset>
|
16
|
-
<input type="hidden" /> <!-- Ensure it's not included in #text_field -->
|
18
|
+
<input type="hidden" data-locator="input nameless"/> <!-- Ensure it's not included in #text_field -->
|
17
19
|
<legend>Personal information</legend>
|
18
20
|
<label for="new_user_first_name" id="first_label" onclick="WatirSpec.addMessage('label')">First name</label>
|
19
|
-
<input name="new_user_first_name" id="new_user_first_name" class="name" /> <br />
|
21
|
+
<input name="new_user_first_name" id="new_user_first_name" class="name" data-locator="input name"/> <br />
|
20
22
|
<label for="new_user_last_name">Last name</label>
|
21
23
|
<input type="no_such_type" name="new_user_last_name" id="new_user_last_name" class="name" /> <br />
|
22
24
|
<label for="new_user_email">Email address</label>
|
23
|
-
<input type="text" name="new_user_email" id="new_user_email" /> <br />
|
25
|
+
<input type="text" name="new_user_email" id="new_user_email" data-locator="first text"/> <br />
|
24
26
|
<label for="new_user_email_confirm">Email address (confirmation)</label>
|
25
27
|
<input type="Text" name="new_user_email_confirm" id="new_user_email_confirm" /> <br />
|
26
28
|
<label for="new_user_country">Country</label>
|
@@ -33,11 +35,11 @@
|
|
33
35
|
<optgroup label="other">
|
34
36
|
<option value="4">United Kingdom</option>
|
35
37
|
<option value="5">USA</option>
|
36
|
-
<option label="Germany" />
|
38
|
+
<option label="Germany" data-locator="Berliner" />
|
37
39
|
</optgroup>
|
38
40
|
</select> <br />
|
39
41
|
<label for="new_user_occupation">Occupation</label>
|
40
|
-
<input type="text" name="new_user_occupation" id="new_user_occupation" value="Developer" onfocus="document.getElementById('onfocus_test').innerHTML = 'changed by onfocus event'"/> <br />
|
42
|
+
<input type="text" class="c" name="new_user_occupation" data-locator="dev" id="new_user_occupation" value="Developer" onfocus="document.getElementById('onfocus_test').innerHTML = 'changed by onfocus event'"/> <br />
|
41
43
|
<label>Without for <input /></label>
|
42
44
|
<label>With<span style="display:none;"> hidden</span> text<input /></label>
|
43
45
|
<label for="new_user_species">Species</label>
|
@@ -45,8 +47,8 @@
|
|
45
47
|
<label for="new_user_code">Personal code</label>
|
46
48
|
<input type="text" title="Your personal code" name="new_user_code" id="new_user_code" value="HE2FF8" readonly="readonly" /> <br />
|
47
49
|
<label for="good_luck">Good Luck</label>
|
48
|
-
<input type="text" title="Good Luck" name="good_luck" id="good_luck" disabled="disabled" readonly="readonly" /> <br />
|
49
|
-
<input type="col" id="unknown_text_field" /> <br />
|
50
|
+
<input type="text" title="Good Luck" name="good_luck" id="good_luck" disabled="disabled" readonly="readonly" data-locator="Good Luck" /> <br />
|
51
|
+
<input type="col" id="unknown_text_field" data-locator="unknown" /> <br />
|
50
52
|
<label for="new_user_languages">Languages</label>
|
51
53
|
<select name="new_user_languages" id="new_user_languages" multiple="multiple" onchange="WatirSpec.addMessage('changed language');">
|
52
54
|
<option id="danish" value="1">Danish</option>
|
@@ -78,13 +80,15 @@
|
|
78
80
|
<input type="week" id="html5_week" name="html5_week" />
|
79
81
|
<label for="html5_time">HTML5 Time</label>
|
80
82
|
<input type="time" id="html5_time" name="html5_time" />
|
83
|
+
<label for="number">Age</label>
|
84
|
+
<input type="number" value="42" name="number" id="number" data-locator="42">
|
81
85
|
</fieldset>
|
82
86
|
<fieldset>
|
83
87
|
<legend>Login information</legend>
|
84
88
|
<label for="new_user_username">Username (max 20 characters)</label>
|
85
89
|
<input type="text" name="new_user_username" id="new_user_username" maxlength="20" size="20" onkeyup="document.getElementById('current_length').innerHTML = this.value.length"/> <span id="current_length">0</span><br />
|
86
90
|
<label for="new_user_password">Password</label>
|
87
|
-
<input type="password" name="new_user_password" id="new_user_password" /> <br />
|
91
|
+
<input type="password" name="new_user_password" id="new_user_password" data-locator="last text"/> <br />
|
88
92
|
<label for="new_user_role">Role</label>
|
89
93
|
<select name="new_user_role" id="new_user_role" disabled="disabled">
|
90
94
|
<option>Administrator</option>
|
@@ -96,7 +100,7 @@
|
|
96
100
|
<legend>Interests</legend>
|
97
101
|
<input type="checkbox" tabindex="1" name="new_user_interests" id="new_user_interests_books" value="books" checked="checked" /> <label for="new_user_interests_books">Books</label>
|
98
102
|
<input type="checkbox" tabindex="2" name="new_user_interests" value="bowling" /> <label>Bowling</label>
|
99
|
-
<input type="checkbox" tabindex="3" name="new_user_interests" id="new_user_interests_cars" value="cars" /> <label for="new_user_interests_cars">Cars</label>
|
103
|
+
<input type="checkbox" tabindex="3" name="new_user_interests" id="new_user_interests_cars" value="cars" data-locator="cars" /> <label for="new_user_interests_cars">Cars</label>
|
100
104
|
<input type="checkbox" tabindex="4" name="new_user_interests" id="new_user_interests_dancing" value="dancing" class="fun" title="Dancing is fun!" /> <label for="new_user_interests_dancing">Dancing</label>
|
101
105
|
<input type="checkbox" tabindex="5" name="new_user_interests" id="new_user_interests_dentistry" value="dentistry" disabled="disabled" /> <label for="new_user_interests_dentistry">Dentistry</label>
|
102
106
|
<input type="hidden" name="new_user_interests" id="new_user_interests_dolls" value="dolls" class="fun" />
|
@@ -123,13 +127,14 @@
|
|
123
127
|
</fieldset>
|
124
128
|
<fieldset>
|
125
129
|
<legend>Actions</legend>
|
126
|
-
<input type="submit" title="Submit the form" name="new_user_submit" id="new_user_submit" value="Submit" />
|
127
|
-
<input type="reset" name="new_user_reset" id="new_user_reset" value="Reset" />
|
128
|
-
<input type="button" name="new_user_button" id="new_user_button" alt="Create a new user" value="Button" onclick="this.value = 'new_value_set_by_onclick_event'" />
|
129
|
-
<input type="BuTTon" name="new_user_button_preview" id="new_user_button_preview" alt="Create a new user" value="Preview" />
|
130
|
-
<button name="new_user_button_2" value="button_2">Button 2</button>
|
131
|
-
<
|
132
|
-
<input type="
|
130
|
+
<input type="submit" title="Submit the form" name="new_user_submit" data-locator="user submit" id="new_user_submit" value="Submit" />
|
131
|
+
<input type="reset" name="new_user_reset" id="new_user_reset" value="Reset" data-locator="reset" />
|
132
|
+
<input type="button" name="new_user_button" id="new_user_button" data-locator="new user" alt="Create a new user" value="Button" onclick="this.value = 'new_value_set_by_onclick_event'" />
|
133
|
+
<input type="BuTTon" name="new_user_button_preview" id="new_user_button_preview" alt="Create a new user" value="Preview" data-locator="preview"/>
|
134
|
+
<button name="new_user_button_2" type="submit" value="button_2" data-locator="Benjamin">Button 2</button>
|
135
|
+
<button name="new_user_button_3" value="button_3" data-locator="No Type">Button 3</button>
|
136
|
+
<input type="image" class="image" name="new_user_image" src="images/button.png" alt="Submittable button" data-locator="submittable button"/>
|
137
|
+
<input type="submit" name="new_user_submit_disabled" id="disabled_button" value="Disabled" disabled="disabled" data-locator="disabled" />
|
133
138
|
<input type="checkbox" name="new_user_submit_disabled" id="toggle_button_checkbox" onclick="var elem = document.getElementById('disabled_button'); elem.disabled = !elem.disabled" />
|
134
139
|
</fieldset>
|
135
140
|
</form>
|
@@ -151,7 +156,7 @@
|
|
151
156
|
</form>
|
152
157
|
|
153
158
|
<!-- testing popup windows -->
|
154
|
-
<input type="button" name="new_popup_window" value="Open window" id="new_popup_window" onclick="window.open('tables.html')">
|
159
|
+
<input type="button" name="new_popup_window" value="Open window" id="new_popup_window" data-locator="last button" onclick="window.open('tables.html')">
|
155
160
|
|
156
161
|
<!-- used for testing javascript events and styles -->
|
157
162
|
<div id="changed_language" style="visibility: hidden;">
|
@@ -161,7 +166,7 @@
|
|
161
166
|
<div id="wants_newsletter" style="display: none;"></div>
|
162
167
|
<div id="onfocus_test"></div>
|
163
168
|
|
164
|
-
<div id="contenteditable" contenteditable="true">Foo</div>
|
169
|
+
<div id="contenteditable" contenteditable="true" class="content" data-locator="content">Foo</div>
|
165
170
|
|
166
171
|
<!-- option disappears onchange -->
|
167
172
|
<select id="obsolete" onchange="this.innerHTML = '';">
|
@@ -7,11 +7,11 @@
|
|
7
7
|
|
8
8
|
<body>
|
9
9
|
<span id="grandparent_span">
|
10
|
-
<div id="grandparent">
|
11
|
-
<span class="parent" id="parent_span">
|
12
|
-
<div id="parent">
|
13
|
-
<div id="first_sibling">
|
14
|
-
<span id="child_span">
|
10
|
+
<div id="grandparent" class="ancestor" data-locator="grandparent">
|
11
|
+
<span class="parent" class="ancestor" id="parent_span" data-locator="grandparent span">
|
12
|
+
<div id="parent" class="ancestor" data-locator="parent">
|
13
|
+
<div id="first_sibling" data-locator="first sibling">
|
14
|
+
<span id="child_span" data-locator="child span">
|
15
15
|
<div id="oldest_child">
|
16
16
|
<span id="grandson_span">
|
17
17
|
<div id="first_grandson"></div>
|
@@ -22,10 +22,10 @@
|
|
22
22
|
<div id="youngest_child"></div>
|
23
23
|
</span>
|
24
24
|
</div>
|
25
|
-
<span class="a" id="between_siblings1"></span>
|
26
|
-
<div class="b" id="second_sibling"
|
27
|
-
<span class="a" id="between_siblings2"></span>
|
28
|
-
<div class="a b" id="third_sibling">
|
25
|
+
<span class="a" id="between_siblings1" data-locator="between_siblings1"></span>
|
26
|
+
<div class="b" id="second_sibling" data-locator="second_sibling">Second</div>
|
27
|
+
<span class="a" id="between_siblings2" data-locator="between_siblings2"></span>
|
28
|
+
<div class="a b" id="third_sibling" data-locator="third_sibling">Third</div>
|
29
29
|
</div>
|
30
30
|
</span>
|
31
31
|
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
4
|
+
<title>Obscured</title>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<h1>No Overlap</h1>
|
8
|
+
<button style="height: 100px; width: 100px;" id="not_obscured"></button>
|
9
|
+
|
10
|
+
<h1>Overlap By Descendant</h1>
|
11
|
+
<button style="height: 100px; width: 100px;" id="has_descendant">
|
12
|
+
<div style="height: 90px; width: 90px; background: rgba(0,255,0,.5);">foobar</div>
|
13
|
+
</button>
|
14
|
+
|
15
|
+
<h1>Overlap By Other</h1>
|
16
|
+
<p>
|
17
|
+
<button style="width: 100px; margin: 40px 0;" id="obscured">foobar</button>
|
18
|
+
</p>
|
19
|
+
<div style="position: absolute; height: 100px; width: 100px; background: rgba(255,0,0,.5); margin-left: 40px; margin-top: -120px;"></div>
|
20
|
+
|
21
|
+
<h1>Overlap But Not At Center</h1>
|
22
|
+
<p>
|
23
|
+
<button style="width: 100px; height: 100px;" id="surrounded">foobar</button>
|
24
|
+
</p>
|
25
|
+
<div style="position: absolute; height: 50px; width: 100px; background: rgba(255,0,0,.5); margin-top: -120px;"></div>
|
26
|
+
<div style="position: absolute; height: 50px; width: 100px; background: rgba(255,0,0,.5); margin-top: -60px;"></div>
|
27
|
+
<div style="position: absolute; height: 100px; width: 50px; background: rgba(255,0,0,.5); margin-top: -120px; margin-left: -5px;"></div>
|
28
|
+
<div style="position: absolute; height: 100px; width: 50px; background: rgba(255,0,0,.5); margin-top: -120px; margin-left: 55px;"></div>
|
29
|
+
|
30
|
+
<button style="position: absolute; width: 0; height: 0; top: -500px; left: -500px; overflow: hidden" id="off_screen">off-screen</button>
|
31
|
+
<button style="display: none;" id="hidden">hidden</button>
|
32
|
+
<button style="position: absolute; top: 5000px;" id="requires_scrolling">requires scrolling</button>
|
33
|
+
</body>
|
34
|
+
</html>
|
@@ -9,12 +9,12 @@
|
|
9
9
|
<div id="messages"></div>
|
10
10
|
<h1>Tables</h1>
|
11
11
|
<h2>Semantic table</h2>
|
12
|
-
<table cellspacing="0" cellpadding="5" rules="groups" frame="box" border="1" id="axis_example" summary="Hugh Laurie made more money than Gregory House in both March and April of 2008.">
|
12
|
+
<table cellspacing="0" data-locator="top table" cellpadding="5" rules="groups" frame="box" border="1" id="axis_example" summary="Hugh Laurie made more money than Gregory House in both March and April of 2008.">
|
13
13
|
<caption>Income tax information for Gregory House and Hugh Laurie</caption>
|
14
14
|
<colgroup width="25%" align="left"></colgroup>
|
15
15
|
<colgroup width="25%" span="3" align="right"></colgroup>
|
16
16
|
<thead id="tax_headers">
|
17
|
-
<tr style="border-bottom: 1px solid black;" class="dark" id="thead_row_1">
|
17
|
+
<tr style="border-bottom: 1px solid black;" class="dark" id="thead_row_1" data-locator="thead row">
|
18
18
|
<th></th>
|
19
19
|
<th id="before_tax" axis="cash" abbr="Before tax">Before income tax</th>
|
20
20
|
<th id="tax" axis="cash" abbr="Tax">Income tax</th>
|
@@ -22,7 +22,7 @@
|
|
22
22
|
</tr>
|
23
23
|
</thead>
|
24
24
|
<tfoot id="tax_totals">
|
25
|
-
<tr class="dark" id="tfoot_row_1">
|
25
|
+
<tr class="dark" id="tfoot_row_1" data-locator="tfoot row">
|
26
26
|
<th axis="sum">Sum</th>
|
27
27
|
<td headers="before_tax">24 349</td>
|
28
28
|
<td headers="tax">5 577</td>
|
@@ -30,20 +30,20 @@
|
|
30
30
|
</tr>
|
31
31
|
</tfoot>
|
32
32
|
<tbody id="first">
|
33
|
-
<tr id="march">
|
33
|
+
<tr id="march" data-locator="tbody row">
|
34
34
|
<th id="d1" axis="date" scope="rowgroup">March 2008</th>
|
35
35
|
<th></th>
|
36
36
|
<th></th>
|
37
37
|
<th></th>
|
38
38
|
</tr>
|
39
|
-
<tr id="gregory">
|
40
|
-
<td id="p1" abbr="Dr. House">Gregory House</td>
|
41
|
-
<td headers="before_tax p1 d1">5 934</td>
|
42
|
-
<td headers="tax p1 d1">1 347</td>
|
43
|
-
<td headers="after_tax p1 d1">4 587</td>
|
39
|
+
<tr id="gregory" class="brick" data-locator="House row">
|
40
|
+
<td id="p1" abbr="Dr. House"data-locator="first cell">Gregory House</td>
|
41
|
+
<td headers="before_tax p1 d1" data-locator="before tax">5 934</td>
|
42
|
+
<td headers="tax p1 d1" data-locator="tax">1 347</td>
|
43
|
+
<td id="last" headers="after_tax p1 d1" data-locator="after tax">4 587</td>
|
44
44
|
</tr>
|
45
45
|
<tr id="hugh">
|
46
|
-
<td id="p2" abbr="Laurie">Hugh Laurie</td>
|
46
|
+
<td id="p2" abbr="Laurie" data-locator="hugh">Hugh Laurie</td>
|
47
47
|
<td headers="before_tax p2 d1">6 300</td>
|
48
48
|
<td headers="tax p2 d1">1 479</td>
|
49
49
|
<td headers="after_tax p2 d1">4 821</td>
|
@@ -72,7 +72,7 @@
|
|
72
72
|
</table>
|
73
73
|
<h2>Table inside a table</h2>
|
74
74
|
<table border="1" id="outer">
|
75
|
-
<tr id="outer_first">
|
75
|
+
<tr id="outer_first" data-locator="first row">
|
76
76
|
<td>
|
77
77
|
Table 1, Row 1, Cell 1
|
78
78
|
</td>
|
@@ -80,7 +80,7 @@
|
|
80
80
|
Table 1, Row 1, Cell 2
|
81
81
|
</td>
|
82
82
|
</tr>
|
83
|
-
<tr id="outer_second">
|
83
|
+
<tr id="outer_second" data-locator="middle row">
|
84
84
|
<td id="t1_r2_c1">
|
85
85
|
Table 1, Row 2, Cell 1
|
86
86
|
</td>
|
@@ -98,7 +98,7 @@
|
|
98
98
|
</table>
|
99
99
|
</td>
|
100
100
|
</tr>
|
101
|
-
<tr id="outer_last">
|
101
|
+
<tr id="outer_last" data-locator="last row">
|
102
102
|
<td>
|
103
103
|
Table 1, Row 3, Cell 1
|
104
104
|
</td>
|
@@ -206,6 +206,11 @@ describe 'RadioSet' do
|
|
206
206
|
radio_set = browser.radio_set(id: 'new_user_newsletter_yes')
|
207
207
|
expect { radio_set.radios(name: '') }.to raise_unknown_object_exception
|
208
208
|
end
|
209
|
+
|
210
|
+
it 'returns the radio button at the specified index' do
|
211
|
+
radio_set = browser.radio_set(id: 'new_user_newsletter_yes')
|
212
|
+
expect(radio_set[1]).to be_a Watir::Radio
|
213
|
+
end
|
209
214
|
end
|
210
215
|
|
211
216
|
describe '#selected' do
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'watirspec_helper'
|
2
|
+
|
3
|
+
describe Watir::Locators::Button::SelectorBuilder do
|
4
|
+
let(:attributes) { Watir::HTMLElement.attribute_list }
|
5
|
+
let(:scope_tag_name) { nil }
|
6
|
+
let(:selector_builder) { described_class.new(attributes) }
|
7
|
+
let(:uppercase) { 'ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞŸŽŠŒ' }
|
8
|
+
let(:lowercase) { 'abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿžšœ' }
|
9
|
+
let(:default_types) do
|
10
|
+
"translate(@type,'#{uppercase}','#{lowercase}')='button' or" \
|
11
|
+
" translate(@type,'#{uppercase}','#{lowercase}')='reset' or"\
|
12
|
+
" translate(@type,'#{uppercase}','#{lowercase}')='submit' or"\
|
13
|
+
" translate(@type,'#{uppercase}','#{lowercase}')='image'"
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#build' do
|
17
|
+
after(:each) do |example|
|
18
|
+
next if example.metadata[:skip_after]
|
19
|
+
|
20
|
+
@query_scope ||= browser
|
21
|
+
built = selector_builder.build(@selector)
|
22
|
+
expect(built).to eq [@wd_locator, (@remaining || {})]
|
23
|
+
|
24
|
+
next unless @data_locator || @tag_name
|
25
|
+
|
26
|
+
expect { @located = @query_scope.wd.first(@wd_locator) }.not_to raise_exception
|
27
|
+
|
28
|
+
if @data_locator
|
29
|
+
expect(@located.attribute('data-locator')).to eq(@data_locator)
|
30
|
+
else
|
31
|
+
expect {
|
32
|
+
expect(@located.tag_name).to eq @tag_name
|
33
|
+
}.not_to raise_exception
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'without any arguments' do
|
38
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
39
|
+
@selector = {}
|
40
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]"}
|
41
|
+
@data_locator = 'user submit'
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'with type' do
|
45
|
+
before(:each) { browser.goto(WatirSpec.url_for('forms_with_input_elements.html')) }
|
46
|
+
|
47
|
+
it 'false only locates with button without a type' do
|
48
|
+
@selector = {type: false}
|
49
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and not(@type))]"}
|
50
|
+
@data_locator = 'No Type'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'true locates button or input with a type' do
|
54
|
+
@selector = {type: true}
|
55
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and @type) or " \
|
56
|
+
"(local-name()='input' and (#{default_types}))]"}
|
57
|
+
@data_locator = 'user submit'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'locates input or button element with specified type' do
|
61
|
+
@selector = {type: 'reset'}
|
62
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and " \
|
63
|
+
"translate(@type,'#{uppercase}','#{lowercase}')='reset') or " \
|
64
|
+
"(local-name()='input' and (translate(@type,'#{uppercase}','#{lowercase}')='reset'))]"}
|
65
|
+
@data_locator = 'reset'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'raises exception when a non-button type input is specified', skip_after: true do
|
69
|
+
selector = {type: 'checkbox'}
|
70
|
+
msg = 'Button Elements can not be located by input type: checkbox'
|
71
|
+
expect { selector_builder.build(selector) }
|
72
|
+
.to raise_exception Watir::Exception::LocatorException, msg
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'with xpath or css' do
|
77
|
+
before(:each) do
|
78
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns tag name and type to the locator' do
|
82
|
+
@selector = {xpath: '#disabled_button', tag_name: 'input', type: 'submit'}
|
83
|
+
@wd_locator = {xpath: '#disabled_button'}
|
84
|
+
@remaining = {tag_name: 'input', type: 'submit'}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with text' do
|
89
|
+
before(:each) { browser.goto(WatirSpec.url_for('forms_with_input_elements.html')) }
|
90
|
+
|
91
|
+
it 'locates value of input element with String' do
|
92
|
+
@selector = {text: 'Button'}
|
93
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and normalize-space()='Button') or " \
|
94
|
+
"(local-name()='input' and (#{default_types}) and @value='Button')]"}
|
95
|
+
@data_locator = 'new user'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'locates text of button element with String' do
|
99
|
+
@selector = {text: 'Button 2'}
|
100
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and normalize-space()='Button 2') or " \
|
101
|
+
"(local-name()='input' and (#{default_types}) and @value='Button 2')]"}
|
102
|
+
@data_locator = 'Benjamin'
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'locates value of input element with simple Regexp' do
|
106
|
+
@selector = {text: /Button/}
|
107
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and contains(text(), 'Button')) or " \
|
108
|
+
"(local-name()='input' and (#{default_types}) and contains(@value, 'Button'))]"}
|
109
|
+
@data_locator = 'new user'
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'locates text of button element with simple Regexp' do
|
113
|
+
@selector = {text: /Button 2/}
|
114
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and contains(text(), 'Button 2')) or " \
|
115
|
+
"(local-name()='input' and (#{default_types}) and contains(@value, 'Button 2'))]"}
|
116
|
+
@data_locator = 'Benjamin'
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'Simple Regexp for text' do
|
120
|
+
@selector = {text: /n 2/}
|
121
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and contains(text(), 'n 2')) or " \
|
122
|
+
"(local-name()='input' and (#{default_types}) and contains(@value, 'n 2'))]"}
|
123
|
+
@data_locator = 'Benjamin'
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'Simple Regexp for value' do
|
127
|
+
@selector = {text: /Prev/}
|
128
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and contains(text(), 'Prev')) or " \
|
129
|
+
"(local-name()='input' and (#{default_types}) and contains(@value, 'Prev'))]"}
|
130
|
+
@data_locator = 'preview'
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'returns complex Regexp to the locator' do
|
134
|
+
@selector = {text: /^foo$/}
|
135
|
+
@wd_locator = {xpath: ".//*[(local-name()='button' and contains(text(), 'foo')) or " \
|
136
|
+
"(local-name()='input' and (#{default_types}) and contains(@value, 'foo'))]"}
|
137
|
+
@remaining = {text: /^foo$/}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'with value' do
|
142
|
+
before(:each) { browser.goto(WatirSpec.url_for('forms_with_input_elements.html')) }
|
143
|
+
|
144
|
+
it 'input element value with String' do
|
145
|
+
@selector = {value: 'Preview'}
|
146
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
147
|
+
"[normalize-space()='Preview' or @value='Preview']"}
|
148
|
+
@data_locator = 'preview'
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'button element value with String' do
|
152
|
+
@selector = {value: 'button_2'}
|
153
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
154
|
+
"[normalize-space()='button_2' or @value='button_2']"}
|
155
|
+
@data_locator = 'Benjamin'
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'input element value with simple Regexp' do
|
159
|
+
@selector = {value: /Prev/}
|
160
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
161
|
+
"[contains(text(), 'Prev') or contains(@value, 'Prev')]"}
|
162
|
+
@data_locator = 'preview'
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'button element value with simple Regexp' do
|
166
|
+
@selector = {value: /on_2/}
|
167
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
168
|
+
"[contains(text(), 'on_2') or contains(@value, 'on_2')]"}
|
169
|
+
@data_locator = 'Benjamin'
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'button element text with String' do
|
173
|
+
@selector = {value: 'Button 2'}
|
174
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
175
|
+
"[normalize-space()='Button 2' or @value='Button 2']"}
|
176
|
+
@data_locator = 'Benjamin'
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'button element text with simple Regexp' do
|
180
|
+
@selector = {value: /ton 2/}
|
181
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
182
|
+
"[contains(text(), 'ton 2') or contains(@value, 'ton 2')]"}
|
183
|
+
@data_locator = 'Benjamin'
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'returns complex Regexp to the locator' do
|
187
|
+
@selector = {value: /^foo$/}
|
188
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
189
|
+
"[contains(text(), 'foo') or contains(@value, 'foo')]"}
|
190
|
+
@remaining = {value: /^foo$/}
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context 'with index' do
|
195
|
+
before(:each) do
|
196
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'positive' do
|
200
|
+
@selector = {index: 3}
|
201
|
+
@wd_locator = {xpath: "(.//*[(local-name()='button') or (local-name()='input' and (#{default_types}))])[4]"}
|
202
|
+
@data_locator = 'preview'
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'negative' do
|
206
|
+
@selector = {index: -4}
|
207
|
+
@wd_locator = {xpath: "(.//*[(local-name()='button') or " \
|
208
|
+
"(local-name()='input' and (#{default_types}))])[last()-3]"}
|
209
|
+
@data_locator = 'submittable button'
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'last' do
|
213
|
+
@selector = {index: -1}
|
214
|
+
@wd_locator = {xpath: "(.//*[(local-name()='button') or " \
|
215
|
+
"(local-name()='input' and (#{default_types}))])[last()]"}
|
216
|
+
@data_locator = 'last button'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'does not return index if it is zero' do
|
220
|
+
@selector = {index: 0}
|
221
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]"}
|
222
|
+
@data_locator = 'user submit'
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'raises exception when index is not an Integer', skip_after: true do
|
226
|
+
selector = {index: 'foo'}
|
227
|
+
msg = 'expected Integer, got "foo":String'
|
228
|
+
expect { selector_builder.build(selector) }.to raise_exception TypeError, msg
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'with multiple locators' do
|
233
|
+
before(:each) do
|
234
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'locates using class and attributes' do
|
238
|
+
@selector = {class: 'image', name: 'new_user_image', src: true}
|
239
|
+
@wd_locator = {xpath: ".//*[(local-name()='button') or (local-name()='input' and (#{default_types}))]" \
|
240
|
+
"[contains(concat(' ', @class, ' '), ' image ')][@name='new_user_image' and @src]"}
|
241
|
+
@data_locator = 'submittable button'
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'delegates adjacent to Element SelectorBuilder' do
|
246
|
+
browser.goto(WatirSpec.url_for('forms_with_input_elements.html'))
|
247
|
+
@query_scope = browser.element(id: 'new_user_button').locate
|
248
|
+
|
249
|
+
@selector = {adjacent: :ancestor, index: 2}
|
250
|
+
@wd_locator = {xpath: './ancestor::*[3]'}
|
251
|
+
@data_locator = 'body'
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|