capybara 3.19.1 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ })()