diff --git a/lib/capybara/minitest.rb b/lib/capybara/minitest.rb index 2d7687395..5f39bad80 100644 --- a/lib/capybara/minitest.rb +++ b/lib/capybara/minitest.rb @@ -190,6 +190,19 @@ def assert_#{assertion_name} *args, &optional_filter_block # @!method assert_no_css # See {Capybara::Node::Matchers#has_no_css?} + ## + # Assert that provided element exists + # + # @!method assert_element + # See {Capybara::Node::Matchers#has_element?} + + ## + # Assert that provided element does not exist + # + # @!method assert_no_element + # @!method refute_element + # See {Capybara::Node::Matchers#has_no_element?} + ## # Assert that provided link exists # @@ -281,7 +294,7 @@ def assert_#{assertion_name} *args, &optional_filter_block # @!method assert_no_table # See {Capybara::Node::Matchers#has_no_table?} - %w[xpath css link button field select table].each do |selector_type| + %w[xpath css element link button field select table].each do |selector_type| define_method "assert_#{selector_type}" do |*args, &optional_filter_block| subject, args = determine_subject(args) locator, options = extract_locator(args) diff --git a/lib/capybara/minitest/spec.rb b/lib/capybara/minitest/spec.rb index 6454a52b6..e60e91401 100644 --- a/lib/capybara/minitest/spec.rb +++ b/lib/capybara/minitest/spec.rb @@ -95,6 +95,18 @@ module Expectations # @!method wont_have_field # See {Capybara::Node::Matchers#has_no_field?} + ## + # Expectation that there is element + # + # @!method must_have_element + # See {Capybara::Node::Matchers#has_element?} + + ## + # Expectation that there is no element + # + # @!method wont_have_element + # See {Capybara::Node::Matchers#has_no_element?} + ## # Expectation that there is link # diff --git a/lib/capybara/node/matchers.rb b/lib/capybara/node/matchers.rb index 8eeeb19e3..9d970fddb 100644 --- a/lib/capybara/node/matchers.rb +++ b/lib/capybara/node/matchers.rb @@ -322,6 +322,31 @@ def has_no_css?(path, **options, &optional_filter_block) has_no_selector?(:css, path, **options, &optional_filter_block) end + ## + # + # Checks if the page or current node has a element with the given + # local name. + # + # @param [String] locator The local name of a element to check for + # @option options [String, Regexp] The attributes values of matching elements + # @return [Boolean] Whether it exists + # + def has_element?(locator = nil, **options, &optional_filter_block) + has_selector?(:element, locator, **options, &optional_filter_block) + end + + ## + # + # Checks if the page or current node has no element with the given + # local name. + # + # @param (see #has_element?) + # @return [Boolean] Whether it doesn't exist + # + def has_no_element?(locator = nil, **options, &optional_filter_block) + has_no_selector?(:element, locator, **options, &optional_filter_block) + end + ## # # Checks if the page or current node has a link with the given diff --git a/lib/capybara/rspec/matchers.rb b/lib/capybara/rspec/matchers.rb index 2e1379914..8d9b10898 100644 --- a/lib/capybara/rspec/matchers.rb +++ b/lib/capybara/rspec/matchers.rb @@ -83,6 +83,11 @@ def match_selector(...) end end + # @!method have_element(locator = nil, **options, &optional_filter_block) + # RSpec matcher for elements. + # + # @see Capybara::Node::Matchers#has_element? + # @!method have_link(locator = nil, **options, &optional_filter_block) # RSpec matcher for links. # diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index 14097bd77..1451a91af 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -45,6 +45,7 @@ class Session fill_in find find_all find_button find_by_id find_field find_link has_content? has_text? has_css? has_no_content? has_no_text? has_no_css? has_no_xpath? has_xpath? select uncheck + has_element? has_no_element? has_link? has_no_link? has_button? has_no_button? has_field? has_no_field? has_checked_field? has_unchecked_field? has_no_table? has_table? unselect has_select? has_no_select? diff --git a/lib/capybara/spec/session/has_element_spec.rb b/lib/capybara/spec/session/has_element_spec.rb new file mode 100644 index 000000000..68496843e --- /dev/null +++ b/lib/capybara/spec/session/has_element_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +Capybara::SpecHelper.spec '#has_element?' do + before do + @session.visit('/with_html') + end + + it 'should be true if the given element is on the page' do + expect(@session).to have_element('a', id: 'foo') + expect(@session).to have_element('a', text: 'A link', href: '/with_simple_html') + expect(@session).to have_element('a', text: :'A link', href: :'/with_simple_html') + expect(@session).to have_element('a', text: 'A link', href: %r{/with_simple_html}) + expect(@session).to have_element('a', text: 'labore', target: '_self') + end + + it 'should be false if the given element is not on the page' do + expect(@session).not_to have_element('a', text: 'monkey') + expect(@session).not_to have_element('a', text: 'A link', href: '/nonexistent-href') + expect(@session).not_to have_element('a', text: 'A link', href: /nonexistent/) + expect(@session).not_to have_element('a', text: 'labore', target: '_blank') + end + + it 'should notify if an invalid locator is specified' do + allow(Capybara::Helpers).to receive(:warn).and_return(nil) + @session.has_element?(@session) + expect(Capybara::Helpers).to have_received(:warn).with(/Called from: .+/) + end +end + +Capybara::SpecHelper.spec '#has_no_element?' do + before do + @session.visit('/with_html') + end + + it 'should be false if the given element is on the page' do + expect(@session).not_to have_no_element('a', id: 'foo') + expect(@session).not_to have_no_element('a', text: 'A link', href: '/with_simple_html') + expect(@session).not_to have_no_element('a', text: 'labore', target: '_self') + end + + it 'should be true if the given element is not on the page' do + expect(@session).to have_no_element('a', text: 'monkey') + expect(@session).to have_no_element('a', text: 'A link', href: '/nonexistent-href') + expect(@session).to have_no_element('a', text: 'A link', href: %r{/nonexistent-href}) + expect(@session).to have_no_element('a', text: 'labore', target: '_blank') + end +end diff --git a/spec/minitest_spec.rb b/spec/minitest_spec.rb index 7a836ca9c..69dd2d77f 100644 --- a/spec/minitest_spec.rb +++ b/spec/minitest_spec.rb @@ -60,6 +60,13 @@ def test_assert_selector refute_selector(:css, 'select#not_form_title') end + def test_assert_element + visit('/with_html') + assert_element('a', text: 'A link') + assert_element(count: 1) { |el| el.text == 'A link' } + assert_no_element(text: 'Not on page') + end + def test_assert_link visit('/with_html') assert_link('A link') @@ -163,6 +170,6 @@ def test_assert_sibling reporter.start MinitestTest.run reporter, {} reporter.report - expect(output.string).to include('22 runs, 53 assertions, 0 failures, 0 errors, 1 skips') + expect(output.string).to include('23 runs, 56 assertions, 0 failures, 0 errors, 1 skips') end end diff --git a/spec/rspec/shared_spec_matchers.rb b/spec/rspec/shared_spec_matchers.rb index c00b69ed0..f56895848 100644 --- a/spec/rspec/shared_spec_matchers.rb +++ b/spec/rspec/shared_spec_matchers.rb @@ -503,6 +503,30 @@ end end + describe 'have_element matcher' do + let(:html) { 'a JPEGa PNG' } + + it 'gives proper description' do + expect(have_element('img').description).to eq('have element "img"') + end + + it 'passes if there is such a element' do + expect(html).to have_element('img', src: '/img.jpg') + end + + it 'fails if there is no such element' do + expect do + expect(html).to have_element('photo') + end.to raise_error(/expected to find element "photo"/) + end + + it 'supports compounding' do + expect(html).to have_element('img', alt: 'a JPEG').and have_element('img', src: '/img.png') + expect(html).to have_element('photo').or have_element('img', src: '/img.jpg') + expect(html).to have_no_element('photo').and have_element('img', alt: 'a PNG') + end + end + describe 'have_link matcher' do let(:html) { 'Just a linkAnother link' }