capybara 3.19.1 → 3.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +18 -0
  3. data/README.md +1 -1
  4. data/lib/capybara/driver/node.rb +4 -0
  5. data/lib/capybara/node/actions.rb +3 -2
  6. data/lib/capybara/node/element.rb +11 -0
  7. data/lib/capybara/node/finders.rb +4 -1
  8. data/lib/capybara/queries/selector_query.rb +19 -5
  9. data/lib/capybara/selector.rb +2 -2
  10. data/lib/capybara/selector/definition/label.rb +27 -10
  11. data/lib/capybara/selector/definition/link.rb +3 -2
  12. data/lib/capybara/selenium/atoms/getAttribute.min.js +1 -0
  13. data/lib/capybara/selenium/atoms/isDisplayed.min.js +1 -0
  14. data/lib/capybara/selenium/atoms/src/getAttribute.js +161 -0
  15. data/lib/capybara/selenium/atoms/src/isDisplayed.js +454 -0
  16. data/lib/capybara/selenium/driver.rb +14 -1
  17. data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +1 -1
  18. data/lib/capybara/selenium/driver_specializations/internet_explorer_driver.rb +1 -1
  19. data/lib/capybara/selenium/driver_specializations/safari_driver.rb +1 -1
  20. data/lib/capybara/selenium/node.rb +25 -1
  21. data/lib/capybara/selenium/nodes/safari_node.rb +19 -6
  22. data/lib/capybara/selenium/patches/atoms.rb +18 -0
  23. data/lib/capybara/spec/session/all_spec.rb +23 -0
  24. data/lib/capybara/spec/session/click_link_spec.rb +11 -0
  25. data/lib/capybara/spec/session/node_spec.rb +78 -5
  26. data/lib/capybara/spec/session/selectors_spec.rb +8 -0
  27. data/lib/capybara/spec/views/animated.erb +49 -0
  28. data/lib/capybara/spec/views/frame_one.erb +1 -0
  29. data/lib/capybara/spec/views/obscured.erb +9 -9
  30. data/lib/capybara/version.rb +1 -1
  31. data/spec/sauce_spec_chrome.rb +1 -0
  32. data/spec/selenium_spec_chrome.rb +4 -2
  33. data/spec/selenium_spec_chrome_remote.rb +4 -2
  34. data/spec/selenium_spec_edge.rb +4 -2
  35. data/spec/selenium_spec_firefox.rb +4 -11
  36. data/spec/selenium_spec_firefox_remote.rb +4 -2
  37. data/spec/selenium_spec_ie.rb +5 -6
  38. data/spec/selenium_spec_safari.rb +7 -12
  39. data/spec/server_spec.rb +4 -2
  40. data/spec/shared_selenium_node.rb +29 -0
  41. metadata +23 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 729c366399588a2a21c59d6b50a20a8a175130ad9c0d08dcea1b09c12cda560a
4
- data.tar.gz: 5754b7164ea9b3633294653efe0a26ef8afda9bfd290d6b582e056dcdb3f0647
3
+ metadata.gz: '087a3e5a863f14cabebd8344fa0e2f04cb5c48c386014e5fd46eab2e715a8040'
4
+ data.tar.gz: 2ea4bc5aec32f6a7bd63856d922264d29d9629f4c6af71867d30b963d2f04697
5
5
  SHA512:
6
- metadata.gz: daf2afa61fddc09e45ce86375f86d10d101b62e00a98ab327d2fc455f5e982b865eb8982138e66700b336952c79a3457eca9726e1a8148ad31636790852b246c
7
- data.tar.gz: c8924cfa0209e24aaa57e692d20bda06bbbfea7e5729a20462c2c325e7d81bb0f74f542cb8d8d1f8d250ca381b57715dc3937bd17c363209c11d257ba50e6035
6
+ metadata.gz: cbc11a7fe022eb230aa9e9342d26b88619cb3cb65931b89487ff448575e6cb794e7460679bd1c37c336bdd65229ca72b4e489b5fbe1a5e2ea7ff92945bb1a1ca
7
+ data.tar.gz: 0a3103be3113216c40efa1cb22cc261141ed940e205c6e9e9483233c5e672079da5b6950aa4f1bf6871524a6f803eb400ed735f48e13429f57eaa9e8376ae367
data/History.md CHANGED
@@ -1,3 +1,21 @@
1
+ # Version 3.20.0
2
+ Release date: 2019-05-14
3
+ ### Added
4
+
5
+ * `Node#obscured?` to check viewport presence and element overlap
6
+ * `:obscured` system filter to check whether elements are obscured in finders, assertions, and expectations
7
+ * :label selector :for option can be a regexp
8
+ * Significantly smaller `isDisplayed`/`getAttribute` atoms for selenium driver. If these produce issues you can disable their use
9
+ by setting an environment variable named 'DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS' (Please also report any issues).
10
+ * `href: false` option with `find_link`/`click_link`/:link selector ignores `href` presence/absence
11
+
12
+ ### Fixed
13
+
14
+ * Workaround Safari issue with send_keys not correctly using top level modifiers
15
+ * Workaround Safari not retrying click due to incorrect error type
16
+ * Fix Safari attach_file block mode when clicking elements associated to the file input
17
+ * Workaround Safari issue with repeated hover
18
+
1
19
  # Version 3.19.1
2
20
  Release date: 2019-05-11
