Allow check/uncheck/choose/attach_file/fill_in to work on element called on when no locator specified

This commit is contained in:
Thomas Walpole 2018-08-31 10:01:40 -07:00
parent fd5ef4fa03
commit e5620e8ae2
9 changed files with 103 additions and 20 deletions

View File

@ -62,6 +62,7 @@ module Capybara
#
# Locate a text field or text area and fill it in with the given text
# The field can be found via its name, id, Capybara.test_id attribute, or label text.
# If no locator is provided will operate on self or a descendant
#
# page.fill_in 'Name', with: 'Bob'
#
@ -82,6 +83,7 @@ module Capybara
# @return [Capybara::Node::Element] The element filled_in
def fill_in(locator = nil, with:, currently_with: nil, fill_options: {}, **find_options)
find_options[:with] = currently_with if currently_with
find_options[:allow_self] = true if locator.nil?
find(:fillable_field, locator, find_options).set(with, fill_options)
end
@ -90,8 +92,9 @@ module Capybara
##
#
# Find a radio button and mark it as checked. The radio button can be found
# via name, id or label text.
# Find a descendant radio button and mark it as checked. The radio button can be found
# via name, id or label text. If no locator is provided this will match against self or
# a descendant.
#
# page.choose('Male')
#
@ -112,8 +115,9 @@ module Capybara
##
#
# Find a check box and mark it as checked. The check box can be found
# via name, id or label text.
# Find a descendant check box and mark it as checked. The check box can be found
# via name, id or label text. If no locator is provided this will match against
# self or a descendant.
#
# page.check('German')
#
@ -135,8 +139,9 @@ module Capybara
##
#
# Find a check box and mark uncheck it. The check box can be found
# via name, id or label text.
# Find a descendant check box and mark uncheck it. The check box can be found
# via name, id or label text. If no locator is provided this will match against
# self or a descendant.
#
# page.uncheck('German')
#
@ -204,10 +209,11 @@ module Capybara
##
#
# Find a file field on the page and attach a file given its path. The file field can
# Find a descendant file field on the page and attach a file given its path. The file field can
# be found via its name, id or label text. In the case of the file field being hidden for
# styling reasons the `make_visible` option can be used to temporarily change the CSS of
# the file field, attach the file, and then revert the CSS back to original.
# the file field, attach the file, and then revert the CSS back to original. If no locator is
# passed this will match self or a descendant.
#
# page.attach_file(locator, '/path/to/file.png')
#
@ -230,6 +236,7 @@ module Capybara
Array(paths).each do |path|
raise Capybara::FileNotFound, "cannot attach file, #{path} does not exist" unless File.exist?(path.to_s)
end
options[:allow_self] = true if locator.nil?
# Allow user to update the CSS style of the file input since they are so often hidden on a page
if make_visible
ff = find(:file_field, locator, options.merge(visible: :all))
@ -291,6 +298,8 @@ module Capybara
end
def _check_with_label(selector, checked, locator, allow_label_click: session_options.automatic_label_click, **options)
options[:allow_self] = true if locator.nil?
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
begin
el = find(selector, locator, options)
@ -299,7 +308,7 @@ module Capybara
raise unless allow_label_click && catch_error?(err)
begin
el ||= find(selector, locator, options.merge(visible: :all))
find(:label, for: el, visible: true).click unless el.checked? == checked
el.session.find(:label, for: el, visible: true).click unless el.checked? == checked
rescue StandardError # swallow extra errors - raise original
raise err
end

View File

@ -58,6 +58,7 @@ module Capybara
end
def matches_filters?(node)
return true if (@resolved_node&.== node) && options[:allow_self]
@applied_filters ||= :system
return false unless matches_text_filter?(node) && matches_exact_text_filter?(node) && matches_visible_filter?(node)
@applied_filters = :node
@ -210,7 +211,7 @@ module Capybara
return if unhandled_options.empty?
invalid_names = unhandled_options.map(&:inspect).join(', ')
valid_names = valid_keys.map(&:inspect).join(', ')
valid_names = (valid_keys - [:allow_self]).map(&:inspect).join(', ')
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
end

View File

@ -194,8 +194,12 @@ end
Capybara.add_selector(:fillable_field) do
label 'field'
xpath do |locator, **options|
xpath = XPath.descendant(:input, :textarea)[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
xpath(:allow_self) do |locator, **options|
xpath = if options[:allow_self]
XPath.descendant_or_self(:input, :textarea)
else
XPath.descendant(:input, :textarea)
end[!XPath.attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
locate_field(xpath, locator, options)
end
@ -223,8 +227,12 @@ end
Capybara.add_selector(:radio_button) do
label 'radio button'
xpath do |locator, **options|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'radio']
xpath(:allow_self) do |locator, **options|
xpath = if options[:allow_self]
XPath.descendant_or_self(:input)
else
XPath.descendant(:input)
end[XPath.attr(:type) == 'radio']
locate_field(xpath, locator, options)
end
@ -239,8 +247,12 @@ Capybara.add_selector(:radio_button) do
end
Capybara.add_selector(:checkbox) do
xpath do |locator, **options|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'checkbox']
xpath(:allow_self) do |locator, **options|
xpath = if options[:allow_self]
XPath.descendant_or_self(:input)
else
XPath.descendant(:input)
end[XPath.attr(:type) == 'checkbox']
locate_field(xpath, locator, options)
end
@ -375,8 +387,12 @@ end
Capybara.add_selector(:file_field) do
label 'file field'
xpath do |locator, options|
xpath = XPath.descendant(:input)[XPath.attr(:type) == 'file']
xpath(:allow_self) do |locator, options|
xpath = if options[:allow_self]
XPath.descendant_or_self(:input)
else
XPath.descendant(:input)
end[XPath.attr(:type) == 'file']
locate_field(xpath, locator, options)
end

