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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +24 -6
  3. data/CHANGES.md +10 -0
  4. data/Gemfile +0 -2
  5. data/README.md +1 -1
  6. data/Rakefile +2 -2
  7. data/lib/watir.rb +2 -3
  8. data/lib/watir/adjacent.rb +2 -2
  9. data/lib/watir/alert.rb +5 -9
  10. data/lib/watir/attribute_helper.rb +2 -3
  11. data/lib/watir/browser.rb +5 -17
  12. data/lib/watir/capabilities.rb +11 -0
  13. data/lib/watir/cell_container.rb +2 -2
  14. data/lib/watir/container.rb +7 -8
  15. data/lib/watir/cookies.rb +2 -12
  16. data/lib/watir/element_collection.rb +2 -2
  17. data/lib/watir/elements/button.rb +2 -11
  18. data/lib/watir/elements/element.rb +43 -25
  19. data/lib/watir/elements/hidden.rb +1 -1
  20. data/lib/watir/elements/iframe.rb +7 -5
  21. data/lib/watir/elements/option.rb +2 -13
  22. data/lib/watir/elements/select.rb +6 -22
  23. data/lib/watir/elements/table.rb +2 -2
  24. data/lib/watir/exception.rb +1 -2
  25. data/lib/watir/generator/base/idl_sorter.rb +1 -1
  26. data/lib/watir/generator/base/spec_extractor.rb +2 -2
  27. data/lib/watir/generator/base/visitor.rb +1 -1
  28. data/lib/watir/generator/html/generator.rb +0 -1
  29. data/lib/watir/generator/html/spec_extractor.rb +1 -1
  30. data/lib/watir/generator/html/visitor.rb +1 -1
  31. data/lib/watir/generator/svg/spec_extractor.rb +1 -1
  32. data/lib/watir/generator/svg/visitor.rb +1 -1
  33. data/lib/watir/js_execution.rb +11 -0
  34. data/lib/watir/js_snippets.rb +1 -1
  35. data/lib/watir/js_snippets/elementObscured.js +14 -0
  36. data/lib/watir/js_snippets/selectedText.js +17 -0
  37. data/lib/watir/legacy_wait.rb +5 -5
  38. data/lib/watir/locators.rb +16 -5
  39. data/lib/watir/locators/anchor/selector_builder.rb +38 -0
  40. data/lib/watir/locators/button/locator.rb +14 -12
  41. data/lib/watir/locators/button/selector_builder.rb +0 -22
  42. data/lib/watir/locators/button/selector_builder/xpath.rb +67 -12
  43. data/lib/watir/locators/button/validator.rb +2 -1
  44. data/lib/watir/locators/cell/selector_builder.rb +0 -14
  45. data/lib/watir/locators/cell/selector_builder/xpath.rb +21 -0
  46. data/lib/watir/locators/element/locator.rb +60 -153
  47. data/lib/watir/locators/element/selector_builder.rb +103 -84
  48. data/lib/watir/locators/element/selector_builder/regexp_disassembler.rb +66 -0
  49. data/lib/watir/locators/element/selector_builder/xpath.rb +195 -82
  50. data/lib/watir/locators/element/selector_builder/xpath_support.rb +27 -0
  51. data/lib/watir/locators/element/validator.rb +2 -9
  52. data/lib/watir/locators/row/selector_builder.rb +5 -22
  53. data/lib/watir/locators/row/selector_builder/xpath.rb +53 -0
  54. data/lib/watir/locators/text_area/selector_builder.rb +1 -1
  55. data/lib/watir/locators/text_area/selector_builder/xpath.rb +19 -0
  56. data/lib/watir/locators/text_field/locator.rb +11 -8
  57. data/lib/watir/locators/text_field/selector_builder.rb +0 -23
  58. data/lib/watir/locators/text_field/selector_builder/xpath.rb +33 -4
  59. data/lib/watir/locators/text_field/validator.rb +4 -4
  60. data/lib/watir/radio_set.rb +5 -5
  61. data/lib/watir/row_container.rb +2 -2
  62. data/lib/watir/user_editable.rb +2 -2
  63. data/lib/watir/version.rb +1 -1
  64. data/lib/watir/wait.rb +24 -37
  65. data/lib/watir/window.rb +11 -8
  66. data/lib/watirspec/remote_server.rb +3 -1
  67. data/spec/locator_spec_helper.rb +1 -1
  68. data/spec/spec_helper.rb +25 -1
  69. data/spec/unit/anchor_locator_spec.rb +68 -0
  70. data/spec/unit/capabilities_spec.rb +27 -0
  71. data/spec/unit/element_locator_spec.rb +184 -101
  72. data/spec/unit/logger_spec.rb +5 -0
  73. data/spec/watirspec/adjacent_spec.rb +34 -34
  74. data/spec/watirspec/after_hooks_spec.rb +78 -35
  75. data/spec/watirspec/alert_spec.rb +10 -0
  76. data/spec/watirspec/browser_spec.rb +27 -1
  77. data/spec/watirspec/element_hidden_spec.rb +6 -0
  78. data/spec/watirspec/elements/button_spec.rb +5 -11
  79. data/spec/watirspec/elements/buttons_spec.rb +1 -1
  80. data/spec/watirspec/elements/checkbox_spec.rb +2 -15
  81. data/spec/watirspec/elements/date_time_field_spec.rb +6 -1
  82. data/spec/watirspec/elements/dd_spec.rb +0 -17
  83. data/spec/watirspec/elements/del_spec.rb +0 -14
  84. data/spec/watirspec/elements/div_spec.rb +0 -18
  85. data/spec/watirspec/elements/dl_spec.rb +0 -17
  86. data/spec/watirspec/elements/dt_spec.rb +0 -17
  87. data/spec/watirspec/elements/element_spec.rb +177 -17
  88. data/spec/watirspec/elements/elements_spec.rb +7 -6
  89. data/spec/watirspec/elements/em_spec.rb +0 -13
  90. data/spec/watirspec/elements/filefield_spec.rb +0 -11
  91. data/spec/watirspec/elements/form_spec.rb +6 -0
  92. data/spec/watirspec/elements/hn_spec.rb +0 -14
  93. data/spec/watirspec/elements/iframe_spec.rb +15 -0
  94. data/spec/watirspec/elements/ins_spec.rb +0 -14
  95. data/spec/watirspec/elements/labels_spec.rb +1 -1
  96. data/spec/watirspec/elements/li_spec.rb +0 -14
  97. data/spec/watirspec/elements/link_spec.rb +22 -14
  98. data/spec/watirspec/elements/links_spec.rb +13 -0
  99. data/spec/watirspec/elements/list_spec.rb +15 -0
  100. data/spec/watirspec/elements/ol_spec.rb +0 -14
  101. data/spec/watirspec/elements/option_spec.rb +0 -10
  102. data/spec/watirspec/elements/p_spec.rb +0 -14
  103. data/spec/watirspec/elements/pre_spec.rb +0 -14
  104. data/spec/watirspec/elements/radio_spec.rb +0 -14
  105. data/spec/watirspec/elements/select_list_spec.rb +0 -10
  106. data/spec/watirspec/elements/span_spec.rb +4 -15
  107. data/spec/watirspec/elements/strong_spec.rb +4 -15
  108. data/spec/watirspec/elements/table_nesting_spec.rb +1 -1
  109. data/spec/watirspec/elements/table_spec.rb +7 -0
  110. data/spec/watirspec/elements/text_field_spec.rb +10 -2
  111. data/spec/watirspec/elements/text_fields_spec.rb +1 -1
  112. data/spec/watirspec/elements/tr_spec.rb +1 -1
  113. data/spec/watirspec/elements/ul_spec.rb +0 -14
  114. data/spec/watirspec/html/closeable.html +8 -0
  115. data/spec/watirspec/html/forms_with_input_elements.html +28 -23
  116. data/spec/watirspec/html/nested_elements.html +9 -9
  117. data/spec/watirspec/html/obscured.html +34 -0
  118. data/spec/watirspec/html/tables.html +13 -13
  119. data/spec/watirspec/radio_set_spec.rb +5 -0
  120. data/spec/watirspec/selector_builder/button_spec.rb +254 -0
  121. data/spec/watirspec/selector_builder/cell_spec.rb +93 -0
  122. data/spec/watirspec/selector_builder/element_spec.rb +639 -0
  123. data/spec/watirspec/selector_builder/row_spec.rb +150 -0
  124. data/spec/watirspec/selector_builder/text_spec.rb +170 -0
  125. data/spec/watirspec/support/rspec_matchers.rb +6 -1
  126. data/spec/watirspec/user_editable_spec.rb +4 -0
  127. data/spec/watirspec/wait_spec.rb +65 -14
  128. data/spec/watirspec/window_switching_spec.rb +54 -1
  129. data/spec/watirspec_helper.rb +2 -0
  130. data/watir.gemspec +7 -1
  131. metadata +86 -8
  132. data/lib/watir/locators/text_area/locator.rb +0 -13
  133. 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
@@ -13,7 +13,7 @@ describe 'TextFields' do
13
13
 
14
14
  describe '#length' do
15
15
  it 'returns the number of text fields' do
16
- expect(browser.text_fields.length).to eq 18
16
+ expect(browser.text_fields.length).to eq 19
17
17
  end
18
18
  end
19
19
 
@@ -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').row(index: 1).cells.each_with_index do |cell, idx|
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
- <div id="messages"></div>
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" onsubmit="WatirSpec.addMessage('submit'); return false;">
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
- <input type="image" class="image" name="new_user_image" src="images/button.png" alt="Submittable button" />
132
- <input type="submit" name="new_user_submit_disabled" id="disabled_button" value="Disabled" disabled="disabled" />
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"></div>
27
- <span class="a" id="between_siblings2"></span>
28
- <div class="a b" id="third_sibling">text</div>
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