capybara 3.29.0 → 3.30.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 +19 -1
- data/README.md +1 -1
- data/lib/capybara/config.rb +7 -3
- data/lib/capybara/helpers.rb +3 -1
- data/lib/capybara/node/actions.rb +23 -19
- data/lib/capybara/node/document.rb +2 -2
- data/lib/capybara/node/document_matchers.rb +3 -3
- data/lib/capybara/node/element.rb +10 -8
- data/lib/capybara/node/finders.rb +12 -10
- data/lib/capybara/node/matchers.rb +39 -33
- data/lib/capybara/node/simple.rb +3 -1
- data/lib/capybara/queries/ancestor_query.rb +1 -1
- data/lib/capybara/queries/selector_query.rb +17 -4
- data/lib/capybara/queries/sibling_query.rb +1 -1
- data/lib/capybara/rack_test/browser.rb +4 -1
- data/lib/capybara/rack_test/driver.rb +1 -1
- data/lib/capybara/rack_test/form.rb +1 -1
- data/lib/capybara/selector.rb +22 -16
- data/lib/capybara/selector/css.rb +1 -1
- data/lib/capybara/selector/definition.rb +2 -2
- data/lib/capybara/selector/definition/button.rb +7 -2
- data/lib/capybara/selector/definition/checkbox.rb +2 -2
- data/lib/capybara/selector/definition/css.rb +3 -1
- data/lib/capybara/selector/definition/datalist_input.rb +1 -1
- data/lib/capybara/selector/definition/datalist_option.rb +1 -1
- data/lib/capybara/selector/definition/element.rb +1 -1
- data/lib/capybara/selector/definition/field.rb +1 -1
- data/lib/capybara/selector/definition/file_field.rb +1 -1
- data/lib/capybara/selector/definition/fillable_field.rb +1 -1
- data/lib/capybara/selector/definition/label.rb +3 -1
- data/lib/capybara/selector/definition/radio_button.rb +2 -2
- data/lib/capybara/selector/definition/select.rb +1 -1
- data/lib/capybara/selector/definition/table.rb +5 -2
- data/lib/capybara/selector/filter_set.rb +11 -9
- data/lib/capybara/selector/filters/base.rb +6 -1
- data/lib/capybara/selector/filters/locator_filter.rb +1 -1
- data/lib/capybara/selector/selector.rb +4 -2
- data/lib/capybara/selenium/driver.rb +19 -11
- data/lib/capybara/selenium/driver_specializations/firefox_driver.rb +2 -2
- data/lib/capybara/selenium/extensions/html5_drag.rb +6 -5
- data/lib/capybara/selenium/node.rb +6 -4
- data/lib/capybara/selenium/nodes/chrome_node.rb +7 -3
- data/lib/capybara/selenium/nodes/edge_node.rb +3 -1
- data/lib/capybara/selenium/nodes/firefox_node.rb +1 -1
- data/lib/capybara/server.rb +15 -3
- data/lib/capybara/server/checker.rb +1 -1
- data/lib/capybara/server/middleware.rb +20 -10
- data/lib/capybara/session.rb +10 -8
- data/lib/capybara/session/config.rb +6 -2
- data/lib/capybara/session/matchers.rb +6 -6
- data/lib/capybara/spec/public/test.js +11 -0
- data/lib/capybara/spec/session/all_spec.rb +15 -0
- data/lib/capybara/spec/session/ancestor_spec.rb +5 -0
- data/lib/capybara/spec/session/assert_text_spec.rb +4 -0
- data/lib/capybara/spec/session/click_button_spec.rb +5 -0
- data/lib/capybara/spec/session/find_spec.rb +20 -0
- data/lib/capybara/spec/session/has_table_spec.rb +51 -5
- data/lib/capybara/spec/session/has_text_spec.rb +31 -0
- data/lib/capybara/spec/session/node_spec.rb +15 -0
- data/lib/capybara/spec/session/save_and_open_screenshot_spec.rb +2 -2
- data/lib/capybara/spec/session/save_screenshot_spec.rb +4 -4
- data/lib/capybara/spec/session/selectors_spec.rb +15 -2
- data/lib/capybara/spec/views/form.erb +5 -0
- data/lib/capybara/version.rb +1 -1
- data/spec/dsl_spec.rb +2 -2
- data/spec/minitest_spec_spec.rb +46 -46
- data/spec/regexp_dissassembler_spec.rb +45 -37
- data/spec/result_spec.rb +3 -3
- data/spec/rspec/features_spec.rb +1 -0
- data/spec/rspec/shared_spec_matchers.rb +3 -3
- data/spec/rspec_spec.rb +4 -4
- data/spec/selenium_spec_chrome.rb +3 -3
- data/spec/selenium_spec_firefox.rb +7 -2
- data/spec/server_spec.rb +42 -0
- data/spec/session_spec.rb +1 -1
- data/spec/shared_selenium_node.rb +3 -3
- data/spec/shared_selenium_session.rb +8 -7
- metadata +3 -3
| @@ -79,14 +79,18 @@ module Capybara | |
| 79 79 |  | 
| 80 80 | 
             
                remove_method :app_host=
         | 
