capybara 2.7.0 → 3.35.3

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 (318) hide show
  1. checksums.yaml +5 -5
  2. data/.yardopts +1 -0
  3. data/History.md +1147 -11
  4. data/License.txt +1 -1
  5. data/README.md +252 -131
  6. data/lib/capybara/config.rb +92 -0
  7. data/lib/capybara/cucumber.rb +3 -3
  8. data/lib/capybara/driver/base.rb +52 -21
  9. data/lib/capybara/driver/node.rb +48 -14
  10. data/lib/capybara/dsl.rb +16 -9
  11. data/lib/capybara/helpers.rb +72 -81
  12. data/lib/capybara/minitest/spec.rb +267 -0
  13. data/lib/capybara/minitest.rb +385 -0
  14. data/lib/capybara/node/actions.rb +337 -89
  15. data/lib/capybara/node/base.rb +50 -32
  16. data/lib/capybara/node/document.rb +19 -3
  17. data/lib/capybara/node/document_matchers.rb +22 -24
  18. data/lib/capybara/node/element.rb +388 -125
  19. data/lib/capybara/node/finders.rb +231 -121
  20. data/lib/capybara/node/matchers.rb +503 -217
  21. data/lib/capybara/node/simple.rb +64 -27
  22. data/lib/capybara/queries/ancestor_query.rb +27 -0
  23. data/lib/capybara/queries/base_query.rb +87 -11
  24. data/lib/capybara/queries/current_path_query.rb +24 -24
  25. data/lib/capybara/queries/match_query.rb +15 -10
  26. data/lib/capybara/queries/selector_query.rb +675 -81
  27. data/lib/capybara/queries/sibling_query.rb +26 -0
  28. data/lib/capybara/queries/style_query.rb +45 -0
  29. data/lib/capybara/queries/text_query.rb +88 -20
  30. data/lib/capybara/queries/title_query.rb +9 -11
  31. data/lib/capybara/rack_test/browser.rb +63 -39
  32. data/lib/capybara/rack_test/css_handlers.rb +6 -4
  33. data/lib/capybara/rack_test/driver.rb +26 -16
  34. data/lib/capybara/rack_test/errors.rb +6 -0
  35. data/lib/capybara/rack_test/form.rb +73 -58
  36. data/lib/capybara/rack_test/node.rb +187 -67
  37. data/lib/capybara/rails.rb +4 -8
  38. data/lib/capybara/registration_container.rb +44 -0
  39. data/lib/capybara/registrations/drivers.rb +42 -0
  40. data/lib/capybara/registrations/patches/puma_ssl.rb +29 -0
  41. data/lib/capybara/registrations/servers.rb +45 -0
  42. data/lib/capybara/result.rb +142 -14
  43. data/lib/capybara/rspec/features.rb +17 -42
  44. data/lib/capybara/rspec/matcher_proxies.rb +82 -0
  45. data/lib/capybara/rspec/matchers/base.rb +111 -0
  46. data/lib/capybara/rspec/matchers/become_closed.rb +33 -0
  47. data/lib/capybara/rspec/matchers/compound.rb +88 -0
  48. data/lib/capybara/rspec/matchers/count_sugar.rb +37 -0
  49. data/lib/capybara/rspec/matchers/have_ancestor.rb +28 -0
  50. data/lib/capybara/rspec/matchers/have_current_path.rb +29 -0
  51. data/lib/capybara/rspec/matchers/have_selector.rb +77 -0
  52. data/lib/capybara/rspec/matchers/have_sibling.rb +27 -0
  53. data/lib/capybara/rspec/matchers/have_text.rb +33 -0
  54. data/lib/capybara/rspec/matchers/have_title.rb +29 -0
  55. data/lib/capybara/rspec/matchers/match_selector.rb +27 -0
  56. data/lib/capybara/rspec/matchers/match_style.rb +43 -0
  57. data/lib/capybara/rspec/matchers/spatial_sugar.rb +39 -0
  58. data/lib/capybara/rspec/matchers.rb +143 -244
  59. data/lib/capybara/rspec.rb +10 -12
  60. data/lib/capybara/selector/builders/css_builder.rb +84 -0
  61. data/lib/capybara/selector/builders/xpath_builder.rb +71 -0
  62. data/lib/capybara/selector/css.rb +102 -0
  63. data/lib/capybara/selector/definition/button.rb +63 -0
  64. data/lib/capybara/selector/definition/checkbox.rb +26 -0
  65. data/lib/capybara/selector/definition/css.rb +10 -0
  66. data/lib/capybara/selector/definition/datalist_input.rb +35 -0
  67. data/lib/capybara/selector/definition/datalist_option.rb +25 -0
  68. data/lib/capybara/selector/definition/element.rb +28 -0
  69. data/lib/capybara/selector/definition/field.rb +40 -0
  70. data/lib/capybara/selector/definition/fieldset.rb +14 -0
  71. data/lib/capybara/selector/definition/file_field.rb +13 -0
  72. data/lib/capybara/selector/definition/fillable_field.rb +33 -0
  73. data/lib/capybara/selector/definition/frame.rb +17 -0
  74. data/lib/capybara/selector/definition/id.rb +6 -0
  75. data/lib/capybara/selector/definition/label.rb +62 -0
  76. data/lib/capybara/selector/definition/link.rb +54 -0
  77. data/lib/capybara/selector/definition/link_or_button.rb +16 -0
  78. data/lib/capybara/selector/definition/option.rb +27 -0
  79. data/lib/capybara/selector/definition/radio_button.rb +27 -0
  80. data/lib/capybara/selector/definition/select.rb +81 -0
  81. data/lib/capybara/selector/definition/table.rb +109 -0
  82. data/lib/capybara/selector/definition/table_row.rb +21 -0
  83. data/lib/capybara/selector/definition/xpath.rb +5 -0
  84. data/lib/capybara/selector/definition.rb +278 -0
  85. data/lib/capybara/selector/filter.rb +3 -46
  86. data/lib/capybara/selector/filter_set.rb +124 -0
  87. data/lib/capybara/selector/filters/base.rb +77 -0
  88. data/lib/capybara/selector/filters/expression_filter.rb +22 -0
  89. data/lib/capybara/selector/filters/locator_filter.rb +29 -0
  90. data/lib/capybara/selector/filters/node_filter.rb +31 -0
  91. data/lib/capybara/selector/regexp_disassembler.rb +214 -0
  92. data/lib/capybara/selector/selector.rb +155 -0
  93. data/lib/capybara/selector/xpath_extensions.rb +17 -0
  94. data/lib/capybara/selector.rb +232 -369
  95. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  96. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  97. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  98. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  99. data/lib/capybara/selenium/driver.rb +380 -142
  100. data/lib/capybara/selenium/driver_specializations/chrome_driver.rb +117 -0
  101. data/lib/capybara/selenium/driver_specializations/edge_driver.rb +124 -0
  102. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +89 -0
  103. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +26 -0
  104. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +24 -0
  105. data/lib/capybara/selenium/extensions/file_input_click_emulation.rb +34 -0
  106. data/lib/capybara/selenium/extensions/find.rb +110 -0
  107. data/lib/capybara/selenium/extensions/html5_drag.rb +228 -0
  108. data/lib/capybara/selenium/extensions/modifier_keys_stack.rb +28 -0
  109. data/lib/capybara/selenium/extensions/scroll.rb +76 -0
  110. data/lib/capybara/selenium/logger_suppressor.rb +40 -0
  111. data/lib/capybara/selenium/node.rb +528 -97
  112. data/lib/capybara/selenium/nodes/chrome_node.rb +137 -0
  113. data/lib/capybara/selenium/nodes/edge_node.rb +104 -0
  114. data/lib/capybara/selenium/nodes/firefox_node.rb +136 -0
  115. data/lib/capybara/selenium/nodes/ie_node.rb +22 -0
  116. data/lib/capybara/selenium/nodes/safari_node.rb +118 -0
  117. data/lib/capybara/selenium/patches/action_pauser.rb +26 -0
  118. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  119. data/lib/capybara/selenium/patches/is_displayed.rb +16 -0
  120. data/lib/capybara/selenium/patches/logs.rb +45 -0
  121. data/lib/capybara/selenium/patches/pause_duration_fix.rb +9 -0
  122. data/lib/capybara/selenium/patches/persistent_client.rb +20 -0
  123. data/lib/capybara/server/animation_disabler.rb +63 -0
  124. data/lib/capybara/server/checker.rb +44 -0
  125. data/lib/capybara/server/middleware.rb +71 -0
  126. data/lib/capybara/server.rb +74 -71
  127. data/lib/capybara/session/config.rb +126 -0
  128. data/lib/capybara/session/matchers.rb +44 -27
  129. data/lib/capybara/session.rb +500 -297
  130. data/lib/capybara/spec/fixtures/no_extension +1 -0
  131. data/lib/capybara/spec/public/jquery.js +5 -5
  132. data/lib/capybara/spec/public/offset.js +6 -0
  133. data/lib/capybara/spec/public/test.js +168 -14
  134. data/lib/capybara/spec/session/accept_alert_spec.rb +37 -14
  135. data/lib/capybara/spec/session/accept_confirm_spec.rb +7 -6
  136. data/lib/capybara/spec/session/accept_prompt_spec.rb +38 -10
  137. data/lib/capybara/spec/session/all_spec.rb +179 -59
  138. data/lib/capybara/spec/session/ancestor_spec.rb +88 -0
  139. data/lib/capybara/spec/session/assert_all_of_selectors_spec.rb +140 -0
  140. data/lib/capybara/spec/session/assert_current_path_spec.rb +75 -0
  141. data/lib/capybara/spec/session/assert_selector_spec.rb +143 -0
  142. data/lib/capybara/spec/session/assert_style_spec.rb +26 -0
  143. data/lib/capybara/spec/session/assert_text_spec.rb +258 -0
  144. data/lib/capybara/spec/session/assert_title_spec.rb +93 -0
  145. data/lib/capybara/spec/session/attach_file_spec.rb +154 -48
  146. data/lib/capybara/spec/session/body_spec.rb +12 -13
  147. data/lib/capybara/spec/session/check_spec.rb +168 -41
  148. data/lib/capybara/spec/session/choose_spec.rb +75 -23
  149. data/lib/capybara/spec/session/click_button_spec.rb +243 -175
  150. data/lib/capybara/spec/session/click_link_or_button_spec.rb +57 -32
  151. data/lib/capybara/spec/session/click_link_spec.rb +100 -53
  152. data/lib/capybara/spec/session/current_scope_spec.rb +11 -10
  153. data/lib/capybara/spec/session/current_url_spec.rb +61 -35
  154. data/lib/capybara/spec/session/dismiss_confirm_spec.rb +7 -7
  155. data/lib/capybara/spec/session/dismiss_prompt_spec.rb +5 -4
  156. data/lib/capybara/spec/session/element/{assert_match_selector.rb → assert_match_selector_spec.rb} +13 -6
  157. data/lib/capybara/spec/session/element/match_css_spec.rb +21 -7
  158. data/lib/capybara/spec/session/element/match_xpath_spec.rb +9 -7
  159. data/lib/capybara/spec/session/element/matches_selector_spec.rb +91 -34
  160. data/lib/capybara/spec/session/evaluate_async_script_spec.rb +23 -0
  161. data/lib/capybara/spec/session/evaluate_script_spec.rb +45 -3
  162. data/lib/capybara/spec/session/execute_script_spec.rb +24 -4
  163. data/lib/capybara/spec/session/fill_in_spec.rb +166 -64
  164. data/lib/capybara/spec/session/find_button_spec.rb +37 -18
  165. data/lib/capybara/spec/session/find_by_id_spec.rb +10 -9
  166. data/lib/capybara/spec/session/find_field_spec.rb +57 -34
  167. data/lib/capybara/spec/session/find_link_spec.rb +47 -10
  168. data/lib/capybara/spec/session/find_spec.rb +290 -144
  169. data/lib/capybara/spec/session/first_spec.rb +91 -48
  170. data/lib/capybara/spec/session/frame/frame_title_spec.rb +23 -0
  171. data/lib/capybara/spec/session/frame/frame_url_spec.rb +23 -0
  172. data/lib/capybara/spec/session/frame/switch_to_frame_spec.rb +116 -0
  173. data/lib/capybara/spec/session/frame/within_frame_spec.rb +112 -0
  174. data/lib/capybara/spec/session/go_back_spec.rb +3 -2
  175. data/lib/capybara/spec/session/go_forward_spec.rb +3 -2
  176. data/lib/capybara/spec/session/has_all_selectors_spec.rb +69 -0
  177. data/lib/capybara/spec/session/has_ancestor_spec.rb +46 -0
  178. data/lib/capybara/spec/session/has_any_selectors_spec.rb +25 -0
  179. data/lib/capybara/spec/session/has_button_spec.rb +76 -19
  180. data/lib/capybara/spec/session/has_css_spec.rb +277 -131
  181. data/lib/capybara/spec/session/has_current_path_spec.rb +98 -26
  182. data/lib/capybara/spec/session/has_field_spec.rb +177 -107
  183. data/lib/capybara/spec/session/has_link_spec.rb +13 -12
  184. data/lib/capybara/spec/session/has_none_selectors_spec.rb +78 -0
  185. data/lib/capybara/spec/session/has_select_spec.rb +191 -95
  186. data/lib/capybara/spec/session/has_selector_spec.rb +128 -64
  187. data/lib/capybara/spec/session/has_sibling_spec.rb +50 -0
  188. data/lib/capybara/spec/session/has_table_spec.rb +172 -5
  189. data/lib/capybara/spec/session/has_text_spec.rb +126 -60
  190. data/lib/capybara/spec/session/has_title_spec.rb +35 -12
  191. data/lib/capybara/spec/session/has_xpath_spec.rb +74 -53
  192. data/lib/capybara/spec/session/{headers.rb → headers_spec.rb} +3 -2
  193. data/lib/capybara/spec/session/html_spec.rb +14 -6
  194. data/lib/capybara/spec/session/matches_style_spec.rb +35 -0
  195. data/lib/capybara/spec/session/node_spec.rb +1028 -131
  196. data/lib/capybara/spec/session/node_wrapper_spec.rb +39 -0
  197. data/lib/capybara/spec/session/refresh_spec.rb +34 -0
  198. data/lib/capybara/spec/session/reset_session_spec.rb +75 -34
  199. data/lib/capybara/spec/session/{response_code.rb → response_code_spec.rb} +2 -1
  200. data/lib/capybara/spec/session/save_and_open_page_spec.rb +3 -2
  201. data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +11 -15
  202. data/lib/capybara/spec/session/save_page_spec.rb +42 -55
  203. data/lib/capybara/spec/session/save_screenshot_spec.rb +16 -14
  204. data/lib/capybara/spec/session/screenshot_spec.rb +2 -2
  205. data/lib/capybara/spec/session/scroll_spec.rb +117 -0
  206. data/lib/capybara/spec/session/select_spec.rb +112 -85
  207. data/lib/capybara/spec/session/selectors_spec.rb +71 -8
  208. data/lib/capybara/spec/session/sibling_spec.rb +52 -0
  209. data/lib/capybara/spec/session/text_spec.rb +38 -23
  210. data/lib/capybara/spec/session/title_spec.rb +17 -5
  211. data/lib/capybara/spec/session/uncheck_spec.rb +71 -12
  212. data/lib/capybara/spec/session/unselect_spec.rb +44 -43
  213. data/lib/capybara/spec/session/visit_spec.rb +99 -32
  214. data/lib/capybara/spec/session/window/become_closed_spec.rb +33 -29
  215. data/lib/capybara/spec/session/window/current_window_spec.rb +5 -3
  216. data/lib/capybara/spec/session/window/open_new_window_spec.rb +5 -3
  217. data/lib/capybara/spec/session/window/switch_to_window_spec.rb +39 -30
  218. data/lib/capybara/spec/session/window/window_opened_by_spec.rb +17 -10
  219. data/lib/capybara/spec/session/window/window_spec.rb +121 -73
  220. data/lib/capybara/spec/session/window/windows_spec.rb +12 -10
  221. data/lib/capybara/spec/session/window/within_window_spec.rb +52 -82
  222. data/lib/capybara/spec/session/within_spec.rb +76 -43
  223. data/lib/capybara/spec/spec_helper.rb +67 -33
  224. data/lib/capybara/spec/test_app.rb +85 -36
  225. data/lib/capybara/spec/views/animated.erb +49 -0
  226. data/lib/capybara/spec/views/buttons.erb +1 -1
  227. data/lib/capybara/spec/views/fieldsets.erb +1 -1
  228. data/lib/capybara/spec/views/form.erb +227 -20
  229. data/lib/capybara/spec/views/frame_child.erb +10 -2
  230. data/lib/capybara/spec/views/frame_one.erb +2 -1
  231. data/lib/capybara/spec/views/frame_parent.erb +2 -2
  232. data/lib/capybara/spec/views/frame_two.erb +1 -1
  233. data/lib/capybara/spec/views/header_links.erb +1 -1
  234. data/lib/capybara/spec/views/host_links.erb +1 -1
  235. data/lib/capybara/spec/views/initial_alert.erb +10 -0
  236. data/lib/capybara/spec/views/obscured.erb +47 -0
  237. data/lib/capybara/spec/views/offset.erb +32 -0
  238. data/lib/capybara/spec/views/path.erb +1 -1
  239. data/lib/capybara/spec/views/popup_one.erb +1 -1
  240. data/lib/capybara/spec/views/popup_two.erb +1 -1
  241. data/lib/capybara/spec/views/postback.erb +1 -1
  242. data/lib/capybara/spec/views/react.erb +45 -0
  243. data/lib/capybara/spec/views/scroll.erb +20 -0
  244. data/lib/capybara/spec/views/spatial.erb +31 -0
  245. data/lib/capybara/spec/views/tables.erb +69 -2
  246. data/lib/capybara/spec/views/with_animation.erb +82 -0
  247. data/lib/capybara/spec/views/with_base_tag.erb +1 -1
  248. data/lib/capybara/spec/views/with_count.erb +1 -1
  249. data/lib/capybara/spec/views/with_dragula.erb +24 -0
  250. data/lib/capybara/spec/views/with_fixed_header_footer.erb +17 -0
  251. data/lib/capybara/spec/views/with_hover.erb +7 -1
  252. data/lib/capybara/spec/views/with_hover1.erb +10 -0
  253. data/lib/capybara/spec/views/with_html.erb +100 -10
  254. data/lib/capybara/spec/views/with_html5_svg.erb +20 -0
  255. data/lib/capybara/spec/views/with_html_entities.erb +1 -1
  256. data/lib/capybara/spec/views/with_jquery_animation.erb +24 -0
  257. data/lib/capybara/spec/views/with_js.erb +49 -3
  258. data/lib/capybara/spec/views/with_jstree.erb +26 -0
  259. data/lib/capybara/spec/views/with_namespace.erb +20 -0
  260. data/lib/capybara/spec/views/with_scope.erb +1 -1
  261. data/lib/capybara/spec/views/with_scope_other.erb +6 -0
  262. data/lib/capybara/spec/views/with_simple_html.erb +1 -1
  263. data/lib/capybara/spec/views/with_sortable_js.erb +21 -0
  264. data/lib/capybara/spec/views/with_title.erb +1 -1
  265. data/lib/capybara/spec/views/with_unload_alert.erb +3 -1
  266. data/lib/capybara/spec/views/with_windows.erb +7 -1
  267. data/lib/capybara/spec/views/within_frames.erb +6 -3
  268. data/lib/capybara/version.rb +2 -1
  269. data/lib/capybara/window.rb +39 -21
  270. data/lib/capybara.rb +208 -186
  271. data/spec/basic_node_spec.rb +52 -39
  272. data/spec/capybara_spec.rb +72 -50
  273. data/spec/css_builder_spec.rb +101 -0
  274. data/spec/css_splitter_spec.rb +38 -0
  275. data/spec/dsl_spec.rb +81 -61
  276. data/spec/filter_set_spec.rb +46 -0
  277. data/spec/fixtures/capybara.csv +1 -0
  278. data/spec/fixtures/certificate.pem +25 -0
  279. data/spec/fixtures/key.pem +27 -0
  280. data/spec/fixtures/selenium_driver_rspec_failure.rb +7 -3
  281. data/spec/fixtures/selenium_driver_rspec_success.rb +7 -3
  282. data/spec/minitest_spec.rb +164 -0
  283. data/spec/minitest_spec_spec.rb +162 -0
  284. data/spec/per_session_config_spec.rb +68 -0
  285. data/spec/rack_test_spec.rb +189 -96
  286. data/spec/regexp_dissassembler_spec.rb +250 -0
  287. data/spec/result_spec.rb +143 -13
  288. data/spec/rspec/features_spec.rb +38 -32
  289. data/spec/rspec/scenarios_spec.rb +9 -7
  290. data/spec/rspec/shared_spec_matchers.rb +959 -0
  291. data/spec/rspec/views_spec.rb +9 -3
  292. data/spec/rspec_matchers_spec.rb +62 -0
  293. data/spec/rspec_spec.rb +127 -30
  294. data/spec/sauce_spec_chrome.rb +43 -0
  295. data/spec/selector_spec.rb +458 -37
  296. data/spec/selenium_spec_chrome.rb +196 -9
  297. data/spec/selenium_spec_chrome_remote.rb +100 -0
  298. data/spec/selenium_spec_edge.rb +47 -0
  299. data/spec/selenium_spec_firefox.rb +210 -0
  300. data/spec/selenium_spec_firefox_remote.rb +80 -0
  301. data/spec/selenium_spec_ie.rb +150 -0
  302. data/spec/selenium_spec_safari.rb +148 -0
  303. data/spec/server_spec.rb +200 -101
  304. data/spec/session_spec.rb +91 -0
  305. data/spec/shared_selenium_node.rb +83 -0
  306. data/spec/shared_selenium_session.rb +558 -0
  307. data/spec/spec_helper.rb +94 -2
  308. data/spec/xpath_builder_spec.rb +93 -0
  309. metadata +420 -60
  310. data/lib/capybara/query.rb +0 -7
  311. data/lib/capybara/spec/session/assert_current_path.rb +0 -60
  312. data/lib/capybara/spec/session/assert_selector.rb +0 -148
  313. data/lib/capybara/spec/session/assert_text.rb +0 -196
  314. data/lib/capybara/spec/session/assert_title.rb +0 -70
  315. data/lib/capybara/spec/session/source_spec.rb +0 -0
  316. data/lib/capybara/spec/session/within_frame_spec.rb +0 -53
  317. data/spec/rspec/matchers_spec.rb +0 -827
  318. data/spec/selenium_spec.rb +0 -151
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
- require 'capybara/dsl'
2
+
3
3
  require 'rspec/core'