3
21
 
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8
8
  [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=capybara&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
9
9
 
10
- **Note** You are viewing the README for the 3.19.x version of Capybara.
10
+ **Note** You are viewing the README for the 3.20.x version of Capybara.
11
11
 
12
12
 
13
13
  Capybara helps you test web applications by simulating how a real user would
@@ -85,6 +85,10 @@ module Capybara
85
85
  raise NotImplementedError
86
86
  end
87
87
 
88
+ def obscured?
89
+ raise NotImplementedError
90
+ end
91
+
88
92
  def checked?
89
93
  raise NotImplementedError
90
94
  end
@@ -191,7 +191,7 @@ module Capybara
191
191
  # @macro waiting_behavior
192
192
  #
193
193
  # @param value [String] Which option to select
194
- # @param from [String] The id, Capybara.test_id atrtribute, name or label of the select box
194
+ # @param from [String] The id, Capybara.test_id attribute, name or label of the select box
195
195
  #
196
196
  # @return [Capybara::Node::Element] The option element selected
197
197
  def select(value = nil, from: nil, **options)
@@ -393,9 +393,10 @@ module Capybara
393
393
  JS
394
394
 
395
395
  CAPTURE_FILE_ELEMENT_SCRIPT = <<~'JS'
396
- document.addEventListener('click', function(e){
396
+ document.addEventListener('click', function file_catcher(e){
397
397
  if (e.target.matches("input[type='file']")) {
398
398
  window._capybara_clicked_file_input = e.target;
399
+ this.removeEventListener('click', file_catcher);
399
400
  e.preventDefault();
400
401
  }
401
402
  })
@@ -290,6 +290,17 @@ module Capybara
290
290
  synchronize { base.visible? }
291
291
  end
292
292
 
293
+ ##
294
+ #
295
+ # Whether or not the element is currently in the viewport and it (or descendants)
296
+ # would be considered clickable at the elements center point.
297
+ #
298
+ # @return [Boolean] Whether the elements center is obscured.
299
+ #
300
+ def obscured?
301
+ synchronize { base.obscured? }
302
+ end
303
+
293
304
  ##
294
305
  #
295
306
  # Whether or not the element is checked.
@@ -31,6 +31,9 @@ module Capybara
31
31
  # * :all - same as false; finds visible and invisible elements.
32
32
  # * :hidden - only finds invisible elements.
33
33
  # * :visible - same as true; only finds visible elements.
34
+ # @option options [Boolean] obscured Only find elements with the specified obscured state:
35
+ # * true - only find elements whose centerpoint is not in the viewport or is obscured by another non-descendant element.
36
+ # * false - only find elements whose centerpoint is in the viewport and is not obscured by other non-descendant elements.
34
37
  # @option options [String, Regexp] id Only find elements with an id that matches the value passed
35
38
  # @option options [String, Array<String>, Regexp] class Only find elements with matching class/classes.
36
39
  # + Absence of a class can be checked by prefixing the class name with !
@@ -139,7 +142,7 @@ module Capybara
139
142
  #
140
143
  # @macro waiting_behavior
141
144
  #
142
- # @option options [String,Regexp,nil] href Value to match against the links href, if nil finds link placeholders (<a> elements with no href attribute)
145
+ # @option options [String,Regexp,nil] href Value to match against the links href, if nil finds link placeholders (<a> elements with no href attribute), if false ignores the href
143
146
  # @option options [String, Regexp] id Match links with the id provided
144
147
  # @option options [String] title Match links with the title provided
145
148
  # @option options [String] alt Match links with a contained img element whose alt matches
@@ -4,7 +4,8 @@ module Capybara
4
4
  module Queries
5
5
  class SelectorQuery < Queries::BaseQuery
6
6
  attr_reader :expression, :selector, :locator, :options
7
- VALID_KEYS = COUNT_KEYS + %i[text id class style visible exact exact_text normalize_ws match wait filter_set]
7
+ VALID_KEYS = COUNT_KEYS +
8
+ %i[text id class style visible obscured exact exact_text normalize_ws match wait filter_set]
8
9
  VALID_MATCH = %i[first smart prefer_exact one].freeze
9
10
 
10
11
  def initialize(*args,
@@ -386,7 +387,7 @@ module Capybara
386
387
  def matches_system_filters?(node)
387
388
  applied_filters << :system
388
389
 
389
- matches_visible_filter?(node) &&
390
+ matches_visibility_filters?(node) &&
390
391
  matches_id_filter?(node) &&
391
392
  matches_class_filter?(node) &&
392
393
  matches_style_filter?(node) &&
@@ -443,14 +444,27 @@ module Capybara
443
444
  matches_text_exactly?(node, exact_text)
444
445
  end
445
446
 
446
- def matches_visible_filter?(node)
447
- case visible
447
+ def matches_visibility_filters?(node)
448
+ obscured = options[:obscured]
449
+ return (visible != :hidden) && (node.initial_cache[:visible] != false) && !node.obscured? if obscured == false
450
+
451
+ vis = case visible
448
452
  when :visible then
449
453
  node.initial_cache[:visible] || (node.initial_cache[:visible].nil? && node.visible?)
450
454
  when :hidden then
451
455
  (node.initial_cache[:visible] == false) || (node.initial_cache[:visbile].nil? && !node.visible?)
452
- else true
456
+ else
457
+ true
453
458
  end
459
+
460
+ vis && case obscured
461
+ when true
462
+ node.obscured?
463
+ when false
464
+ !node.obscured?
465
+ else
466
+ true
467
+ end
454
468
  end
455
469
 
456
470
  def matches_text_exactly?(node, value)
@@ -48,7 +48,7 @@ require 'capybara/selector/definition'
48
48
  # * :title (String) — Matches the title attribute
49
49
  # * :alt (String) — Matches the alt attribute of a contained img element
50
50
  # * :class (String, Array<String>, Regexp, XPath::Expression) — Matches the class(es) provided
51
- # * :href (String, Regexp, nil) — Matches the normalized href of the link, if nil will find <a> elements with no href attribute
51
+ # * :href (String, Regexp, nil, false) — Matches the normalized href of the link, if nil will find <a> elements with no href attribute, if false ignores href
52
52
  # * :style (String, Regexp, Hash)
53
53
  #
54
54
  # * **:button** - Find buttons ( input [of type submit, reset, image, button] or button elements )
@@ -146,7 +146,7 @@ require 'capybara/selector/definition'
146
146
  # * **:label** - Find label elements
147
147
  # * Locator: Match id or text contents
148
148
  # * Filters:
149
- # * :for (Element, String) — The element or id of the element associated with the label
149
+ # * :for (Element, String, Regexp) — The element or id of the element associated with the label
150
150
  #
151
151
  # * **:table** - Find table elements
152
152
  # * Locator: id or caption text of table
@@ -11,10 +11,9 @@ Capybara.add_selector(:label, locator_type: [String, Symbol]) do
11
11
  end
12
12
  if options.key?(:for)
13
13
  if (for_option = options[:for].is_a?(Capybara::Node::Element) ? options[:for][:id] : options[:for])
14
- with_attr = XPath.attr(:for) == for_option.to_s
15
- labelable_elements = %i[button input keygen meter output progress select textarea]
14
+ with_attr = builder(XPath.self).add_attribute_conditions(for: for_option)
16
15
  wrapped = !XPath.attr(:for) &
17
- XPath.descendant(*labelable_elements)[XPath.attr(:id) == for_option.to_s]
16
+ builder(XPath.self.descendant(*labelable_elements)).add_attribute_conditions(id: for_option)
18
17
  xpath = xpath[with_attr | wrapped]
19
18
  end
20
19
  end
@@ -22,22 +21,40 @@ Capybara.add_selector(:label, locator_type: [String, Symbol]) do
22
21
  end
23
22
 
24
23
  node_filter(:for) do |node, field_or_value|
25
- # Non element values were handled through the expression filter
26
- next true unless field_or_value.is_a? Capybara::Node::Element
27
-
28
- if (for_val = node[:for])
29
- field_or_value[:id] == for_val
24
+ case field_or_value
25
+ when Capybara::Node::Element
26
+ if (for_val = node[:for])
27
+ field_or_value[:id] == for_val
28
+ else
29
+ field_or_value.find_xpath('./ancestor::label[1]').include? node.base
30
+ end
31
+ when Regexp
32
+ if (for_val = node[:for])
33
+ field_or_value.match? for_val
34
+ else
35
+ node.find_xpath(XPath.descendant(*labelable_elements).to_s)
36
+ .any? { |n| field_or_value.match? n[:id] }
37
+ end
30
38
  else
31
- field_or_value.find_xpath('./ancestor::label[1]').include? node.base
39
+ # Non element/regexp values were handled through the expression filter
40
+ true
32
41
  end
33
42
  end
34
43
 
35
44
  describe_expression_filters do |**options|
36
45
  next unless options.key?(:for) && !options[:for].is_a?(Capybara::Node::Element)
37
46
 
38
- " for element with id of \"#{options[:for]}\""
47
+ if options[:for].is_a? Regexp
48
+ " for element with id matching #{options[:for].inspect}"
49
+ else
50
+ " for element with id of \"#{options[:for]}\""
51
+ end
39
52
  end
40
53
  describe_node_filters do |**options|
41
54
  " for element #{options[:for]}" if options[:for]&.is_a?(Capybara::Node::Element)
42
55
  end
56
+
57
+ def labelable_elements
58
+ %i[button input keygen meter output progress select textarea]
59
+ end
43
60
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  Capybara.add_selector(:link, locator_type: [String, Symbol]) do
4
4
  xpath do |locator, href: true, alt: nil, title: nil, **|
5
- xpath = builder(XPath.descendant(:a)).add_attribute_conditions(href: href)
5
+ xpath = XPath.descendant(:a)
6
+ xpath = builder(xpath).add_attribute_conditions(href: href) unless href == false
6
7
 
7
8
  unless locator.nil?
8
9
  locator = locator.to_s
@@ -35,7 +36,7 @@ Capybara.add_selector(:link, locator_type: [String, Symbol]) do
35
36
  desc = +''
36
37
  if (href = options[:href])
37
38
  desc << " with href #{'matching ' if href.is_a? Regexp}#{href.inspect}"
38
- elsif options.key?(:href) # is nil/false specified?
39
+ elsif options.key?(:href) && href != false # is nil specified?
39
40
  desc << ' with no href attribute'
40
41
  end
41
42
  desc << " with download attribute#{" #{download}" if download.is_a? String}" if download
@@ -0,0 +1 @@
1
+ (function(){function u(e){var t=e.tagName.toUpperCase();if("OPTION"==t)return!0;if("INPUT"!=t)return!1;var r=e.type.toLowerCase();return"checkbox"==r||"radio"==r}function s(e){var t="selected",r=e.type&&e.type.toLowerCase();return"checkbox"!=r&&"radio"!=r||(t="checked"),!!e[t]}function c(e,t){var r=e.getAttributeNode(t);return r&&r.specified?r.value:null}var i=["allowfullscreen","allowpaymentrequest","allowusermedia","async","autofocus","autoplay","checked","compact","complete","controls","declare","default","defaultchecked","defaultselected","defer","disabled","ended","formnovalidate","hidden","indeterminate","iscontenteditable","ismap","itemscope","loop","multiple","muted","nohref","nomodule","noresize","noshade","novalidate","nowrap","open","paused","playsinline","pubdate","readonly","required","reversed","scoped","seamless","seeking","selected","truespeed","typemustmatch","willvalidate"],d={"class":"className",readonly:"readOnly"};return function f(e,t){var r=null,a=t.toLowerCase();if("style"==a)return(r=e.style)&&"string"!=typeof r&&(r=r.cssText),r;if(("selected"==a||"checked"==a)&&u(e))return s(e)?"true":null;if(tagName=e.tagName.toUpperCase(),"IMG"==tagName&&"src"==a||"A"==tagName&&"href"==a)return(r=c(e,a))&&(r=e[a]),r;if("spellcheck"==a){if(null===!(r=c(e,a))){if("false"==r.toLowerCase())return"false";if("true"==r.toLowerCase())return"true"}return e[a]+""}var l,n=d[t]||t;if(i.some(function(e){e==a}))return(r=!(null===(r=c(e,a)))||e[n])?"true":null;try{l=e[n]}catch(o){}return null!=(r=null==l||"object"==typeof l||"function"==typeof l?c(e,t):l)?r.toString():null}})()
@@ -0,0 +1 @@
1
+ (function(){function f(t,e,n){function r(t){var e=x(t);if(0<e.height&&0<e.width)return!0;if("PATH"==t.tagName.toUpperCase()&&(0<e.height||0<e.width)){var n=window.getComputedStyle(t)["stroke-width"];return!!n&&0<parseInt(n,10)}return"hidden"!=window.getComputedStyle(t).overflow&&Array.prototype.slice.call(t.childNodes).some(function(t){return t.nodeType==Node.TEXT_NODE||t.nodeType==Node.ELEMENT_NODE&&r(t)})}function i(t){return C(t)==T.HIDDEN&&Array.prototype.slice.call(t.childNodes).every(function(t){return t.nodeType!=Node.ELEMENT_NODE||i(t)||!r(t)})}var o=t.tagName.toUpperCase();if("BODY"==o)return!0;var a=D(t);if(a&&a.tagName&&"DETAILS"==a.tagName.toUpperCase()&&!a.open&&"SUMMARY"!=o)return!1;if("OPTION"==o||"OPTGROUP"==o){var u=v(t,function(t){return"SELECT"==t.tagName.toUpperCase()});return!!u&&f(u,!0,n)}var l=c(t);if(l)return!!l.image&&0<l.rect.width&&0<l.rect.height&&f(l.image,e,n);if("INPUT"==o&&"hidden"==t.type.toLowerCase())return!1;if("NOSCRIPT"==o)return!1;var d=window.getComputedStyle(t).visibility;return"collapse"!=d&&"hidden"!=d&&(!!n(t)&&(!(!e&&0==h(t))&&(!!r(t)&&!i(t))))}function E(t){var e=x(t);return{left:e.left,right:e.left+e.width,top:e.top,bottom:e.top+e.height}}function D(t){return t.parentElement}function C(t){function e(t){function e(t){if(t==u)return!0;var e=window.getComputedStyle(t),n=e.display;return 0!=n.indexOf("inline")&&"contents"!=n&&("absolute"!=r||"static"!=e.position)}var r=window.getComputedStyle(t).position;if("fixed"==r)return i=!0,t==u?null:u;for(var n=D(t);n&&!e(n);)n=D(n);return n}function n(t){var e=t;if("visible"==d)if(t==u&&l)e=l;else if(t==l)return{x:"visible",y:"visible"};var n=window.getComputedStyle(e),r={x:n["overflow-x"],y:n["overflow-y"]};return t==u&&(r.x="visible"==r.x?"auto":r.x,r.y="visible"==r.y?"auto":r.y),r}function r(t){return t==u?{x:window.scrollX,y:window.scrollY}:{x:t.scrollLeft,y:t.scrollTop}}for(var i,o=E(t),a=t.ownerDocument,u=a.documentElement,l=a.body,d=window.getComputedStyle(u).overflow,f=e(t);f;f=e(f)){var h=n(f);if("visible"!=h.x||"visible"!=h.y){var s=x(f);if(0==s.width||0==s.height)return T.HIDDEN;var p=o.right<s.left,c=o.bottom<s.top;if(p&&"hidden"==h.x||c&&"hidden"==h.y)return T.HIDDEN;if(p&&"visible"!=h.x||c&&"visible"!=h.y){var v=r(f),g=o.right<s.left-v.x,w=o.bottom<s.top-v.y;return g&&"visible"!=h.x||w&&"visible"!=h.x?T.HIDDEN:C(f)==T.HIDDEN?T.HIDDEN:T.SCROLL}var N=o.left>=s.left+s.width,m=o.top>=s.top+s.height;if(N&&"hidden"==h.x||m&&"hidden"==h.y)return T.HIDDEN;if(N&&"visible"!=h.x||m&&"visible"!=h.y){if(i){var y=r(f);if(o.left>=u.scrollWidth-y.x||o.right>=u.scrollHeight-y.y)return T.HIDDEN}return C(f)==T.HIDDEN?T.HIDDEN:T.SCROLL}}}return T.NONE}function o(t){var e=t.document.documentElement;return{width:e.clientWidth,height:e.clientHeight}}function p(t,e,n,r){return{left:t,top:e,width:n,height:r}}function x(t){var e,n=c(t);if(n)return n.rect;if("HTML"==t.tagName.toUpperCase()){t.ownerDocument;var r=o(window);return p(0,0,r.width,r.height)}try{e=t.getBoundingClientRect()}catch(i){return p(0,0,0,0)}return p(e.left,e.top,e.right-e.left,e.bottom-e.top)}function h(t){var e=1,n=window.getComputedStyle(t).opacity;n&&(e=Number(n));var r=D(t);return r&&r.nodeType==Node.ELEMENT_NODE&&(e*=h(r)),e}function s(t){var e=t.shape.toLowerCase(),n=t.coords.split(",");if("rect"==e&&4==n.length){var r=n[0],i=n[1];return p(r,i,n[2]-r,n[3]-i)}if("circle"==e&&3==n.length){var o=n[0],a=n[1],u=n[2];return p(o-u,a-u,2*u,2*u)}if("poly"==e&&2<n.length){for(var l=n[0],d=n[1],f=l,h=d,s=2;s+1<n.length;s+=2)l=Math.min(l,n[s]),f=Math.max(f,n[s]),d=Math.min(d,n[s+1]),h=Math.max(h,n[s+1]);return p(l,d,f-l,h-d)}return p(0,0,0,0)}function c(t){var e=t.tagName.toUpperCase(),n="MAP"==e;if(!n&&"AREA"!=e)return null;var r=n?t:"MAP"==D(t).tagName.toUpperCase()?D(t):null,i=null,o=null;if(r&&r.name&&((i=r.ownerDocument.querySelector("*[usemap='#"+r.name+"']"))&&(o=x(i),!n&&"default"!=t.shape.toLowerCase()))){var a=s(t),u=Math.min(Math.max(a.left,0),o.width),l=Math.min(Math.max(a.top,0),o.height),d=Math.min(a.width,o.width-u),f=Math.min(a.height,o.height-l);o=p(u+o.left,l+o.top,d,f)}return{image:i,rect:o||p(0,0,0,0)}}function v(t,e){for(t&&(t=D(t));t;){if(e(t))return t;t=D(t)}return null}function r(t){var e=t.parentNode;if(e&&e.shadowRoot&&t.assignedSlot!==undefined)return t.assignedSlot?t.assignedSlot.parentNode:null;if(t.getDestinationInsertionPoints){var n=t.getDestinationInsertionPoints();if(0<n.length)return n[n.length-1]}return e}var T={NONE:"none",HIDDEN:"hidden",SCROLL:"scroll"};return function i(t,e){function n(t){if("none"==window.getComputedStyle(t).display)return!1;var e=r(t);if("function"==typeof ShadowRoot&&e instanceof ShadowRoot){if(e.host.shadowRoot!==e)return!1;e=e.host}return!(!e||e.nodeType!=Node.DOCUMENT_NODE&&e.nodeType!=Node.DOCUMENT_FRAGMENT_NODE)||e&&n(e)}return f(t,!!e,n)}})()
@@ -0,0 +1,161 @@
1
+ (function(){
2
+ var BOOLEAN_PROPERTIES = [
3
+ "allowfullscreen",
4
+ "allowpaymentrequest",
5
+ "allowusermedia",
6
+ "async",
7
+ "autofocus",
8
+ "autoplay",
9
+ "checked",
10
+ "compact",
11
+ "complete",
12
+ "controls",
13
+ "declare",
14
+ "default",
15
+ "defaultchecked",
16
+ "defaultselected",
17
+ "defer",
18
+ "disabled",
19
+ "ended",
20
+ "formnovalidate",
21
+ "hidden",
22
+ "indeterminate",
23
+ "iscontenteditable",
24
+ "ismap",
25
+ "itemscope",
26
+ "loop",
27
+ "multiple",
28
+ "muted",
29
+ "nohref",
30
+ "nomodule",
31
+ "noresize",
32
+ "noshade",
33
+ "novalidate",
34
+ "nowrap",
35
+ "open",
36
+ "paused",
37
+ "playsinline",
38
+ "pubdate",
39
+ "readonly",
40
+ "required",
41
+ "reversed",
42
+ "scoped",
43
+ "seamless",
44
+ "seeking",
45
+ "selected",
46
+ "truespeed",
47
+ "typemustmatch",
48
+ "willvalidate"
49
+ ];
50
+
51
+ var PROPERTY_ALIASES = {
52
+ "class": "className",
53
+ "readonly": "readOnly"
54
+ };
55
+
56
+ function isSelectable(element){
57
+ var tagName = element.tagName.toUpperCase();
58
+
59
+ if (tagName == "OPTION"){
60
+ return true;
61
+ }
62
+
63
+ if (tagName == "INPUT") {
64
+ var type = element.type.toLowerCase();
65
+ return type == "checkbox" || type == "radio";
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ function isSelected(element){
72
+ var propertyName = "selected";
73
+ var type = element.type && element.type.toLowerCase();
74
+ if ("checkbox" == type || "radio" == type) {
75
+ propertyName = "checked";
76
+ }
77
+
78
+ return !!element[propertyName];
79
+ }
80
+
81
+ function getAttributeValue(element, name){
82
+ var attr = element.getAttributeNode(name);
83
+ return (attr && attr.specified) ? attr.value : null;
84
+ }
85
+
86
+ return function get(element, attribute){
87
+ var value = null;
88
+ var name = attribute.toLowerCase();
89
+
90
+ if ("style" == name) {
91
+ value = element.style;
92
+
93
+ if (value && (typeof value != "string")) {
94
+ value = value.cssText;
95
+ }
96
+
97
+ return value;
98
+ }
99
+
100
+ if (("selected" == name || "checked" == name) &&
101
+ isSelectable(element)) {
102
+ return isSelected(element) ? "true" : null;
103
+ }
104
+
105
+ tagName = element.tagName.toUpperCase();
106
+
107
+ // The property is consistent. Return that in preference to the attribute for links and images.
108
+ if (((tagName == "IMG") && name == "src") ||
109
+ ((tagName == "A") && name == "href")) {
110
+ value = getAttributeValue(element, name);
111
+ if (value) {
112
+ // We want the full URL if present
113
+ value = element[name];
114
+ }
115
+ return value;
116
+ }
117
+
118
+ if ("spellcheck" == name) {
119
+ value = getAttributeValue(element, name);
120
+ if (!value === null) {
121
+ if (value.toLowerCase() == "false") {
122
+ return "false";
123
+ } else if (value.toLowerCase() == "true") {
124
+ return "true";
125
+ }
126
+ }
127
+ // coerce the property value to a string
128
+ return element[name] + "";
129
+ }
130
+ var propName = PROPERTY_ALIASES[attribute] || attribute;
131
+ if (BOOLEAN_PROPERTIES.some(function(prop){ prop == name })) {
132
+ value = getAttributeValue(element, name);
133
+ value = !(value === null) || element[propName];
134
+ return value ? "true" : null;
135
+ }
136
+ var property;
137
+ try {
138
+ property = element[propName]
139
+ } catch (e) {
140
+ // Leaves property undefined or null
141
+ }
142
+ // 1- Call getAttribute if getProperty fails,
143
+ // i.e. property is null or undefined.
144
+ // This happens for event handlers in Firefox.
145
+ // For example, calling getProperty for 'onclick' would
146
+ // fail while getAttribute for 'onclick' will succeed and
147
+ // return the JS code of the handler.
148
+ //
149
+ // 2- When property is an object we fall back to the
150
+ // actual attribute instead.
151
+ // See issue http://code.google.com/p/selenium/issues/detail?id=966
152
+ if ((property == null) || (typeof property == "object") || (typeof property == "function")) {
153
+ value = getAttributeValue(element, attribute);
154
+ } else {
155
+ value = property;
156
+ };
157
+
158
+ // The empty string is a valid return value.
159
+ return value != null ? value.toString() : null;
160
+ };
161
+ })()
@@ -0,0 +1,454 @@
1
+ (function(){
2
+ var OverflowState = {
3
+ NONE: "none",
4
+ HIDDEN: "hidden",
5
+ SCROLL: "scroll"
6
+ };
7
+
8
+ function isShown_(elem, ignoreOpacity, parentsDisplayedFn) {
9
+ // By convention, BODY element is always shown: BODY represents the document
10
+ // and even if there's nothing rendered in there, user can always see there's
11
+ // the document.
12
+ var elemTagName = elem.tagName.toUpperCase();
13
+ if (elemTagName == "BODY") {
14
+ return true;
15
+ }
16
+
17
+ // Child of DETAILS element is not shown unless the DETAILS element is open
18
+ // or the child is a SUMMARY element.
19
+
20
+ var parent = getParentElement(elem);
21
+ if (parent && parent.tagName && (parent.tagName.toUpperCase() == "DETAILS") &&
22
+ !parent.open && !(elemTagName == "SUMMARY")) {
23
+ return false;
24
+ }
25
+
26
+ // Option or optgroup is shown if enclosing select is shown (ignoring the
27
+ // select's opacity).
28
+ if ((elemTagName == "OPTION") ||
29
+ (elemTagName == "OPTGROUP")) {
30
+ var select = getAncestor(elem, function(e) {
31
+ return e.tagName.toUpperCase() == "SELECT";
32
+ });
33
+ return !!select && isShown_(select, true, parentsDisplayedFn);
34
+ }
35
+
36
+ // Image map elements are shown if image that uses it is shown, and
37
+ // the area of the element is positive.
38
+ var imageMap = maybeFindImageMap_(elem);
39
+ if (imageMap) {
40
+ return !!imageMap.image &&
41
+ imageMap.rect.width > 0 && imageMap.rect.height > 0 &&
42
+ isShown_(imageMap.image, ignoreOpacity, parentsDisplayedFn);
43
+ }
44
+
45
+ // Any hidden input is not shown.
46
+ if ((elemTagName == "INPUT") && (elem.type.toLowerCase() == "hidden")) {
47
+ return false;
48
+ }
49
+
50
+ // Any NOSCRIPT element is not shown.
51
+ if (elemTagName == "NOSCRIPT") {
52
+ return false;
53
+ }
54
+
55
+ // Any element with hidden/collapsed visibility is not shown.
56
+ var visibility = window.getComputedStyle(elem)["visibility"];
57
+ if (visibility == "collapse" || visibility == "hidden") {
58
+ return false;
59
+ }
60
+
61
+ if (!parentsDisplayedFn(elem)) {
62
+ return false;
63
+ }
64
+
65
+ // Any transparent element is not shown.
66
+ if (!ignoreOpacity && getOpacity(elem) == 0) {
67
+ return false;
68
+ }
69
+
70
+ // Any element without positive size dimensions is not shown.
71
+ function positiveSize(e) {
72
+ var rect = getClientRect(e);
73
+ if (rect.height > 0 && rect.width > 0) {
74
+ return true;
75
+ }
76
+ // A vertical or horizontal SVG Path element will report zero width or
77
+ // height but is "shown" if it has a positive stroke-width.
78
+ if ((e.tagName.toUpperCase() == "PATH") && (rect.height > 0 || rect.width > 0)) {
79
+ var strokeWidth = window.getComputedStyle(e)["stroke-width"];
80
+ return !!strokeWidth && (parseInt(strokeWidth, 10) > 0);
81
+ }
82
+ // Zero-sized elements should still be considered to have positive size
83
+ // if they have a child element or text node with positive size, unless
84
+ // the element has an 'overflow' style of "hidden".
85
+ return window.getComputedStyle(e)["overflow"] != "hidden" &&
86
+ Array.prototype.slice.call(e.childNodes).some(function(n) {
87
+ return (n.nodeType == Node.TEXT_NODE) ||
88
+ ((n.nodeType == Node.ELEMENT_NODE) && positiveSize(n));
89
+ });
90
+ }
91
+
92
+ if (!positiveSize(elem)) {
93
+ return false;
94
+ }
95
+
96
+ // Elements that are hidden by overflow are not shown.
97
+ function hiddenByOverflow(e) {
98
+ return getOverflowState(e) == OverflowState.HIDDEN &&
99
+ Array.prototype.slice.call(e.childNodes).every(function(n) {
100
+ return (n.nodeType != Node.ELEMENT_NODE) || hiddenByOverflow(n) ||
101
+ !positiveSize(n);
102
+ });
103
+ }
104
+ return !hiddenByOverflow(elem);
105
+ }
106
+
107
+ function getClientRegion(elem) {
108
+ var region = getClientRect(elem);
109
+ return { left: region.left,
110
+ right: region.left + region.width,
111
+ top: region.top,
112
+ bottom: region.top + region.height };
113
+ }
114
+
115
+ function getParentElement(node) {
116
+ return node.parentElement
117
+ }
118
+
119
+ function getOverflowState(elem) {
120
+ var region = getClientRegion(elem);
121
+ var ownerDoc = elem.ownerDocument;
122
+ var htmlElem = ownerDoc.documentElement;
123
+ var bodyElem = ownerDoc.body;
124
+ var htmlOverflowStyle = window.getComputedStyle(htmlElem)["overflow"];
125
+ var treatAsFixedPosition;
126
+
127
+ // Return the closest ancestor that the given element may overflow.
128
+ function getOverflowParent(e) {
129
+ function canBeOverflowed(container) {
130
+ // The HTML element can always be overflowed.
131
+ if (container == htmlElem) {
132
+ return true;
133
+ }
134
+ var containerStyle = window.getComputedStyle(container);
135
+ // An element cannot overflow an element with an inline or contents display style.
136
+ var containerDisplay = containerStyle["display"];
137
+ if ((containerDisplay.indexOf("inline") == 0) ||
138
+ (containerDisplay == "contents")) {
139
+ return false;
140
+ }
141
+ // An absolute-positioned element cannot overflow a static-positioned one.
142
+ if ((position == "absolute") && (containerStyle["position"] == "static")) {
143
+ return false;
144
+ }
145
+ return true;
146
+ }
147
+
148
+ var position = window.getComputedStyle(e)["position"];
149
+ if (position == "fixed") {
150
+ treatAsFixedPosition = true;
151
+ // Fixed-position element may only overflow the viewport.
152
+ return e == htmlElem ? null : htmlElem;
153
+ } else {
154
+ var parent = getParentElement(e);
155
+ while (parent && !canBeOverflowed(parent)) {
156
+ parent = getParentElement(parent);
157
+ }
158
+ return parent;
159
+ }
160
+ };
161
+
162
+ // Return the x and y overflow styles for the given element.
163
+ function getOverflowStyles(e) {
164
+ // When the <html> element has an overflow style of 'visible', it assumes
165
+ // the overflow style of the body, and the body is really overflow:visible.
166
+ var overflowElem = e;
167
+ if (htmlOverflowStyle == "visible") {
168
+ // Note: bodyElem will be null/undefined in SVG documents.
169
+ if (e == htmlElem && bodyElem) {
170
+ overflowElem = bodyElem;
171
+ } else if (e == bodyElem) {
172
+ return {x: "visible", y: "visible"};
173
+ }
174
+ }
175
+ var overflowElemStyle = window.getComputedStyle(overflowElem);
176
+ var overflow = {
177
+ x: overflowElemStyle["overflow-x"],
178
+ y: overflowElemStyle["overflow-y"]
179
+ };
180
+ // The <html> element cannot have a genuine 'visible' overflow style,
181
+ // because the viewport can't expand; 'visible' is really 'auto'.
182
+ if (e == htmlElem) {
183
+ overflow.x = overflow.x == "visible" ? "auto" : overflow.x;
184
+ overflow.y = overflow.y == "visible" ? "auto" : overflow.y;
185
+ }
186
+ return overflow;
187
+ };
188
+
189
+ // Returns the scroll offset of the given element.
190
+ function getScroll(e) {
191
+ if (e == htmlElem) {
192
+ return { x: window.scrollX, y: window.scrollY }
193
+ }
194
+ return { x: e.scrollLeft, y: e.scrollTop }
195
+ }
196
+
197
+ // Check if the element overflows any ancestor element.
198
+ for (var container = getOverflowParent(elem);
199
+ !!container;
200
+ container = getOverflowParent(container)) {
201
+ var containerOverflow = getOverflowStyles(container);
202
+
203
+ // If the container has overflow:visible, the element cannot overflow it.
204
+ if (containerOverflow.x == "visible" && containerOverflow.y == "visible") {
205
+ continue;
206
+ }
207
+
208
+ var containerRect = getClientRect(container);
209
+
210
+ // Zero-sized containers without overflow:visible hide all descendants.
211
+ if (containerRect.width == 0 || containerRect.height == 0) {
212
+ return OverflowState.HIDDEN;
213
+ }
214
+
215
+ // Check "underflow": if an element is to the left or above the container
216
+ var underflowsX = region.right < containerRect.left;
217
+ var underflowsY = region.bottom < containerRect.top;
218
+ if ((underflowsX && containerOverflow.x == "hidden") ||
219
+ (underflowsY && containerOverflow.y == "hidden")) {
220
+ return OverflowState.HIDDEN;
221
+ } else if ((underflowsX && containerOverflow.x != "visible") ||
222
+ (underflowsY && containerOverflow.y != "visible")) {
223
+ // When the element is positioned to the left or above a container, we
224
+ // have to distinguish between the element being completely outside the
225
+ // container and merely scrolled out of view within the container.
226
+ var containerScroll = getScroll(container);
227
+ var unscrollableX = region.right < containerRect.left - containerScroll.x;
228
+ var unscrollableY = region.bottom < containerRect.top - containerScroll.y;
229
+ if ((unscrollableX && containerOverflow.x != "visible") ||
230
+ (unscrollableY && containerOverflow.x != "visible")) {
231
+ return OverflowState.HIDDEN;
232
+ }
233
+ var containerState = getOverflowState(container);
234
+ return containerState == OverflowState.HIDDEN ?
235
+ OverflowState.HIDDEN : OverflowState.SCROLL;
236
+ }
237
+
238
+ // Check "overflow": if an element is to the right or below a container
239
+ var overflowsX = region.left >= containerRect.left + containerRect.width;
240
+ var overflowsY = region.top >= containerRect.top + containerRect.height;
241
+ if ((overflowsX && containerOverflow.x == "hidden") ||
242
+ (overflowsY && containerOverflow.y == "hidden")) {
243
+ return OverflowState.HIDDEN;
244
+ } else if ((overflowsX && containerOverflow.x != "visible") ||
245
+ (overflowsY && containerOverflow.y != "visible")) {
246
+ // If the element has fixed position and falls outside the scrollable area
247
+ // of the document, then it is hidden.
248
+ if (treatAsFixedPosition) {
249
+ var docScroll = getScroll(container);
250
+ if ((region.left >= htmlElem.scrollWidth - docScroll.x) ||
251
+ (region.right >= htmlElem.scrollHeight - docScroll.y)) {
252
+ return OverflowState.HIDDEN;
253
+ }
254
+ }
255
+ // If the element can be scrolled into view of the parent, it has a scroll
256
+ // state; unless the parent itself is entirely hidden by overflow, in
257
+ // which it is also hidden by overflow.
258
+ var containerState = getOverflowState(container);
259
+ return containerState == OverflowState.HIDDEN ?
260
+ OverflowState.HIDDEN : OverflowState.SCROLL;
261
+ }
262
+ }
263
+
264
+ // Does not overflow any ancestor.
265
+ return OverflowState.NONE;
266
+ }
267
+
268
+ function getViewportSize(win) {
269
+ var el = win.document.documentElement;
270
+ return { width: el.clientWidth, height: el.clientHeight };
271
+ }
272
+
273
+ function rect_(x, y, w, h){
274
+ return { left: x, top: y, width: w, height: h };
275
+ }
276
+
277
+ function getClientRect(elem) {
278
+ var imageMap = maybeFindImageMap_(elem);
279
+ if (imageMap) {
280
+ return imageMap.rect;
281
+ } else if (elem.tagName.toUpperCase() == "HTML") {
282
+ // Define the client rect of the <html> element to be the viewport.
283
+ var doc = elem.ownerDocument;
284
+ // TODO: Is this too simplified???
285
+ var viewportSize = getViewportSize(window);
286
+ return rect_(0, 0, viewportSize.width, viewportSize.height);
287
+ } else {
288
+ var nativeRect;
289
+ try {
290
+ nativeRect = elem.getBoundingClientRect();
291
+ } catch (e) {
292
+ return rect_(0, 0, 0, 0);
293
+ }
294
+
295
+ return rect_(nativeRect.left, nativeRect.top,
296
+ nativeRect.right - nativeRect.left, nativeRect.bottom - nativeRect.top);
297
+ }
298
+ }
299
+
300
+ function getOpacity(elem) {
301
+ // By default the element is opaque.
302
+ var elemOpacity = 1;
303
+
304
+ var opacityStyle = window.getComputedStyle(elem)["opacity"];
305
+ if (opacityStyle) {
306
+ elemOpacity = Number(opacityStyle);
307
+ }
308
+
309
+ // Let's apply the parent opacity to the element.
310
+ var parentElement = getParentElement(elem);
311
+ if (parentElement && parentElement.nodeType == Node.ELEMENT_NODE) {
312
+ elemOpacity = elemOpacity * getOpacity(parentElement);
313
+ }
314
+ return elemOpacity;
315
+ }
316
+
317
+ function getAreaRelativeRect_(area) {
318
+ var shape = area.shape.toLowerCase();
319
+ var coords = area.coords.split(",");
320
+ if (shape == "rect" && coords.length == 4) {
321
+ var x = coords[0], y = coords[1];
322
+ return rect_(x, y, coords[2] - x, coords[3] - y);
323
+ } else if (shape == "circle" && coords.length == 3) {
324
+ var centerX = coords[0], centerY = coords[1], radius = coords[2];
325
+ return rect_(centerX - radius, centerY - radius, 2 * radius, 2 * radius);
326
+ } else if (shape == "poly" && coords.length > 2) {
327
+ var minX = coords[0], minY = coords[1], maxX = minX, maxY = minY;
328
+ for (var i = 2; i + 1 < coords.length; i += 2) {
329
+ minX = Math.min(minX, coords[i]);
330
+ maxX = Math.max(maxX, coords[i]);
331
+ minY = Math.min(minY, coords[i + 1]);
332
+ maxY = Math.max(maxY, coords[i + 1]);
333
+ }
334
+ return rect_(minX, minY, maxX - minX, maxY - minY);
335
+ }
336
+ return rect_(0, 0, 0, 0);
337
+ }
338
+
339
+ function maybeFindImageMap_(elem) {
340
+ // If not a <map> or <area>, return null indicating so.
341
+ var elemTagName = elem.tagName.toUpperCase();
342
+ var isMap = elemTagName == "MAP";
343
+ if (!isMap && (elemTagName != "AREA")) {
344
+ return null;
345
+ }
346
+
347
+ // Get the <map> associated with this element, or null if none.
348
+ var map = isMap ? elem :
349
+ ((getParentElement(elem).tagName.toUpperCase() == "MAP") ?
350
+ getParentElement(elem) : null);
351
+
352
+ var image = null, rect = null;
353
+ if (map && map.name) {
354
+ var mapDoc = map.ownerDocument;
355
+
356
+ image = mapDoc.querySelector("*[usemap='#" + map.name + "']");
357
+
358
+ if (image) {
359
+ rect = getClientRect(image);
360
+ if (!isMap && elem.shape.toLowerCase() != "default") {
361
+ // Shift and crop the relative area rectangle to the map.
362
+ var relRect = getAreaRelativeRect_(elem);
363
+ var relX = Math.min(Math.max(relRect.left, 0), rect.width);
364
+ var relY = Math.min(Math.max(relRect.top, 0), rect.height);
365
+ var w = Math.min(relRect.width, rect.width - relX);
366
+ var h = Math.min(relRect.height, rect.height - relY);
367
+ rect = rect_(relX + rect.left, relY + rect.top, w, h);
368
+ }
369
+ }
370
+ }
371
+
372
+ return {image: image, rect: rect || rect_(0, 0, 0, 0)};
373
+ }
374
+
375
+ function getAncestor(element, matcher) {
376
+ if (element) {
377
+ element = getParentElement(element);
378
+ }
379
+ while (element) {
380
+ if (matcher(element)) {
381
+ return element;
382
+ }
383
+ element = getParentElement(element);
384
+ }
385
+ // Reached the root of the DOM without a match
386
+ return null;
387
+ }
388
+
389
+
390
+ function isElement(node, opt_tagName) {
391
+ // because we call this with deprecated tags such as SHADOW
392
+ if (opt_tagName && (typeof opt_tagName !== "string")) {
393
+ opt_tagName = opt_tagName.toString();
394
+ }
395
+ return !!node && node.nodeType == Node.ELEMENT_NODE &&
396
+ (!opt_tagName || node.tagName.toUpperCase() == opt_tagName);
397
+ }
398
+
399
+ function getParentNodeInComposedDom(node) {
400
+ var /**@type {Node}*/ parent = node.parentNode;
401
+
402
+ // Shadow DOM v1
403
+ if (parent && parent.shadowRoot && node.assignedSlot !== undefined) {
404
+ // Can be null on purpose, meaning it has no parent as
405
+ // it hasn't yet been slotted
406
+ return node.assignedSlot ? node.assignedSlot.parentNode : null;
407
+ }
408
+
409
+ // Shadow DOM V0 (deprecated)
410
+ if (node.getDestinationInsertionPoints) {
411
+ var destinations = node.getDestinationInsertionPoints();
412
+ if (destinations.length > 0) {
413
+ return destinations[destinations.length - 1];
414
+ }
415
+ }
416
+
417
+ return parent;
418
+ }
419
+
420
+ return function isShown(elem, opt_ignoreOpacity) {
421
+ /**
422
+ * Determines whether an element or its parents have `display: none` set
423
+ * @param {!Node} e the element
424
+ * @return {boolean}
425
+ */
426
+ function displayed(e) {
427
+ if (window.getComputedStyle(e)["display"] == "none"){
428
+ return false;
429
+ }
430
+
431
+ var parent = getParentNodeInComposedDom(e);
432
+
433
+ if ((typeof ShadowRoot === "function") && (parent instanceof ShadowRoot)) {
434
+ if (parent.host.shadowRoot !== parent) {
435
+ // There is a younger shadow root, which will take precedence over
436
+ // the shadow this element is in, thus this element won't be
437
+ // displayed.
438
+ return false;
439
+ } else {
440
+ parent = parent.host;
441
+ }
442
+ }
443
+
444
+ if (parent && (parent.nodeType == Node.DOCUMENT_NODE ||
445
+ parent.nodeType == Node.DOCUMENT_FRAGMENT_NODE)) {
446
+ return true;
447
+ }
448
+
449
+ return parent && displayed(parent);
450
+ }
451
+
452
+ return isShown_(elem, !!opt_ignoreOpacity, displayed);
453
+ };
454
+ })()