| 81 81 | 
             
                def app_host=(url)
         | 
| 82 | 
            -
                   | 
| 82 | 
            +
                  unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
         | 
| 83 | 
            +
                    raise ArgumentError, "Capybara.app_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
         | 
| 84 | 
            +
                  end
         | 
| 83 85 |  | 
| 84 86 | 
             
                  @app_host = url
         | 
| 85 87 | 
             
                end
         | 
| 86 88 |  | 
| 87 89 | 
             
                remove_method :default_host=
         | 
| 88 90 | 
             
                def default_host=(url)
         | 
| 89 | 
            -
                   | 
| 91 | 
            +
                  unless url.nil? || url.match?(URI::DEFAULT_PARSER.make_regexp)
         | 
| 92 | 
            +
                    raise ArgumentError, "Capybara.default_host should be set to a url (http://www.example.com). Attempted to set #{url.inspect}."
         | 
| 93 | 
            +
                  end
         | 
| 90 94 |  | 
| 91 95 | 
             
                  @default_host = url
         | 
| 92 96 | 
             
                end
         | 
| @@ -20,7 +20,7 @@ module Capybara | |
| 20 20 | 
             
                # @return [true]
         | 
| 21 21 | 
             
                #
         | 
| 22 22 | 
             
                def assert_current_path(path, **options)
         | 
| 23 | 
            -
                  _verify_current_path(path, options) do |query|
         | 
| 23 | 
            +
                  _verify_current_path(path, **options) do |query|
         | 
| 24 24 | 
             
                    raise Capybara::ExpectationNotMet, query.failure_message unless query.resolves_for?(self)
         | 
| 25 25 | 
             
                  end
         | 
| 26 26 | 
             
                end
         | 
| @@ -36,7 +36,7 @@ module Capybara | |
| 36 36 | 
             
                # @return [true]
         | 
| 37 37 | 
             
                #
         | 
| 38 38 | 
             
                def assert_no_current_path(path, **options)
         | 
| 39 | 
            -
                  _verify_current_path(path, options) do |query|
         | 
| 39 | 
            +
                  _verify_current_path(path, **options) do |query|
         | 
| 40 40 | 
             
                    raise Capybara::ExpectationNotMet, query.negative_failure_message if query.resolves_for?(self)
         | 
| 41 41 | 
             
                  end
         | 
| 42 42 | 
             
                end
         | 
| @@ -51,7 +51,7 @@ module Capybara | |
| 51 51 | 
             
                # @return [Boolean]
         | 
| 52 52 | 
             
                #
         | 
| 53 53 | 
             
                def has_current_path?(path, **options)
         | 
| 54 | 
            -
                  make_predicate(options) { assert_current_path(path, options) }
         | 
| 54 | 
            +
                  make_predicate(options) { assert_current_path(path, **options) }
         | 
| 55 55 | 
             
                end
         | 
| 56 56 |  | 
| 57 57 | 
             
                ##
         | 
| @@ -64,13 +64,13 @@ module Capybara | |
| 64 64 | 
             
                # @return [Boolean]
         | 
| 65 65 | 
             
                #
         | 
| 66 66 | 
             
                def has_no_current_path?(path, **options)
         | 
| 67 | 
            -
                  make_predicate(options) { assert_no_current_path(path, options) }
         | 
| 67 | 
            +
                  make_predicate(options) { assert_no_current_path(path, **options) }
         | 
| 68 68 | 
             
                end
         | 
| 69 69 |  | 
| 70 70 | 
             
              private
         | 
| 71 71 |  | 
| 72 | 
            -
                def _verify_current_path(path, options)
         | 
| 73 | 
            -
                  query = Capybara::Queries::CurrentPathQuery.new(path, options)
         | 
| 72 | 
            +
                def _verify_current_path(path, **options)
         | 
| 73 | 
            +
                  query = Capybara::Queries::CurrentPathQuery.new(path, **options)
         | 