4
+ require 'capybara/dsl'
4
5
  require 'capybara/rspec/matchers'
5
6
  require 'capybara/rspec/features'
7
+ require 'capybara/rspec/matcher_proxies'
6
8
 
7
9
  RSpec.configure do |config|
8
- config.include Capybara::DSL, :type => :feature
9
- config.include Capybara::RSpecMatchers, :type => :feature
10
- config.include Capybara::RSpecMatchers, :type => :view
11
-
12
- # A work-around to support accessing the current example that works in both
13
- # RSpec 2 and RSpec 3.
14
- fetch_current_example = RSpec.respond_to?(:current_example) ?
15
- proc { RSpec.current_example } : proc { |context| context.example }
10
+ config.include Capybara::DSL, type: :feature
11
+ config.include Capybara::RSpecMatchers, type: :feature
12
+ config.include Capybara::DSL, type: :system
13
+ config.include Capybara::RSpecMatchers, type: :system
14
+ config.include Capybara::RSpecMatchers, type: :view
16
15
 
17
16
  # The before and after blocks must run instantaneously, because Capybara
18
17
  # might not actually be used in all examples where it's included.
@@ -22,12 +21,11 @@ RSpec.configure do |config|
22
21
  Capybara.use_default_driver
23
22
  end
24
23
  end