View File

@ -27,7 +27,7 @@ class Capybara::Selenium::MarionetteNode < Capybara::Selenium::Node
def set_file(value) # rubocop:disable Naming/AccessorMethodName
# By default files are appended so we have to clear here if its multiple and already set
native.clear if multiple? && driver.evaluate_script("arguments[0].files", self).any?
native.clear if multiple? && driver.evaluate_script('arguments[0].files', self).any?
return super if browser_version >= 62.0
# Workaround lack of support for multiple upload by uploading one at a time

View File

@ -22,6 +22,13 @@ Capybara::SpecHelper.spec '#attach_file' do
expect(extract_results(@session)['image']).to eq(File.basename(__FILE__))
end
it 'should be able to set on element if no locator passed' do
ff = @session.find(:file_field, 'Image')
ff.attach_file(with_os_path_separators(__FILE__))
@session.click_button('awesome')
expect(extract_results(@session)['image']).to eq(File.basename(__FILE__))
end
it 'casts to string' do
@session.attach_file :form_image, with_os_path_separators(__FILE__)
@session.click_button('awesome')

View File

@ -68,6 +68,13 @@ Capybara::SpecHelper.spec '#check' do
expect(extract_results(@session)['pets']).to include('dog', 'cat', 'hamster')
end
it 'should be able to check itself if no locator specified' do
cb = @session.find(:id, 'form_pets_cat')
cb.check
@session.click_button('awesome')
expect(extract_results(@session)['pets']).to include('dog', 'cat', 'hamster')
end
it 'casts to string' do
@session.check(:form_pets_cat)
@session.click_button('awesome')
@ -142,6 +149,20 @@ Capybara::SpecHelper.spec '#check' do
expect(extract_results(@session)['cars']).to include('mclaren')
end
it 'should check via clicking the label with :for attribute if locator nil' do
cb = @session.find(:checkbox, 'form_cars_tesla', unchecked: true, visible: :hidden)
cb.check
@session.click_button('awesome')
expect(extract_results(@session)['cars']).to include('tesla')
end
it 'should check self via clicking the wrapping label if locator nil' do
cb = @session.find(:checkbox, 'form_cars_mclaren', unchecked: true, visible: :hidden)
cb.check
@session.click_button('awesome')
expect(extract_results(@session)['cars']).to include('mclaren')
end
it 'should not click the label if unneeded' do
expect(@session.find(:checkbox, 'form_cars_jaguar', checked: true, visible: :hidden)).to be_truthy
@session.check('form_cars_jaguar')

View File

@ -23,6 +23,13 @@ Capybara::SpecHelper.spec '#choose' do
expect(extract_results(@session)['gender']).to eq('male')
end
it 'should be able to choose self when no locator string specified' do
rb = @session.find(:id, 'gender_male')
rb.choose
@session.click_button('awesome')
expect(extract_results(@session)['gender']).to eq('male')
end
it 'casts to string' do
@session.choose('Both')
@session.click_button(:awesome)
@ -82,12 +89,19 @@ Capybara::SpecHelper.spec '#choose' do
Capybara.automatic_label_click = old_click_label
end
it 'should select by clicking the link if available' do
it 'should select by clicking the label if available' do
@session.choose('party_democrat')
@session.click_button('awesome')
expect(extract_results(@session)['party']).to eq('democrat')
end
it 'should select self by clicking the label if no locator specified' do
cb = @session.find(:id, 'party_democrat', visible: :hidden)
cb.choose
@session.click_button('awesome')
expect(extract_results(@session)['party']).to eq('democrat')
end
it 'should raise error if not allowed to click label' do
expect { @session.choose('party_democrat', allow_label_click: false) }.to raise_error(Capybara::ElementNotFound, 'Unable to find visible radio button "party_democrat"')
end

View File

@ -137,6 +137,13 @@ Capybara::SpecHelper.spec '#fill_in' do
expect(extract_results(@session)['schmooo']).to eq('Schmooo for all')
end
it 'should be able to fill in element called on when no locator passed' do
field = @session.find(:fillable_field, 'form[password]')
field.fill_in(with: 'supasikrit')
@session.click_button('awesome')
expect(extract_results(@session)['password']).to eq('supasikrit')
end
it "should throw an exception if a hash containing 'with' is not provided" do
expect { @session.fill_in 'Name' }.to raise_error(ArgumentError, /with/)
end

View File

@ -26,6 +26,14 @@ Capybara::SpecHelper.spec '#uncheck' do
expect(extract_results(@session)['pets']).not_to include('hamster')
end
it 'should be able to uncheck itself if no locator specified' do
cb = @session.find(:id, 'form_pets_hamster')
cb.uncheck
@session.click_button('awesome')
expect(extract_results(@session)['pets']).to include('dog')
expect(extract_results(@session)['pets']).not_to include('hamster')
end
it 'casts to string' do
@session.uncheck(:form_pets_hamster)
@session.click_button('awesome')