| 74 74 | 
             
                  document.synchronize(query.wait) do
         | 
| 75 75 | 
             
                    yield(query)
         | 
| 76 76 | 
             
                  end
         | 
| @@ -12,10 +12,21 @@ $(function() { | |
| 12 12 | 
             
              $('#drag_html5, #drag_html5_scroll').on('dragstart', function(ev){
         | 
| 13 13 | 
             
                ev.originalEvent.dataTransfer.setData("text", ev.target.id);
         | 
| 14 14 | 
             
              });
         | 
| 15 | 
            +
              $('#drag_html5, #drag_html5_scroll').on('dragend', function(ev){
         | 
| 16 | 
            +
                $(this).after('<div class="log">DragEnd with client position: ' + ev.clientX + ',' + ev.clientY)
         | 
| 17 | 
            +
              });
         | 
| 15 18 | 
             
              $('#drop_html5, #drop_html5_scroll').on('dragover', function(ev){
         | 
| 16 19 | 
             
                $(this).after('<div class="log">DragOver with client position: ' + ev.clientX + ',' + ev.clientY)
         | 
| 17 20 | 
             
                if ($(this).hasClass('drop')) { ev.preventDefault(); }
         | 
| 18 21 | 
             
              });
         | 
| 22 | 
            +
              $('#drop_html5, #drop_html5_scroll').on('dragleave', function(ev){
         | 
| 23 | 
            +
                $(this).after('<div class="log">DragLeave with client position: ' + ev.clientX + ',' + ev.clientY)
         | 
| 24 | 
            +
                if ($(this).hasClass('drop')) { ev.preventDefault(); }
         | 
| 25 | 
            +
              });
         | 
| 26 | 
            +
              $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
         | 
| 27 | 
            +
                $(this).after('<div class="log">Drop with client position: ' + ev.clientX + ',' + ev.clientY)
         | 
| 28 | 
            +
                if ($(this).hasClass('drop')) { ev.preventDefault(); }
         | 
| 29 | 
            +
              });
         | 
