Allow "select" of datalist values from input element
This commit is contained in:
parent
09924678e8
commit
da5e5aae98
|
@ -174,8 +174,42 @@ module Capybara
|
|||
#
|
||||
# @return [Capybara::Node::Element] The option element selected
|
||||
def select(value = nil, from: nil, **options)
|
||||
scope = from ? find(:select, from, options) : self
|
||||
scope.find(:option, value, options).select_option
|
||||
scope = if from
|
||||
synchronize(Capybara::Queries::BaseQuery.wait(options, session_options.default_max_wait_time)) do
|
||||
begin
|
||||
find(:select, from, options)
|
||||
rescue Capybara::ElementNotFound => select_error
|
||||
raise if %i[selected with_selected multiple].any? { |option| options.key?(option) }
|
||||
begin
|
||||
find(:datalist_input, from, options)
|
||||
rescue Capybara::ElementNotFound => dlinput_error
|
||||
raise Capybara::ElementNotFound, "#{select_error.message} and #{dlinput_error.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
self
|
||||
end
|
||||
|
||||
if scope.respond_to?(:tag_name) && scope.tag_name == "input"
|
||||
begin
|
||||
# TODO: this is a more efficient but won't work with non-JS drivers
|
||||
# datalist_options = session.evaluate_script('Array.prototype.slice.call((arguments[0].list||{}).options || []).filter(function(el){ return !el.disabled }).map(function(el){ return { "value": el.value, "label": el.label} })', scope)
|
||||
datalist_options = session.evaluate_script(DATALIST_OPTIONS_SCRIPT, scope)
|
||||
if (option = datalist_options.find { |o| o['value'] == value || o['label'] == value })
|
||||
scope.set(option["value"])
|
||||
else
|
||||
raise ::Capybara::ElementNotFound, "Unable to find datalist option \"#{value}\""
|
||||
end
|
||||
rescue ::Capybara::NotSupportedByDriverError
|
||||
# Implement for drivers that don't support JS
|
||||
datalist = find(:xpath, XPath.descendant(:datalist)[XPath.attr(:id) == scope[:list]], visible: false)
|
||||
option = datalist.find(:datalist_option, value, disabled: false)
|
||||
scope.set(option.value)
|
||||
end
|
||||
else
|
||||
scope.find(:option, value, options).select_option
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -291,6 +325,12 @@ module Capybara
|
|||
delete el.capybara_style_cache;
|
||||
}
|
||||
JS
|
||||
|
||||
DATALIST_OPTIONS_SCRIPT = <<-'JS'.freeze
|
||||
Array.prototype.slice.call((arguments[0].list||{}).options || []).
|
||||
filter(function(el){ return !el.disabled }).
|
||||
map(function(el){ return { "value": el.value, "label": el.label} })
|
||||
JS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -90,7 +90,7 @@ class Capybara::RackTest::Node < Capybara::Driver::Node
|
|||
return true if string_node.disabled?
|
||||
|
||||
if %w[option optgroup].include? tag_name
|
||||
find_xpath("parent::*[self::optgroup or self::select]")[0].disabled?
|
||||
find_xpath("parent::*[self::optgroup or self::select or self::datalist]")[0].disabled?
|
||||
else
|
||||
!find_xpath("parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]").empty?
|
||||
end
|
||||
|
|
|
@ -407,6 +407,34 @@ Capybara.add_selector(:select) do
|
|||
end
|
||||
end
|
||||
|
||||
Capybara.add_selector(:datalist_input) do
|
||||
label "input box with datalist completion"
|
||||
|
||||
xpath do |locator, **options|
|
||||
xpath = XPath.descendant(:input)[XPath.attr(:list)]
|
||||
locate_field(xpath, locator, options)
|
||||
end
|
||||
|
||||
filter_set(:_field, %i[disabled name placeholder])
|
||||
|
||||
filter(:options) do |node, options|
|
||||
actual = node.find("//datalist[@id=#{node[:list]}]", visible: :all).all(:datalist_option, wait: false).map(&:value)
|
||||
options.sort == actual.sort
|
||||
end
|
||||
|
||||
filter(:with_options) do |node, options|
|
||||
options.all? { |option| node.find("//datalist[@id=#{node[:list]}]", visible: :all).first(:datalist_option, option) }
|
||||
end
|
||||
|
||||
describe do |options: nil, with_options: nil, **opts|
|
||||
desc = "".dup
|
||||
desc << " with options #{options.inspect}" if options
|
||||
desc << " with at least options #{with_options.inspect}" if with_options
|
||||
desc << describe_all_expression_filters(opts)
|
||||
desc
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Find option elements
|
||||
|
@ -433,6 +461,25 @@ Capybara.add_selector(:option) do
|
|||
end
|
||||
end
|
||||
|
||||
Capybara.add_selector(:datalist_option) do
|
||||
label "datalist option"
|
||||
visible(:all)
|
||||
|
||||
xpath do |locator|
|
||||
xpath = XPath.descendant(:option)
|
||||
xpath = xpath[XPath.string.n.is(locator.to_s) | (XPath.attr(:value) == locator.to_s)] unless locator.nil?
|
||||
xpath
|
||||
end
|
||||
|
||||
filter(:disabled, :boolean) { |node, value| !(value ^ node.disabled?) }
|
||||
|
||||
describe do |**options|
|
||||
desc = "".dup
|
||||
desc << " that is#{' not' unless options[:disabled]} disabled" if options.key?(:disabled)
|
||||
desc
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Find file input elements
|
||||
|
|
|
@ -84,9 +84,35 @@ Capybara::SpecHelper.spec "#select" do
|
|||
expect(@session.find_field('Title').value).to eq('Miss')
|
||||
end
|
||||
|
||||
context "input with datalist" do
|
||||
it "should select an option" do
|
||||
@session.select("Audi", from: 'manufacturer')
|
||||
@session.click_button('awesome')
|
||||
expect(extract_results(@session)['manufacturer']).to eq('Audi')
|
||||
end
|
||||
|
||||
it "should not find an input without a datalist" do
|
||||
expect do
|
||||
@session.select("Thomas", from: 'form_first_name')
|
||||
end.to raise_error(/Unable to find visible input box with datalist completion "form_first_name" that is not disabled/)
|
||||
end
|
||||
|
||||
it "should not select an option that doesn't exist" do
|
||||
expect do
|
||||
@session.select("Tata", from: 'manufacturer')
|
||||
end.to raise_error(/Unable to find datalist option "Tata"/)
|
||||
end
|
||||
|
||||
it "should not select a disabled option" do
|
||||
expect do
|
||||
@session.select("Mercedes", from: 'manufacturer')
|
||||
end.to raise_error(/Unable to find datalist option "Mercedes"/)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a locator that doesn't exist" do
|
||||
it "should raise an error" do
|
||||
msg = "Unable to find visible select box \"does not exist\" that is not disabled"
|
||||
msg = /Unable to find visible select box "does not exist" that is not disabled/
|
||||
expect do
|
||||
@session.select('foo', from: 'does not exist')
|
||||
end.to raise_error(Capybara::ElementNotFound, msg)
|
||||
|
|
|
@ -197,6 +197,15 @@ New line after and before textarea tag
|
|||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="text" name="form[manufacturer]" list="manufacturers" id="manufacturer"/>
|
||||
<datalist id="manufacturers">
|
||||
<option value="Jaguar">J</option>
|
||||
<option value="Audi">
|
||||
<option value="Mercedes" disabled>
|
||||
</datalist>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="checkbox" name="form[valueless_checkbox]" id="valueless_checkbox" checked="checked"/>
|
||||
<label for="valueless_checkbox">Valueless Checkbox</label>
|
||||
|
|
Loading…
Reference in New Issue