25
- config.before do
24
+
25
+ config.before do |example|
26
26
  if self.class.include?(Capybara::DSL)
27
- example = fetch_current_example.call(self)
28
27
  Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
29
28
  Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
30
29
  end
31
30
  end
32
31
  end
33
-
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xpath'
4
+
5
+ module Capybara
6
+ class Selector
7
+ # @api private
8
+ class CSSBuilder
9
+ def initialize(expression)
10
+ @expression = expression || ''
11
+ end
12
+
13
+ attr_reader :expression
14
+
15
+ def add_attribute_conditions(**attributes)
16
+ @expression = attributes.inject(expression) do |css, (name, value)|
17
+ conditions = if name == :class
18
+ class_conditions(value)
19
+ elsif value.is_a? Regexp
20
+ regexp_conditions(name, value)
21
+ else
22
+ [attribute_conditions(name => value)]
23
+ end
24
+
25
+ ::Capybara::Selector::CSS.split(css).map do |sel|
26
+ next sel if conditions.empty?
27
+
28
+ conditions.map { |cond| sel + cond }.join(', ')
29
+ end.join(', ')
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def regexp_conditions(name, value)
36
+ Selector::RegexpDisassembler.new(value).alternated_substrings.map do |strs|
37
+ strs.map do |str|
38
+ "[#{name}*='#{str}'#{' i' if value.casefold?}]"
39
+ end.join
40
+ end
41
+ end
42
+
43
+ def attribute_conditions(attributes)
44
+ attributes.map do |attribute, value|
45
+ case value
46
+ when XPath::Expression
47
+ raise ArgumentError, "XPath expressions are not supported for the :#{attribute} filter with CSS based selectors"
48
+ when Regexp
49
+ Selector::RegexpDisassembler.new(value).substrings.map do |str|
50
+ "[#{attribute}*='#{str}'#{' i' if value.casefold?}]"
51
+ end.join
52
+ when true
53
+ "[#{attribute}]"
54
+ when false
55
+ ':not([attribute])'
56
+ else
57
+ if attribute == :id
58
+ "##{::Capybara::Selector::CSS.escape(value)}"
59
+ else
60
+ "[#{attribute}='#{value}']"
61
+ end
62
+ end
63
+ end.join
64
+ end
65
+
66
+ def class_conditions(classes)
67
+ case classes
68
+ when XPath::Expression
69
+ raise ArgumentError, 'XPath expressions are not supported for the :class filter with CSS based selectors'
70
+ when Regexp
71
+ Selector::RegexpDisassembler.new(classes).alternated_substrings.map do |strs|
72
+ strs.map do |str|
73
+ "[class*='#{str}'#{' i' if classes.casefold?}]"
74
+ end.join
75
+ end
76
+ else
77
+ cls = Array(classes).reject { |c| c.is_a? Regexp }.group_by { |cl| cl.match?(/^!(?!!!)/) }
78
+ [(cls[false].to_a.map { |cl| ".#{Capybara::Selector::CSS.escape(cl.sub(/^!!/, ''))}" } +
79
+ cls[true].to_a.map { |cl| ":not(.#{Capybara::Selector::CSS.escape(cl.slice(1..-1))})" }).join]
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xpath'
4
+
5
+ module Capybara
6
+ class Selector
7
+ # @api private
8
+ class XPathBuilder
9
+ def initialize(expression)
10
+ @expression = expression || ''
11
+ end
12
+
13
+ attr_reader :expression
14
+
15
+ def add_attribute_conditions(**conditions)
16
+ @expression = conditions.inject(expression) do |xp, (name, value)|
17
+ conditions = name == :class ? class_conditions(value) : attribute_conditions(name => value)
18
+ return xp if conditions.nil?
19
+
20
+ if xp.is_a? XPath::Expression
21
+ xp[conditions]
22
+ else
23
+ "(#{xp})[#{conditions}]"
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def attribute_conditions(attributes)
31
+ attributes.map do |attribute, value|
32
+ case value
33
+ when XPath::Expression
34
+ XPath.attr(attribute)[value]
35
+ when Regexp
36
+ XPath.attr(attribute)[regexp_to_xpath_conditions(value)]
37
+ when true
38
+ XPath.attr(attribute)
39
+ when false, nil
40
+ !XPath.attr(attribute)
41
+ else
42
+ XPath.attr(attribute) == value.to_s
43
+ end
44
+ end.reduce(:&)
45
+ end
46
+
47
+ def class_conditions(classes)
48
+ case classes
49
+ when XPath::Expression, Regexp
50
+ attribute_conditions(class: classes)
51
+ else
52
+ Array(classes).reject { |c| c.is_a? Regexp }.map do |klass|
53
+ if klass.match?(/^!(?!!!)/)
54
+ !XPath.attr(:class).contains_word(klass.slice(1..-1))
55
+ else
56
+ XPath.attr(:class).contains_word(klass.sub(/^!!/, ''))
57
+ end
58
+ end.reduce(:&)
59
+ end
60
+ end
61
+
62
+ def regexp_to_xpath_conditions(regexp)
63
+ condition = XPath.current
64
+ condition = condition.uppercase if regexp.casefold?
65
+ Selector::RegexpDisassembler.new(regexp).alternated_substrings.map do |strs|
66
+ strs.map { |str| condition.contains(str) }.reduce(:&)
67
+ end.reduce(:|)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'capybara/selector/selector'
4
+
5
+ module Capybara
6
+ class Selector
7
+ class CSS
8
+ def self.escape(str)
9
+ value = str.dup
10
+ out = +''
11
+ out << value.slice!(0...1) if value.match?(/^[-_]/)
12
+ out << (value[0].match?(NMSTART) ? value.slice!(0...1) : escape_char(value.slice!(0...1)))
13
+ out << value.gsub(/[^a-zA-Z0-9_-]/) { |char| escape_char char }
14
+ out
15
+ end
16
+
17
+ def self.escape_char(char)
18
+ char.match?(%r{[ -/:-~]}) ? "\\#{char}" : format('\\%06<hex>x', hex: char.ord)
19
+ end
20
+
21
+ def self.split(css)
22
+ Splitter.new.split(css)
23
+ end
24
+
25
+ S = '\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
26
+ H = /[0-9a-fA-F]/.freeze
27
+ UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/.freeze
28
+ NONASCII = /[#{S}]/.freeze
29
+ ESCAPE = /#{UNICODE}|\\[ -~#{S}]/.freeze
30
+ NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/.freeze
31
+
32
+ class Splitter
33
+ def split(css)
34
+ selectors = []
35
+ StringIO.open(css.to_s) do |str|
36
+ selector = +''
37
+ while (char = str.getc)
38
+ case char
39
+ when '['
40
+ selector << parse_square(str)
41
+ when '('
42
+ selector << parse_paren(str)
43
+ when '"', "'"
44
+ selector << parse_string(char, str)
45
+ when '\\'
46
+ selector << char + str.getc
47
+ when ','
48
+ selectors << selector.strip
49
+ selector.clear
50
+ else
51
+ selector << char
52
+ end
53
+ end
54
+ selectors << selector.strip
55
+ end
56
+ selectors
57
+ end
58
+
59
+ private
60
+
61
+ def parse_square(strio)
62
+ parse_block('[', ']', strio)
63
+ end
64
+
65
+ def parse_paren(strio)
66
+ parse_block('(', ')', strio)
67
+ end
68
+
69
+ def parse_block(start, final, strio)
70
+ block = start
71
+ while (char = strio.getc)
72
+ case char
73
+ when final
74
+ return block + char
75
+ when '\\'
76
+ block += char + strio.getc
77
+ when '"', "'"
78
+ block += parse_string(char, strio)
79
+ else
80
+ block += char
81
+ end
82
+ end
83
+ raise ArgumentError, "Invalid CSS Selector - Block end '#{final}' not found"
84
+ end
85
+
86
+ def parse_string(quote, strio)
87
+ string = quote
88
+ while (char = strio.getc)
89
+ string += char
90
+ case char
91
+ when quote
92
+ return string
93
+ when '\\'
94
+ string += strio.getc
95
+ end
96
+ end
97
+ raise ArgumentError, 'Invalid CSS Selector - string end not found'
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:button, locator_type: [String, Symbol]) do
4
+ xpath(:value, :title, :type, :name) do |locator, **options|
5
+ input_btn_xpath = XPath.descendant(:input)[XPath.attr(:type).one_of('submit', 'reset', 'image', 'button')]
6
+ btn_xpath = XPath.descendant(:button)
7
+ aria_btn_xpath = XPath.descendant[XPath.attr(:role).equals('button')]
8
+ image_btn_xpath = XPath.descendant(:input)[XPath.attr(:type) == 'image']
9
+
10
+ unless locator.nil?
11
+ locator = locator.to_s
12
+ locator_matchers = combine_locators(locator, config: self)
13
+ btn_matchers = locator_matchers |
14
+ XPath.string.n.is(locator) |
15
+ XPath.descendant(:img)[XPath.attr(:alt).is(locator)]
16
+
17
+ input_btn_xpath = input_btn_xpath[locator_matchers] + locate_label(locator).descendant(input_btn_xpath)
18
+ btn_xpath = btn_xpath[btn_matchers] + locate_label(locator).descendant(btn_xpath)
19
+ aria_btn_xpath = aria_btn_xpath[btn_matchers]
20
+
21
+ alt_matches = XPath.attr(:alt).is(locator)
22
+ alt_matches |= XPath.attr(:'aria-label').is(locator) if enable_aria_label
23
+ image_btn_xpath = image_btn_xpath[alt_matches] + locate_label(locator).descendant(image_btn_xpath)
24
+ end
25
+
26
+ btn_xpaths = [input_btn_xpath, btn_xpath, image_btn_xpath]
27
+ btn_xpaths << aria_btn_xpath if enable_aria_role
28
+
29
+ %i[value title type].inject(btn_xpaths.inject(&:union)) do |memo, ef|
30
+ memo.where(find_by_attr(ef, options[ef]))
31
+ end
32
+ end
33
+
34
+ node_filter(:disabled, :boolean, default: false, skip_if: :all) { |node, value| !(value ^ node.disabled?) }
35
+ expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
36
+
37
+ node_filter(:name) { |node, value| !value.is_a?(Regexp) || value.match?(node[:name]) }
38
+ expression_filter(:name) do |xpath, val|
39
+ builder(xpath).add_attribute_conditions(name: val)
40
+ end
41
+
42
+ describe_expression_filters do |disabled: nil, **options|
43
+ desc = +''
44
+ desc << ' that is not disabled' if disabled == false
45
+ desc << describe_all_expression_filters(**options)
46
+ end
47
+
48
+ describe_node_filters do |disabled: nil, **|
49
+ ' that is disabled' if disabled == true
50
+ end
51
+
52
+ def combine_locators(locator, config:)
53
+ [
54
+ XPath.attr(:id).equals(locator),
55
+ XPath.attr(:name).equals(locator),
56
+ XPath.attr(:value).is(locator),
57
+ XPath.attr(:title).is(locator),
58
+ (XPath.attr(:id) == XPath.anywhere(:label)[XPath.string.n.is(locator)].attr(:for)),
59
+ (XPath.attr(:'aria-label').is(locator) if config.enable_aria_label),
60
+ (XPath.attr(config.test_id) == locator if config.test_id)
61
+ ].compact.inject(&:|)
62
+ end
63
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:checkbox, locator_type: [String, Symbol]) do
4
+ xpath do |locator, allow_self: nil, **options|
5
+ xpath = XPath.axis(allow_self ? :"descendant-or-self" : :descendant, :input)[
6
+ XPath.attr(:type) == 'checkbox'
7
+ ]
8
+ locate_field(xpath, locator, **options)
9
+ end
10
+
11
+ filter_set(:_field, %i[checked unchecked disabled name])
12
+
13
+ node_filter(%i[option with]) do |node, value|
14
+ val = node.value
15
+ (value.is_a?(Regexp) ? value.match?(val) : val == value.to_s).tap do |res|
16
+ add_error("Expected value to be #{value.inspect} but it was #{val.inspect}") unless res
17
+ end
18
+ end
19
+
20
+ describe_node_filters do |option: nil, with: nil, **|
21
+ desc = +''
22
+ desc << " with value #{option.inspect}" if option
23
+ desc << " with value #{with.inspect}" if with
24
+ desc
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:css, locator_type: [String, Symbol], raw_locator: true) do
4
+ css do |css|
5
+ if css.is_a? Symbol
6
+ Capybara::Helpers.warn "DEPRECATED: Passing a symbol (#{css.inspect}) as the CSS locator is deprecated - please pass a string instead : #{Capybara::Helpers.filter_backtrace(caller)}"
7
+ end
8
+ css
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:datalist_input, locator_type: [String, Symbol]) do
4
+ label 'input box with datalist completion'
5
+
6
+ xpath do |locator, **options|
7
+ xpath = XPath.descendant(:input)[XPath.attr(:list)]
8
+ locate_field(xpath, locator, **options)
9
+ end
10
+
11
+ filter_set(:_field, %i[disabled name placeholder])
12
+
13
+ node_filter(:options) do |node, options|
14
+ actual = node.find("//datalist[@id=#{node[:list]}]", visible: :all).all(:datalist_option, wait: false).map(&:value)
15
+ (options.sort == actual.sort).tap do |res|
16
+ add_error("Expected #{options.inspect} options found #{actual.inspect}") unless res
17
+ end
18
+ end
19
+
20
+ expression_filter(:with_options) do |expr, options|
21
+ options.inject(expr) do |xpath, option|
22
+ xpath.where(XPath.attr(:list) == XPath.anywhere(:datalist)[expression_for(:datalist_option, option)].attr(:id))
23
+ end
24
+ end
25
+
26
+ describe_expression_filters do |with_options: nil, **|
27
+ desc = +''
28
+ desc << " with at least options #{with_options.inspect}" if with_options
29
+ desc
30
+ end
31
+
32
+ describe_node_filters do |options: nil, **|
33
+ " with options #{options.inspect}" if options
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ Capybara.add_selector(:datalist_option, locator_type: [String, Symbol]) do
4
+ label 'datalist option'
5
+ visible(:all)
6
+
7
+ xpath do |locator|
8
+ xpath = XPath.descendant(:option)
9
+ xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:value) == locator.to_s)] unless locator.nil?
10
+ xpath
11
+ end
12
+
13
+ node_filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
14
+ expression_filter(:disabled) { |xpath, val| val ? xpath : xpath[~XPath.attr(:disabled)] }
15
+
16
+ describe_expression_filters do |disabled: nil, **options|
17
+ desc = +''
18
+ desc << ' that is not disabled' if disabled == false
19
+ desc << describe_all_expression_filters(**options)
20
+ end
21
+
22
+ describe_node_filters do |**options|
23
+ ' that is disabled' if options[:disabled]
24
+ end
25
+ end