| 19 30 | 
             
              $('#drop_html5, #drop_html5_scroll').on('drop', function(ev){
         | 
| 20 31 | 
             
                ev.preventDefault();
         | 
| 21 32 | 
             
                var oev = ev.originalEvent;
         | 
| @@ -36,6 +36,13 @@ Capybara::SpecHelper.spec '#all' do | |
| 36 36 | 
             
                expect(@result).to include('Smith', 'John', 'John Smith')
         | 
| 37 37 | 
             
              end
         | 
| 38 38 |  | 
| 39 | 
            +
              it 'should allow reversing the order' do
         | 
| 40 | 
            +
                @session.visit('/form')
         | 
| 41 | 
            +
                fields = @session.all(:fillable_field, 'Name', exact: false).to_a
         | 
| 42 | 
            +
                reverse_fields = @session.all(:fillable_field, 'Name', order: :reverse, exact: false).to_a
         | 
| 43 | 
            +
                expect(fields).to eq(reverse_fields.reverse)
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 39 46 | 
             
              it 'should raise an error when given invalid options' do
         | 
| 40 47 | 
             
                expect { @session.all('//p', schmoo: 'foo') }.to raise_error(ArgumentError)
         | 
| 41 48 | 
             
              end
         | 
| @@ -128,6 +135,7 @@ Capybara::SpecHelper.spec '#all' do | |
| 128 135 | 
             
                  it 'should succeed when the number of elements founds matches the expectation' do
         | 
| 129 136 | 
             
                    expect { @session.all(:css, 'h1, p', count: 4) }.not_to raise_error
         | 
| 130 137 | 
             
                  end
         | 
| 138 | 
            +
             | 
| 131 139 | 
             
                  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
         | 
| 132 140 | 
             
                    expect { @session.all(:css, 'h1, p', count: 5) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 133 141 | 
             
                  end
         | 
| @@ -137,6 +145,7 @@ Capybara::SpecHelper.spec '#all' do | |
| 137 145 | 
             
                  it 'should succeed when the number of elements founds matches the expectation' do
         | 
| 138 146 | 
             
                    expect { @session.all(:css, 'h1, p', minimum: 0) }.not_to raise_error
         | 
| 139 147 | 
             
                  end
         | 
| 148 | 
            +
             | 
| 140 149 | 
             
                  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
         | 
| 141 150 | 
             
                    expect { @session.all(:css, 'h1, p', minimum: 5) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 142 151 | 
             
                  end
         | 
| @@ -146,6 +155,7 @@ Capybara::SpecHelper.spec '#all' do | |
| 146 155 | 
             
                  it 'should succeed when the number of elements founds matches the expectation' do
         | 
| 147 156 | 
             
                    expect { @session.all(:css, 'h1, p', maximum: 4) }.not_to raise_error
         | 
| 148 157 | 
             
                  end
         | 
| 158 | 
            +
             | 
| 149 159 | 
             
                  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
         | 
| 150 160 | 
             
                    expect { @session.all(:css, 'h1, p', maximum: 0) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 151 161 | 
             
                  end
         | 
| @@ -155,6 +165,7 @@ Capybara::SpecHelper.spec '#all' do | |
| 155 165 | 
             
                  it 'should succeed when the number of elements founds matches the expectation' do
         | 
| 156 166 | 
             
                    expect { @session.all(:css, 'h1, p', between: 2..7) }.not_to raise_error
         | 
| 157 167 | 
             
                  end
         | 
| 168 | 
            +
             | 
| 158 169 | 
             
                  it 'should raise ExpectationNotMet when the number of elements founds does not match the expectation' do
         | 
| 159 170 | 
             
                    expect { @session.all(:css, 'h1, p', between: 0..3) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 160 171 | 
             
                  end
         | 
| @@ -175,6 +186,7 @@ Capybara::SpecHelper.spec '#all' do | |
| 175 186 | 
             
                          between: 0..3 }
         | 
| 176 187 | 
             
                    expect { @session.all(:css, 'h1, p', o) }.not_to raise_error
         | 
| 177 188 | 
             
                  end
         | 
| 189 | 
            +
             | 
| 178 190 | 
             
                  context 'with no :count expectation' do
         | 
| 179 191 | 
             
                    it 'fails if :minimum is not met' do
         | 
| 180 192 | 
             
                      o = { minimum: 5,
         | 
| @@ -182,18 +194,21 @@ Capybara::SpecHelper.spec '#all' do | |
| 182 194 | 
             
                            between: 2..7 }
         | 
| 183 195 | 
             
                      expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 184 196 | 
             
                    end
         | 
| 197 | 
            +
             | 
| 185 198 | 
             
                    it 'fails if :maximum is not met' do
         | 
| 186 199 | 
             
                      o = { minimum: 0,
         | 
| 187 200 | 
             
                            maximum: 0,
         | 
| 188 201 | 
             
                            between: 2..7 }
         | 
| 189 202 | 
             
                      expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 190 203 | 
             
                    end
         | 
| 204 | 
            +
             | 
| 191 205 | 
             
                    it 'fails if :between is not met' do
         | 
| 192 206 | 
             
                      o = { minimum: 0,
         | 
| 193 207 | 
             
                            maximum: 4,
         | 
| 194 208 | 
             
                            between: 0..3 }
         | 
| 195 209 | 
             
                      expect { @session.all(:css, 'h1, p', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 196 210 | 
             
                    end
         | 
| 211 | 
            +
             | 
| 197 212 | 
             
                    it 'succeeds if all combineable expectations are met' do
         | 
| 198 213 | 
             
                      o = { minimum: 0,
         | 
| 199 214 | 
             
                            maximum: 4,
         | 
| @@ -20,6 +20,11 @@ Capybara::SpecHelper.spec '#ancestor' do | |
| 20 20 | 
             
                expect(el.ancestor('//div', text: "Ancestor\nAncestor\nAncestor")[:id]).to eq('ancestor3')
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 | 
            +
              it 'should find the closest ancestor' do
         | 
| 24 | 
            +
                el = @session.find(:css, '#child')
         | 
| 25 | 
            +
                expect(el.ancestor('.//div', order: :reverse, match: :first)[:id]).to eq('ancestor1')
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 23 28 | 
             
              it 'should raise an error if there are multiple matches' do
         | 
| 24 29 | 
             
                el = @session.find(:css, '#child')
         | 
| 25 30 | 
             
                expect { el.ancestor('//div') }.to raise_error(Capybara::Ambiguous)
         | 
| @@ -159,6 +159,7 @@ Capybara::SpecHelper.spec '#assert_text' do | |
| 159 159 | 
             
                        between: 0..4 }
         | 
| 160 160 | 
             
                  expect { @session.assert_text('Header', o) }.not_to raise_error
         | 
| 161 161 | 
             
                end
         | 
| 162 | 
            +
             | 
| 162 163 | 
             
                context 'with no :count expectation' do
         | 
| 163 164 | 
             
                  it 'fails if :minimum is not met' do
         | 
| 164 165 | 
             
                    o = { minimum: 6,
         | 
| @@ -166,18 +167,21 @@ Capybara::SpecHelper.spec '#assert_text' do | |
| 166 167 | 
             
                          between: 2..7 }
         | 
| 167 168 | 
             
                    expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 168 169 | 
             
                  end
         | 
| 170 | 
            +
             | 
| 169 171 | 
             
                  it 'fails if :maximum is not met' do
         | 
| 170 172 | 
             
                    o = { minimum: 0,
         | 
| 171 173 | 
             
                          maximum: 0,
         | 
| 172 174 | 
             
                          between: 2..7 }
         | 
| 173 175 | 
             
                    expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 174 176 | 
             
                  end
         | 
| 177 | 
            +
             | 
| 175 178 | 
             
                  it 'fails if :between is not met' do
         | 
| 176 179 | 
             
                    o = { minimum: 0,
         | 
| 177 180 | 
             
                          maximum: 5,
         | 
| 178 181 | 
             
                          between: 0..4 }
         | 
| 179 182 | 
             
                    expect { @session.assert_text('Header', o) }.to raise_error(Capybara::ExpectationNotMet)
         | 
| 180 183 | 
             
                  end
         | 
| 184 | 
            +
             | 
| 181 185 | 
             
                  it 'succeeds if all combineable expectations are met' do
         | 
| 182 186 | 
             
                    o = { minimum: 0,
         | 
| 183 187 | 
             
                          maximum: 5,
         | 
| @@ -186,6 +186,11 @@ Capybara::SpecHelper.spec '#click_button' do | |
| 186 186 | 
             
                  @session.click_button(name: 'form[awesome]')
         | 
| 187 187 | 
             
                  expect(extract_results(@session)['first_name']).to eq('John')
         | 
| 188 188 | 
             
                end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                it 'should submit by specific button name regex' do
         | 
| 191 | 
            +
                  @session.click_button(name: /form\[awes.*\]/)
         | 
| 192 | 
            +
                  expect(extract_results(@session)['first_name']).to eq('John')
         | 
| 193 | 
            +
                end
         | 
| 189 194 | 
             
              end
         | 
| 190 195 |  | 
| 191 196 | 
             
              context 'with fields associated with the form using the form attribute', requires: [:form_attribute] do
         | 
| @@ -275,14 +275,17 @@ Capybara::SpecHelper.spec '#find' do | |
| 275 275 | 
             
                      @session.find(:css, '.multiple', match: :one)
         | 
| 276 276 | 
             
                    end.to raise_error(Capybara::Ambiguous)
         | 
| 277 277 | 
             
                  end
         | 
| 278 | 
            +
             | 
| 278 279 | 
             
                  it 'raises an error even if there the match is exact and the others are inexact' do
         | 
| 279 280 | 
             
                    expect do
         | 
| 280 281 | 
             
                      @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], exact: false, match: :one)
         | 
| 281 282 | 
             
                    end.to raise_error(Capybara::Ambiguous)
         | 
| 282 283 | 
             
                  end
         | 
| 284 | 
            +
             | 
| 283 285 | 
             
                  it 'returns the element if there is only one' do
         | 
| 284 286 | 
             
                    expect(@session.find(:css, '.singular', match: :one).text).to eq('singular')
         | 
| 285 287 | 
             
                  end
         | 
| 288 | 
            +
             | 
| 286 289 | 
             
                  it 'raises an error if there is no match' do
         | 
| 287 290 | 
             
                    expect do
         | 
| 288 291 | 
             
                      @session.find(:css, '.does-not-exist', match: :one)
         | 
| @@ -294,6 +297,7 @@ Capybara::SpecHelper.spec '#find' do | |
| 294 297 | 
             
                  it 'returns the first matched element' do
         | 
| 295 298 | 
             
                    expect(@session.find(:css, '.multiple', match: :first).text).to eq('multiple one')
         | 
| 296 299 | 
             
                  end
         | 
| 300 | 
            +
             | 
| 297 301 | 
             
                  it 'raises an error if there is no match' do
         | 
| 298 302 | 
             
                    expect do
         | 
| 299 303 | 
             
                      @session.find(:css, '.does-not-exist', match: :first)
         | 
| @@ -308,19 +312,23 @@ Capybara::SpecHelper.spec '#find' do | |
| 308 312 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: false)
         | 
| 309 313 | 
             
                      end.to raise_error(Capybara::Ambiguous)
         | 
| 310 314 | 
             
                    end
         | 
| 315 | 
            +
             | 
| 311 316 | 
             
                    it 'finds a single exact match when there also are inexact matches' do
         | 
| 312 317 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: false)
         | 
| 313 318 | 
             
                      expect(result.text).to eq('almost singular')
         | 
| 314 319 | 
             
                    end
         | 
| 320 | 
            +
             | 
| 315 321 | 
             
                    it 'raises an error when there are multiple inexact matches' do
         | 
| 316 322 | 
             
                      expect do
         | 
| 317 323 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: false)
         | 
| 318 324 | 
             
                      end.to raise_error(Capybara::Ambiguous)
         | 
| 319 325 | 
             
                    end
         | 
| 326 | 
            +
             | 
| 320 327 | 
             
                    it 'finds a single inexact match' do
         | 
| 321 328 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: false)
         | 
| 322 329 | 
             
                      expect(result.text).to eq('almost singular but not quite')
         | 
| 323 330 | 
             
                    end
         | 
| 331 | 
            +
             | 
| 324 332 | 
             
                    it 'raises an error if there is no match' do
         | 
| 325 333 | 
             
                      expect do
         | 
| 326 334 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: false)
         | 
| @@ -334,20 +342,24 @@ Capybara::SpecHelper.spec '#find' do | |
| 334 342 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :smart, exact: true)
         | 
