watir 6.14.0 → 6.15.0

Sign up to get free protection for your applications and to get access to all the features.
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