capybara 3.13.2 → 3.14.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.
- checksums.yaml +4 -4
- data/History.md +16 -1
- data/README.md +2 -2
- data/lib/capybara.rb +13 -6
- data/lib/capybara/node/base.rb +11 -4
- data/lib/capybara/rack_test/driver.rb +4 -0
- data/lib/capybara/rack_test/errors.rb +6 -0
- data/lib/capybara/rack_test/node.rb +18 -2
- data/lib/capybara/selenium/driver.rb +5 -0
- data/lib/capybara/selenium/extensions/find.rb +3 -3
- data/lib/capybara/selenium/extensions/html5_drag.rb +52 -1
- data/lib/capybara/selenium/nodes/chrome_node.rb +7 -0
- data/lib/capybara/selenium/nodes/firefox_node.rb +7 -0
- data/lib/capybara/spec/public/test.js +1 -0
- data/lib/capybara/spec/session/node_spec.rb +9 -0
- data/lib/capybara/spec/session/scroll_spec.rb +1 -1
- data/lib/capybara/spec/session/within_spec.rb +23 -0
- data/lib/capybara/spec/test_app.rb +2 -2
- data/lib/capybara/spec/views/with_hover.erb +1 -0
- data/lib/capybara/spec/views/with_hover1.erb +10 -0
- data/lib/capybara/spec/views/with_scope_other.erb +6 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/shared_selenium_session.rb +29 -0
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f79ad67119c7cc637fcab5cbc4fe95b0d80a76c2c38182f2a6b8beb3564f8448
         | 
| 4 | 
            +
              data.tar.gz: 9d5c1ca24bbc3e7f2a9c18c71c5b61a78b96d24147b21c9b0f753217d4fc2772
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a799dc6db1032d812bf749ca8ee8ca8b46e59e957130f80ddfb9d5c2b08fce1a543ce3a0e9c28ee2bf38470867bc6d8a64ce6ed3f3d6c32ae3f31f41c071cb8f
         | 
| 7 | 
            +
              data.tar.gz: d41beb222470a77bd2f2784a8def99ef3fc4b2928ee1b50cb66167d1764a94ec7ea1be59424c4d09f2a7981b3c6ba47ee6df02b13d6472480f10d9698c742e82
         | 
    
        data/History.md
    CHANGED
    
    | @@ -1,3 +1,18 @@ | |
| 1 | 
            +
            # Version 3.14
         | 
| 2 | 
            +
            Release date: 2019-02-25
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            ### Added
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            * rack_test driver now supports reloading elements when the document changes - Issue #2157
         | 
| 7 | 
            +
            * Selenium driver HTML5 drag-drop emulation now emits multiple move events so drag direction
         | 
| 8 | 
            +
              is determinable [Erkki Eilonen, Thomas Walpole]
         | 
| 9 | 
            +
            * Capybara.server_errors now defaults to [Exception] - Issue #2160 [Edgars Beigarts]
         | 
| 10 | 
            +
            ### Fixed
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            * Workaround hover issue with FF 65 - Issue #2156
         | 
| 13 | 
            +
            * Workaround chromedriver issue when setting blank strings to react controlled text fields
         | 
| 14 | 
            +
            * Workaround chromedriver issue with popup windows not loading content - https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
         | 
| 15 | 
            +
             | 
| 1 16 | 
             
            # Version 3.13.2
         | 
| 2 17 | 
             
            Release date: 2019-01-24
         | 
| 3 18 |  | 
| @@ -22,7 +37,7 @@ Release date: 2019-01-23 | |
| 22 37 | 
             
            * Speed optimizations around multiple element location and path generation when using the Selenium driver
         | 
| 23 38 | 
             
            * Support for locator type checking in custom selectors
         | 
| 24 39 | 
             
            * Allow configuration of gumbo use - defaults to off
         | 
| 25 | 
            -
            * `assert_style`/`has_style`/`have_style`  | 
| 40 | 
            +
            * `assert_style`/`has_style`/`have_style` deprecated in favor of `assert_matches_style`/`matches_styles?`/`match_style`
         | 
| 26 41 | 
             
            * :style filter added to selectors
         | 