| 335 343 | 
             
                      end.to raise_error(Capybara::Ambiguous)
         | 
| 336 344 | 
             
                    end
         | 
| 345 | 
            +
             | 
| 337 346 | 
             
                    it 'finds a single exact match when there also are inexact matches' do
         | 
| 338 347 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :smart, exact: true)
         | 
| 339 348 | 
             
                      expect(result.text).to eq('almost singular')
         | 
| 340 349 | 
             
                    end
         | 
| 350 | 
            +
             | 
| 341 351 | 
             
                    it 'raises an error when there are multiple inexact matches' do
         | 
| 342 352 | 
             
                      expect do
         | 
| 343 353 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :smart, exact: true)
         | 
| 344 354 | 
             
                      end.to raise_error(Capybara::ElementNotFound)
         | 
| 345 355 | 
             
                    end
         | 
| 356 | 
            +
             | 
| 346 357 | 
             
                    it 'raises an error when there is a single inexact matches' do
         | 
| 347 358 | 
             
                      expect do
         | 
| 348 359 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :smart, exact: true)
         | 
| 349 360 | 
             
                      end.to raise_error(Capybara::ElementNotFound)
         | 
| 350 361 | 
             
                    end
         | 
| 362 | 
            +
             | 
| 351 363 | 
             
                    it 'raises an error if there is no match' do
         | 
| 352 364 | 
             
                      expect do
         | 
| 353 365 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :smart, exact: true)
         | 
| @@ -362,18 +374,22 @@ Capybara::SpecHelper.spec '#find' do | |
| 362 374 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: false)
         | 
| 363 375 | 
             
                      expect(result.text).to eq('multiple one')
         | 
| 364 376 | 
             
                    end
         | 
| 377 | 
            +
             | 
| 365 378 | 
             
                    it 'finds a single exact match when there also are inexact matches' do
         | 
| 366 379 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: false)
         | 
| 367 380 | 
             
                      expect(result.text).to eq('almost singular')
         | 
| 368 381 | 
             
                    end
         | 
| 382 | 
            +
             | 
| 369 383 | 
             
                    it 'picks the first one when there are multiple inexact matches' do
         | 
| 370 384 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: false)
         | 
| 371 385 | 
             
                      expect(result.text).to eq('almost singular but not quite')
         | 
| 372 386 | 
             
                    end
         | 
| 387 | 
            +
             | 
| 373 388 | 
             
                    it 'finds a single inexact match' do
         | 
| 374 389 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: false)
         | 
| 375 390 | 
             
                      expect(result.text).to eq('almost singular but not quite')
         | 
| 376 391 | 
             
                    end
         | 
| 392 | 
            +
             | 
| 377 393 | 
             
                    it 'raises an error if there is no match' do
         | 
| 378 394 | 
             
                      expect do
         | 
| 379 395 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: false)
         | 
| @@ -386,20 +402,24 @@ Capybara::SpecHelper.spec '#find' do | |
| 386 402 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('multiple')], match: :prefer_exact, exact: true)
         | 
| 387 403 | 
             
                      expect(result.text).to eq('multiple one')
         | 