| 27 42 |  | 
| 28 43 | 
             
            # Version 3.12.0
         | 
    
        data/README.md
    CHANGED
    
    | @@ -6,7 +6,7 @@ | |
| 6 6 | 
             
            [](https://gitter.im/jnicklas/capybara?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
         | 
| 7 7 | 
             
            [](https://dependabot.com/compatibility-score.html?dependency-name=capybara&package-manager=bundler&version-scheme=semver)
         | 
| 8 8 |  | 
| 9 | 
            -
            **Note** You are viewing the README for the  | 
| 9 | 
            +
            **Note** You are viewing the README for the 3.14.x version of Capybara.
         | 
| 10 10 |  | 
| 11 11 | 
             
            Capybara helps you test web applications by simulating how a real user would
         | 
| 12 12 | 
             
            interact with your app. It is agnostic about the driver running your tests and
         | 
| @@ -409,7 +409,7 @@ and test server, see [Transactions and database setup](#transactions-and-databas | |
| 409 409 |  | 
| 410 410 | 
             
            ### <a name="apparition"></a>Apparition
         | 
| 411 411 |  | 
| 412 | 
            -
            The [apparition driver](https://github.com/twalpole/apparition) in a new driver  | 
| 412 | 
            +
            The [apparition driver](https://github.com/twalpole/apparition) in a new driver that allows you to run tests using Chrome in a headless
         | 
| 413 413 | 
             
            or headed configuration. It attempts to provide backwards compatibility with the [Poltergeist driver API](https://github.com/teampoltergeist/poltergeist)
         | 
| 414 414 | 
             
            while allowing for the use of modern JS/CSS. It uses CDP to communicate with Chrome, thereby obviating the need for chromedriver.
         | 
| 415 415 | 
             
            A compatibility layer for capybara-webkit is planned, although has not yet been started. This driver is being developed by the
         | 
    
        data/lib/capybara.rb
    CHANGED
    
    | @@ -73,7 +73,7 @@ module Capybara | |
| 73 73 | 
             
                # [asset_host = String]               Where dynamic assets are hosted - will be prepended to relative asset locations if present (Default: nil)
         | 
| 74 74 | 
             
                # [run_server = Boolean]              Whether to start a Rack server for the given Rack app (Default: true)
         | 
| 75 75 | 
             
                # [raise_server_errors = Boolean]     Should errors raised in the server be raised in the tests? (Default: true)
         | 
| 76 | 
            -
                # [server_errors = Array\<Class\>]    Error classes that should be raised in the tests if they are raised in the server and Capybara.raise_server_errors is true (Default: [ | 
| 76 | 
            +
                # [server_errors = Array\<Class\>]    Error classes that should be raised in the tests if they are raised in the server and Capybara.raise_server_errors is true (Default: [Exception])
         | 
| 77 77 | 
             
                # [default_selector = :css/:xpath]    Methods which take a selector use the given type by default (Default: :css)
         | 
| 78 78 | 
             
                # [default_max_wait_time = Numeric]   The maximum number of seconds to wait for asynchronous processes to finish (Default: 2)
         | 
| 79 79 | 
             
                # [ignore_hidden_elements = Boolean]  Whether to ignore hidden elements on the page (Default: true)
         | 
| @@ -516,7 +516,7 @@ Capybara.configure do |config| | |
| 516 516 | 
             
              config.exact = false
         | 
| 517 517 | 
             
              config.exact_text = false
         | 
| 518 518 | 
             
              config.raise_server_errors = true
         | 
| 519 | 
            -
              config.server_errors = [ | 
| 519 | 
            +
              config.server_errors = [Exception]
         | 
| 520 520 | 
             
              config.visible_text_only = false
         | 
| 521 521 | 
             
              config.automatic_label_click = false
         | 
| 522 522 | 
             
              config.enable_aria_label = false
         | 
| @@ -544,13 +544,20 @@ Capybara.register_driver :selenium_headless do |app| | |
| 544 544 | 
             
            end
         | 
| 545 545 |  | 
| 546 546 | 
             
            Capybara.register_driver :selenium_chrome do |app|
         | 
| 547 | 
            -
               | 
| 547 | 
            +
              browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
         | 
| 548 | 
            +
                # Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
         | 
| 549 | 
            +
                opts.args << '--disable-site-isolation-trials'
         | 
| 550 | 
            +
              end
         | 
| 551 | 
            +
              Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
         | 
| 548 552 | 
             
            end
         | 
| 549 553 |  | 
| 550 554 | 
             
            Capybara.register_driver :selenium_chrome_headless do |app|
         | 
| 551 555 | 
             
              Capybara::Selenium::Driver.load_selenium
         | 
| 552 | 
            -
              browser_options = ::Selenium::WebDriver::Chrome::Options.new
         | 
| 553 | 
            -
             | 
| 554 | 
            -
             | 
| 556 | 
            +
              browser_options = ::Selenium::WebDriver::Chrome::Options.new.tap do |opts|
         | 
| 557 | 
            +
                opts.args << '--headless'
         | 
| 558 | 
            +
                opts.args << '--disable-gpu' if Gem.win_platform?
         | 
| 559 | 
            +
                # Workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2650&q=load&sort=-id&colspec=ID%20Status%20Pri%20Owner%20Summary
         | 
| 560 | 
            +
                opts.args << '--disable-site-isolation-trials'
         | 
| 561 | 
            +
              end
         | 
| 555 562 | 
             
              Capybara::Selenium::Driver.new(app, browser: :chrome, options: browser_options)
         | 
| 556 563 | 
             
            end
         | 
    
        data/lib/capybara/node/base.rb
    CHANGED
    
    | @@ -83,11 +83,18 @@ module Capybara | |
| 83 83 | 
             
                      yield
         | 
| 84 84 | 
             
                    rescue StandardError => err
         | 
| 85 85 | 
             
                      session.raise_server_error!
         | 
| 86 | 
            -
                      raise err unless  | 
| 87 | 
            -
                      raise err if timer.expired?
         | 
| 86 | 
            +
                      raise err unless catch_error?(err, errors)
         | 
| 88 87 |  | 
| 89 | 
            -
                       | 
| 90 | 
            -
             | 
| 88 | 
            +
                      if driver.wait?
         | 
| 89 | 
            +
                        raise err if timer.expired?
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                        sleep(0.01)
         | 
| 92 | 
            +
                        reload if session_options.automatic_reload
         | 
| 93 | 
            +
                      else
         | 
| 94 | 
            +
                        old_base = @base
         | 
| 95 | 
            +
                        reload if session_options.automatic_reload
         | 
| 96 | 
            +
                        raise err if old_base == @base
         | 
| 97 | 
            +
                      end
         | 
| 91 98 | 
             
                      retry
         | 
| 92 99 | 
             
                    ensure
         | 
| 93 100 | 
             
                      session.synchronized = false
         | 
| @@ -102,4 +102,8 @@ class Capybara::RackTest::Driver < Capybara::Driver::Base | |
| 102 102 | 
             
              def put(*args, &block); browser.put(*args, &block); end
         | 
| 103 103 | 
             
              def delete(*args, &block); browser.delete(*args, &block); end
         | 
| 104 104 | 
             
              def header(key, value); browser.header(key, value); end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              def invalid_element_errors
         | 
| 107 | 
            +
                [Capybara::RackTest::Errors::StaleElementReferenceError]
         | 
| 108 | 
            +
              end
         | 
| 105 109 | 
             
            end
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require 'capybara/rack_test/errors'
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            class Capybara::RackTest::Node < Capybara::Driver::Node
         | 
| 4 6 | 
             
              BLOCK_ELEMENTS = %w[p h1 h2 h3 h4 h5 h6 ol ul pre address blockquote dl div fieldset form hr noscript table].freeze
         | 
| 5 7 |  | 
| @@ -106,14 +108,24 @@ class Capybara::RackTest::Node < Capybara::Driver::Node | |
| 106 108 | 
             
                native.path
         | 
| 107 109 | 
             
              end
         | 
| 108 110 |  | 
| 109 | 
            -
              def find_xpath(locator)
         | 
| 111 | 
            +
              def find_xpath(locator, **_hints)
         | 
| 110 112 | 
             
                native.xpath(locator).map { |el| self.class.new(driver, el) }
         | 
| 111 113 | 
             
              end
         | 
| 112 114 |  | 
| 113 | 
            -
              def find_css(locator)
         | 
| 115 | 
            +
              def find_css(locator, **_hints)
         | 
| 114 116 | 
             
                native.css(locator, Capybara::RackTest::CSSHandlers.new).map { |el| self.class.new(driver, el) }
         | 
| 115 117 | 
             
              end
         | 
| 116 118 |  | 
| 119 | 
            +
              public_instance_methods(false).each do |meth_name|
         | 
| 120 | 
            +
                alias_method "unchecked_#{meth_name}", meth_name
         | 
| 121 | 
            +
                private "unchecked_#{meth_name}" # rubocop:disable Layout/AccessModifierIndentation,Style/AccessModifierDeclarations
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                define_method meth_name do |*args|
         | 
| 124 | 
            +
                  stale_check
         | 
| 125 | 
            +
                  send("unchecked_#{meth_name}", *args)
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
             | 
| 117 129 | 
             
              def ==(other)
         | 
| 118 130 | 
             
                native == other.native
         | 
| 119 131 | 
             
              end
         | 
| @@ -141,6 +153,10 @@ protected | |
| 141 153 |  | 
| 142 154 | 
             
            private
         | 
| 143 155 |  | 
| 156 | 
            +
              def stale_check
         | 
| 157 | 
            +
                raise Capybara::RackTest::Errors::StaleElementReferenceError unless native.document == driver.dom
         | 
| 158 | 
            +
              end
         | 
| 159 | 
            +
             | 
| 144 160 | 
             
              def deselect_options
         | 
| 145 161 | 
             
                select_node.find_xpath('.//option[@selected]').each { |node| node.native.remove_attribute('selected') }
         | 
| 146 162 | 
             
              end
         | 
| @@ -386,6 +386,11 @@ private | |
| 386 386 | 
             
                  raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired?
         | 
| 387 387 |  | 
| 388 388 | 
             
                  sleep 0.01
         | 
| 389 | 
            +
             | 
| 390 | 
            +
                  # It has been observed that it is possible that asynchronous JS code in
         | 
| 391 | 
            +
                  # the application under test can navigate the browser away from about:blank
         | 
| 392 | 
            +
                  # if the timing is just right. Ensure we are still at about:blank...
         | 
| 393 | 
            +
                  @browser.navigate.to('about:blank') unless current_url == 'about:blank'
         | 
| 389 394 | 
             
                end
         | 
| 390 395 | 
             
              end
         | 
| 391 396 |  | 
| @@ -77,9 +77,9 @@ module Capybara | |
| 77 77 | 
             
                  def is_displayed_atom # rubocop:disable Naming/PredicateName
         | 
| 78 78 | 
             
                    @@is_displayed_atom ||= begin
         | 
| 79 79 | 
             
                      browser.send(:bridge).send(:read_atom, 'isDisplayed')
         | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 80 | 
            +
                                            rescue StandardError
         | 
| 81 | 
            +
                                              # If the atom doesn't exist or other error
         | 
| 82 | 
            +
                                              ''
         | 
| 83 83 | 
             
                    end
         | 
| 84 84 | 
             
                  end
         | 
| 85 85 | 
             
                end
         | 
| @@ -30,6 +30,44 @@ class Capybara::Selenium::Node | |
| 30 30 | 
             
                  var source = arguments[0];
         | 
| 31 31 | 
             
                  var target = arguments[1];
         | 
| 32 32 |  | 
| 33 | 
            +
                  function rectCenter(rect){
         | 
| 34 | 
            +
                    return new DOMPoint(
         | 
| 35 | 
            +
                      (rect.left + rect.right)/2,
         | 
| 36 | 
            +
                      (rect.top + rect.bottom)/2
         | 
| 37 | 
            +
                    );
         | 
| 38 | 
            +
                  }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  function pointOnRect(pt, rect) {
         | 
| 41 | 
            +
                  	var rectPt = rectCenter(rect);
         | 
| 42 | 
            +
                  	var slope = (rectPt.y - pt.y) / (rectPt.x - pt.x);
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  	if (pt.x <= rectPt.x) { // left side
         | 
| 45 | 
            +
                  		var minXy = slope * (rect.left - pt.x) + pt.y;
         | 
| 46 | 
            +
                  		if (rect.top <= minXy && minXy <= rect.bottom)
         | 
| 47 | 
            +
                        return new DOMPoint(rect.left, minXy);
         | 
| 48 | 
            +
                  	}
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  	if (pt.x >= rectPt.x) { // right side
         | 
| 51 | 
            +
                  		var maxXy = slope * (rect.right - pt.x) + pt.y;
         | 
| 52 | 
            +
                  		if (rect.top <= maxXy && maxXy <= rect.bottom)
         | 
| 53 | 
            +
                        return new DOMPoint(rect.right, maxXy);
         | 
| 54 | 
            +
                  	}
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  	if (pt.y <= rectPt.y) { // top side
         | 
| 57 | 
            +
                  		var minYx = (rectPt.top - pt.y) / slope + pt.x;
         | 
| 58 | 
            +
                  		if (rect.left <= minYx && minYx <= rect.right)
         | 
| 59 | 
            +
                        return new DOMPoint(minYx, rect.top);
         | 
| 60 | 
            +
                  	}
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  	if (pt.y >= rectPt.y) { // bottom side
         | 
| 63 | 
            +
                  		var maxYx = (rect.bottom - pt.y) / slope + pt.x;
         | 
| 64 | 
            +
                  		if (rect.left <= maxYx && maxYx <= rect.right)
         | 
| 65 | 
            +
                        return new DOMPoint(maxYx, rect.bottom);
         | 
| 66 | 
            +
                  	}
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    return new DOMPoint(pt.x,pt.y);
         | 
| 69 | 
            +
                  }
         | 
| 70 | 
            +
             | 
| 33 71 | 
             
                  var dt = new DataTransfer();
         | 
| 34 72 | 
             
                  var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
         | 
| 35 73 |  | 
| @@ -41,11 +79,24 @@ class Capybara::Selenium::Node | |
| 41 79 | 
             
                    dt.setData('text/uri-list', source.src);
         | 
| 42 80 | 
             
                    dt.setData('text', source.src);
         | 
| 43 81 | 
             
                  }
         | 
| 82 | 
            +
             | 
| 44 83 | 
             
                  var dragEvent = new DragEvent('dragstart', opts);
         | 
| 45 84 | 
             
                  source.dispatchEvent(dragEvent);
         | 
| 46 85 | 
             
                  target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
         | 
| 47 | 
            -
                  var  | 
| 86 | 
            +
                  var targetRect = target.getBoundingClientRect();
         | 
| 87 | 
            +
                  var sourceCenter = rectCenter(source.getBoundingClientRect());
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  // fire 2 dragover events to simulate dragging with a direction
         | 
| 90 | 
            +
                  var entryPoint = pointOnRect(sourceCenter, targetRect)
         | 
| 91 | 
            +
                  var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
         | 
| 92 | 
            +
                  var dragOverEvent = new DragEvent('dragover', dragOverOpts);
         | 
| 48 93 | 
             
                  target.dispatchEvent(dragOverEvent);
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  var targetCenter = rectCenter(targetRect);
         | 
| 96 | 
            +
                  dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts);
         | 
| 97 | 
            +
                  dragOverEvent = new DragEvent('dragover', dragOverOpts);
         | 
| 98 | 
            +
                  target.dispatchEvent(dragOverEvent);
         | 
| 99 | 
            +
             | 
| 49 100 | 
             
                  var dragLeaveEvent = new DragEvent('dragleave', opts);
         | 
| 50 101 | 
             
                  target.dispatchEvent(dragLeaveEvent);
         | 
| 51 102 | 
             
                  if (dragOverEvent.defaultPrevented) {
         | 
| @@ -5,6 +5,13 @@ require 'capybara/selenium/extensions/html5_drag' | |
| 5 5 | 
             
            class Capybara::Selenium::ChromeNode < Capybara::Selenium::Node
         | 
| 6 6 | 
             
              include Html5Drag
         | 
| 7 7 |  | 
| 8 | 
            +
              def set_text(value, clear: nil, **_unused)
         | 
| 9 | 
            +
                super.tap do
         | 
| 10 | 
            +
                  # React doesn't see the chromedriver element clear
         | 
| 11 | 
            +
                  send_keys(:space, :backspace) if value.to_s.empty? && clear.nil?
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
             | 
| 8 15 | 
             
              def set_file(value) # rubocop:disable Naming/AccessorMethodName
         | 
| 9 16 | 
             
                super(value)
         | 
| 10 17 | 
             
              rescue ::Selenium::WebDriver::Error::ExpectedError => err
         | 
| @@ -58,6 +58,13 @@ class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node | |
| 58 58 | 
             
                html5_drag_to(element)
         | 
| 59 59 | 
             
              end
         | 
| 60 60 |  | 
| 61 | 
            +
              def hover
         | 
| 62 | 
            +
                return super unless browser_version >= 65.0
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                # work around issue 2156 - https://github.com/teamcapybara/capybara/issues/2156
         | 
| 65 | 
            +
                scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 61 68 | 
             
            private
         | 
| 62 69 |  | 
| 63 70 | 
             
              def click_with_options(click_options)
         | 
| @@ -13,6 +13,7 @@ $(function() { | |
| 13 13 | 
             
                ev.originalEvent.dataTransfer.setData("text", ev.target.id);
         | 
| 14 14 | 
             
              });
         | 
| 15 15 | 
             
              $('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
         | 
| 16 | 
            +
                $(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
         | 
| 16 17 | 
             
                if ($(this).hasClass('drop')) { ev.preventDefault(); }
         | 
| 17 18 | 
             
              });
         | 
| 18 19 | 
             
              $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
         | 
| @@ -344,6 +344,15 @@ Capybara::SpecHelper.spec 'node' do | |
| 344 344 | 
             
                  @session.find(:css, '.wrapper.scroll_needed').hover
         | 
| 345 345 | 
             
                  expect(@session.find(:css, '.wrapper.scroll_needed .hidden_until_hover', visible: false)).to be_visible
         | 
| 346 346 | 
             
                end
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                it 'should hover again after following a link and back' do
         | 
| 349 | 
            +
                  @session.visit('/with_hover')
         | 
| 350 | 
            +
                  @session.find(:css, '.wrapper:not(.scroll_needed)').hover
         | 
| 351 | 
            +
                  @session.click_link('Other hover page')
         | 
| 352 | 
            +
                  @session.click_link('Go back')
         | 
| 353 | 
            +
                  @session.find(:css, '.wrapper:not(.scroll_needed)').hover
         | 
| 354 | 
            +
                  expect(@session.find(:css, '.wrapper:not(.scroll_needed) .hidden_until_hover', visible: false)).to be_visible
         | 
| 355 | 
            +
                end
         | 
| 347 356 | 
             
              end
         | 
| 348 357 |  | 
| 349 358 | 
             
              describe '#click' do
         | 
| @@ -108,7 +108,7 @@ Capybara::SpecHelper.spec '#scroll_to', requires: [:scroll] do | |
| 108 108 | 
             
                expect(@session.evaluate_script('[window.scrollX || window.pageXOffset, window.scrollY || window.pageYOffset]')).to eq [50, 75]
         | 
| 109 109 | 
             
              end
         | 
| 110 110 |  | 
| 111 | 
            -
              it 'can scroll the scroll the scrolling element by a  | 
| 111 | 
            +
              it 'can scroll the scroll the scrolling element by a specific amount' do
         | 
| 112 112 | 
             
                scrolling_element = @session.find(:css, '#scrollable')
         | 
| 113 113 | 
             
                scrolling_element.scroll_to 100, 100
         | 
| 114 114 | 
             
                scrolling_element.scroll_to(:current, offset: [-50, 50])
         | 
| @@ -26,6 +26,29 @@ Capybara::SpecHelper.spec '#within' do | |
| 26 26 | 
             
                  end
         | 
| 27 27 | 
             
                  expect(@session).to have_content('Bar')
         | 
| 28 28 | 
             
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                it 'should reload the node if the page is changed' do
         | 
| 31 | 
            +
                  @session.within(:css, '#for_foo') do
         | 
| 32 | 
            +
                    @session.visit('/with_scope_other')
         | 
| 33 | 
            +
                    expect(@session).to have_content('Different text')
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                it 'should reload multiple nodes if the page is changed' do
         | 
| 38 | 
            +
                  @session.within(:css, '#for_bar') do
         | 
| 39 | 
            +
                    @session.within(:css, 'form[action="/redirect"]') do
         | 
| 40 | 
            +
                      @session.refresh
         | 
| 41 | 
            +
                      expect(@session).to have_content('First Name')
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                it 'should error if the page is changed and a matching node no longer exists' do
         | 
| 47 | 
            +
                  @session.within(:css, '#for_foo') do
         | 
| 48 | 
            +
                    @session.visit('/')
         | 
| 49 | 
            +
                    expect { @session.text }.to raise_error(StandardError)
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 29 52 | 
             
              end
         | 
| 30 53 |  | 
| 31 54 | 
             
              context 'with XPath selector' do
         | 
| @@ -6,8 +6,8 @@ require 'rack' | |
| 6 6 | 
             
            require 'yaml'
         | 
| 7 7 |  | 
| 8 8 | 
             
            class TestApp < Sinatra::Base
         | 
| 9 | 
            -
              class TestAppError <  | 
| 10 | 
            -
              class TestAppOtherError <  | 
| 9 | 
            +
              class TestAppError < Exception; end # rubocop:disable Lint/InheritException
         | 
| 10 | 
            +
              class TestAppOtherError < Exception # rubocop:disable Lint/InheritException
         | 
| 11 11 | 
             
                def initialize(string1, msg)
         | 
| 12 12 | 
             
                  @something = string1
         | 
| 13 13 | 
             
                  @message = msg
         | 
| @@ -10,6 +10,7 @@ | |
| 10 10 | 
             
                  .hidden_until_hover {display: none;}
         | 
| 11 11 | 
             
                  .wrapper:hover .hidden_until_hover { display: block }
         | 
| 12 12 | 
             
                </style>
         | 
| 13 | 
            +
                <a href='with_hover1'>Other hover page</a>
         | 
| 13 14 | 
             
                <div class="wrapper">
         | 
| 14 15 | 
             
                  Some text here so the wrapper has size
         | 
| 15 16 | 
             
                  <div class="hidden_until_hover">Here I am</div>
         | 
    
        data/lib/capybara/version.rb
    CHANGED
    
    
| @@ -307,6 +307,15 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode| | |
| 307 307 | 
             
                    expect(session).to have_xpath('//div[contains(., "HTML5 Dropped drag_html5")]')
         | 
| 308 308 | 
             
                  end
         | 
| 309 309 |  | 
| 310 | 
            +
                  it 'should set clientX/Y in dragover events' do
         | 
| 311 | 
            +
                    session.visit('/with_js')
         | 
| 312 | 
            +
                    element = session.find('//div[@id="drag_html5"]')
         | 
| 313 | 
            +
                    target = session.find('//div[@id="drop_html5"]')
         | 
| 314 | 
            +
                    element.drag_to(target)
         | 
| 315 | 
            +
                    session.all(:css, 'div.log').each { |el| puts el.text }
         | 
| 316 | 
            +
                    expect(session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
         | 
| 317 | 
            +
                  end
         | 
| 318 | 
            +
             | 
| 310 319 | 
             
                  it 'should not HTML5 drag and drop on a non HTML5 drop element' do
         | 
| 311 320 | 
             
                    session.visit('/with_js')
         | 
| 312 321 | 
             
                    element = session.find('//div[@id="drag_html5"]')
         | 
| @@ -453,6 +462,26 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode| | |
| 453 462 | 
             
                    expect(session).to have_selector(:element, "{custom}": 'abcdef')
         | 
| 454 463 | 
             
                  end
         | 
| 455 464 | 
             
                end
         | 
| 465 | 
            +
             | 
| 466 | 
            +
                describe 'with react' do
         | 
| 467 | 
            +
                  context 'controlled components' do
         | 
| 468 | 
            +
                    it 'can set and clear a text field' do
         | 
| 469 | 
            +
                      session.visit 'https://reactjs.org/docs/forms.html'
         | 
| 470 | 
            +
                      session.all(:css, 'h2#controlled-components ~ p a', text: 'Try it on CodePen')[0].click
         | 
| 471 | 
            +
                      sleep 2 # give codepen a chance to stabilize result frame
         | 
| 472 | 
            +
                      session.within_frame(:css, 'iframe.result-iframe') do
         | 
| 473 | 
            +
                        session.fill_in('Name:', with: 'abc')
         | 
| 474 | 
            +
                        session.accept_prompt 'A name was submitted: abc' do
         | 
| 475 | 
            +
                          session.click_button('Submit')
         | 
| 476 | 
            +
                        end
         | 
| 477 | 
            +
                        session.fill_in('Name:', with: '')
         | 
| 478 | 
            +
                        session.accept_prompt(/A name was submitted: $/) do
         | 
| 479 | 
            +
                          session.click_button('Submit')
         | 
| 480 | 
            +
                        end
         | 
| 481 | 
            +
                      end
         | 
| 482 | 
            +
                    end
         | 
| 483 | 
            +
                  end
         | 
| 484 | 
            +
                end
         | 
| 456 485 | 
             
              end
         | 
| 457 486 |  | 
| 458 487 | 
             
              def headless_or_remote?
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: capybara
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 3. | 
| 4 | 
            +
              version: 3.14.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Thomas Walpole
         | 
| @@ -10,7 +10,7 @@ autorequire: | |
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain:
         | 
| 12 12 | 
             
            - gem-public_cert.pem
         | 
| 13 | 
            -
            date: 2019- | 
| 13 | 
            +
            date: 2019-02-25 00:00:00.000000000 Z
         | 
| 14 14 | 
             
            dependencies:
         | 
| 15 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 16 16 | 
             
              name: addressable
         | 
| @@ -375,6 +375,7 @@ files: | |
| 375 375 | 
             
            - lib/capybara/rack_test/browser.rb
         | 
| 376 376 | 
             
            - lib/capybara/rack_test/css_handlers.rb
         | 
| 377 377 | 
             
            - lib/capybara/rack_test/driver.rb
         | 
| 378 | 
            +
            - lib/capybara/rack_test/errors.rb
         | 
| 378 379 | 
             
            - lib/capybara/rack_test/form.rb
         | 
| 379 380 | 
             
            - lib/capybara/rack_test/node.rb
         | 
| 380 381 | 
             
            - lib/capybara/rails.rb
         | 
| @@ -541,12 +542,14 @@ files: | |
| 541 542 | 
             
            - lib/capybara/spec/views/with_count.erb
         | 
| 542 543 | 
             
            - lib/capybara/spec/views/with_fixed_header_footer.erb
         | 
| 543 544 | 
             
            - lib/capybara/spec/views/with_hover.erb
         | 
| 545 | 
            +
            - lib/capybara/spec/views/with_hover1.erb
         | 
| 544 546 | 
             
            - lib/capybara/spec/views/with_html.erb
         | 
| 545 547 | 
             
            - lib/capybara/spec/views/with_html5_svg.erb
         | 
| 546 548 | 
             
            - lib/capybara/spec/views/with_html_entities.erb
         | 
| 547 549 | 
             
            - lib/capybara/spec/views/with_js.erb
         | 
| 548 550 | 
             
            - lib/capybara/spec/views/with_namespace.erb
         | 
| 549 551 | 
             
            - lib/capybara/spec/views/with_scope.erb
         | 
| 552 | 
            +
            - lib/capybara/spec/views/with_scope_other.erb
         | 
| 550 553 | 
             
            - lib/capybara/spec/views/with_simple_html.erb
         | 
| 551 554 | 
             
            - lib/capybara/spec/views/with_slow_unload.erb
         | 
| 552 555 | 
             
            - lib/capybara/spec/views/with_title.erb
         |