| 388 404 | 
             
                    end
         | 
| 405 | 
            +
             | 
| 389 406 | 
             
                    it 'finds a single exact match when there also are inexact matches' do
         | 
| 390 407 | 
             
                      result = @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular')], match: :prefer_exact, exact: true)
         | 
| 391 408 | 
             
                      expect(result.text).to eq('almost singular')
         | 
| 392 409 | 
             
                    end
         | 
| 410 | 
            +
             | 
| 393 411 | 
             
                    it 'raises an error if there are multiple inexact matches' do
         | 
| 394 412 | 
             
                      expect do
         | 
| 395 413 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singul')], match: :prefer_exact, exact: true)
         | 
| 396 414 | 
             
                      end.to raise_error(Capybara::ElementNotFound)
         | 
| 397 415 | 
             
                    end
         | 
| 416 | 
            +
             | 
| 398 417 | 
             
                    it 'raises an error if there is a single inexact match' do
         | 
| 399 418 | 
             
                      expect do
         | 
| 400 419 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('almost_singular but')], match: :prefer_exact, exact: true)
         | 
| 401 420 | 
             
                      end.to raise_error(Capybara::ElementNotFound)
         | 
| 402 421 | 
             
                    end
         | 
| 422 | 
            +
             | 
| 403 423 | 
             
                    it 'raises an error if there is no match' do
         | 
| 404 424 | 
             
                      expect do
         | 
| 405 425 | 
             
                        @session.find(:xpath, XPath.descendant[XPath.attr(:class).is('does-not-exist')], match: :prefer_exact, exact: true)
         | 
| @@ -143,10 +143,56 @@ Capybara::SpecHelper.spec '#has_no_table?' do | |
| 143 143 | 
             
                 ])
         | 
| 144 144 | 
             
              end
         | 
| 145 145 |  | 
| 146 | 
            -
               | 
| 147 | 
            -
                 | 
| 148 | 
            -
                   | 
| 149 | 
            -
                     | 
| 150 | 
            -
             | 
| 146 | 
            +
              context 'using :with_cols' do
         | 
| 147 | 
            +
                it 'should consider a single column' do
         | 
| 148 | 
            +
                  expect(@session).to have_no_table('Vertical Headers', with_cols:
         | 
| 149 | 
            +
                    [
         | 
| 150 | 
            +
                      { 'First Name' => 'Joe' }
         | 
| 151 | 
            +
                    ])
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                it 'should be true even if the last column does exist' do
         | 
| 155 | 
            +
                  expect(@session).to have_no_table('Vertical Headers', with_cols:
         | 
| 156 | 
            +
                    [
         | 
| 157 | 
            +
                      {
         | 
| 158 | 
            +
                        'First Name' => 'What?',
         | 
| 159 | 
            +
                        'What?' => 'Walpole',
         | 
| 160 | 
            +
                        'City' => 'Oceanside' # This line makes the example fail
         | 
| 161 | 
            +
                      }
         | 
| 162 | 
            +
                    ])
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                it 'should be true if none of the columns exist' do
         | 
| 166 | 
            +
                  expect(@session).to have_no_table('Vertical Headers', with_cols:
         | 
| 167 | 
            +
                    [
         | 
| 168 | 
            +
                      {
         | 
| 169 | 
            +
                        'First Name' => 'What?',
         | 
| 170 | 
            +
                        'What?' => 'Walpole',
         | 
| 171 | 
            +
                        'City' => 'What?'
         | 
| 172 | 
            +
                      }
         | 
| 173 | 
            +
                    ])
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                it 'should be true if the first column does match' do
         | 
| 177 | 
            +
                  expect(@session).to have_no_table('Vertical Headers', with_cols:
         | 
| 178 | 
            +
                    [
         | 
| 179 | 
            +
                      {
         | 
| 180 | 
            +
                        'First Name' => 'Thomas',
         | 
| 181 | 
            +
                        'Last Name' => 'What',
         | 
| 182 | 
            +
                        'City' => 'What'
         | 
| 183 | 
            +
                      }
         | 
| 184 | 
            +
                    ])
         | 
| 185 | 
            +
                end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                it 'should be true if none of the columns match' do
         | 
| 188 | 
            +
                  expect(@session).to have_no_table('Vertical Headers', with_cols:
         | 
| 189 | 
            +
                    [
         | 
| 190 | 
            +
                      {
         | 
| 191 | 
            +
                        'First Name' => 'What',
         | 
| 192 | 
            +
                        'Last Name' => 'What',
         | 
| 193 | 
            +
                        'City' => 'What'
         | 
| 194 | 
            +
                      }
         | 
| 195 | 
            +
                    ])
         | 
| 196 | 
            +
                end
         | 
| 151 197 | 
             
              end
         | 
| 152 198 | 
             
            end
         | 
| @@ -111,6 +111,37 @@ Capybara::SpecHelper.spec '#has_text?' do | |
| 111 111 | 
             
                expect(@session).not_to have_text(/xxxxyzzz/)
         | 
| 112 112 | 
             
              end
         | 
| 113 113 |  | 
| 114 | 
            +
              context 'with object implementing to_s and to_hash' do
         | 
| 115 | 
            +
                it 'should work if the object is passed alone' do
         | 
| 116 | 
            +
                  with_to_hash = Class.new do
         | 
| 117 | 
            +
                    def to_s; '42' end
         | 
| 118 | 
            +
                    def to_hash; { value: 'Other hash' } end
         | 
| 119 | 
            +
                  end.new
         | 
| 120 | 
            +
                  @session.visit('/with_html')
         | 
| 121 | 
            +
                  expect(@session).to have_text(with_to_hash)
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                it 'should work if passed with empty options' do
         | 
| 125 | 
            +
                  with_to_hash = Class.new do
         | 
| 126 | 
            +
                    def to_s; '42' end
         | 
| 127 | 
            +
                    def to_hash; { value: 'Other hash' } end
         | 
| 128 | 
            +
                  end.new
         | 
| 129 | 
            +
                  @session.visit('/with_html')
         | 
| 130 | 
            +
                  expect(@session).to have_text(:visible, with_to_hash, {})
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                it 'should fail if passed without empty options' do
         | 
| 134 | 
            +
                  with_to_hash = Class.new do
         | 
| 135 | 
            +
                    def to_s; '42' end
         | 
| 136 | 
            +
                    def to_hash; { blah: 'Other hash' } end
         | 
| 137 | 
            +
                  end.new
         | 
| 138 | 
            +
                  @session.visit('/with_html')
         | 
| 139 | 
            +
                  expect do
         | 
| 140 | 
            +
                    expect(@session).to have_text(:visible, with_to_hash)
         | 
| 141 | 
            +
                  end.to raise_error(ArgumentError)
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
              end
         | 
| 144 | 
            +
             | 
| 114 145 | 
             
              context 'with exact: true option' do
         | 
| 115 146 | 
             
                it 'should be true if text matches exactly' do
         | 
| 116 147 | 
             
                  @session.visit('/with_html')
         | 
| @@ -493,6 +493,21 @@ Capybara::SpecHelper.spec 'node' do | |
| 493 493 | 
             
                    expect(@session).to have_css('div.log', text: /DragOver with client position: [1-9]\d*,[1-9]\d*/, count: 2)
         | 
| 494 494 | 
             
                  end
         | 
| 495 495 |  | 
| 496 | 
            +
                  it 'should preserve clientX/Y from last dragover event' do
         | 
| 497 | 
            +
                    @session.visit('/with_js')
         | 
| 498 | 
            +
                    element = @session.find('//div[@id="drag_html5"]')
         | 
| 499 | 
            +
                    target = @session.find('//div[@id="drop_html5"]')
         | 
| 500 | 
            +
                    element.drag_to(target)
         | 
| 501 | 
            +
             | 
| 502 | 
            +
                    # The first "DragOver" div is inserted by the last dragover event dispatched
         | 
| 503 | 
            +
                    drag_over_div = @session.find('//div[@class="log" and starts-with(text(), "DragOver")]', match: :first)
         | 
| 504 | 
            +
                    position = drag_over_div.text.sub('DragOver ', '')
         | 
| 505 | 
            +
             | 
| 506 | 
            +
                    expect(@session).to have_css('div.log', text: /DragLeave #{position}/, count: 1)
         | 
| 507 | 
            +
                    expect(@session).to have_css('div.log', text: /Drop #{position}/, count: 1)
         | 
| 508 | 
            +
                    expect(@session).to have_css('div.log', text: /DragEnd #{position}/, count: 1)
         | 
| 509 | 
            +
                  end
         | 
| 510 | 
            +
             | 
| 496 511 | 
             
                  it 'should not HTML5 drag and drop on a non HTML5 drop element' do
         | 
| 497 512 | 
             
                    @session.visit('/with_js')
         | 
| 498 513 | 
             
                    element = @session.find('//div[@id="drag_html5"]')
         |