diff --git a/lib/capybara/node/actions.rb b/lib/capybara/node/actions.rb index 1c97ff3a..1d68dc54 100644 --- a/lib/capybara/node/actions.rb +++ b/lib/capybara/node/actions.rb @@ -71,8 +71,12 @@ module Capybara # @option options [String] :with The value to fill in - required # @option options [Hash] :fill_options Driver specific options regarding how to fill fields # @option options [Boolean] :multiple Match fields that can have multiple values? + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute + # @option options [String] placeholder Match fields that match the placeholder attribute # def fill_in(locator, options={}) + locator, options = nil, locator if locator.is_a? Hash raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with) with = options.delete(:with) fill_options = options.delete(:fill_options) @@ -93,6 +97,8 @@ module Capybara # @param [String] locator Which radio button to choose # # @option options [String] :option Value of the radio_button to choose + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute # @macro waiting_behavior # @macro click_label def choose(locator, options={}) @@ -125,6 +131,8 @@ module Capybara # @param [String] locator Which check box to check # # @option options [String] :option Value of the checkbox to select + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute # @macro click_label # @macro waiting_behavior # @@ -158,6 +166,8 @@ module Capybara # @param [String] locator Which check box to uncheck # # @option options [String] :option Value of the checkbox to deselect + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute # @macro click_label # @macro waiting_behavior # @@ -241,8 +251,11 @@ module Capybara # @option options [Symbol] match (Capybara.match) The matching strategy to use (:one, :first, :prefer_exact, :smart). # @option options [Boolean] exact (Capybara.exact) Match the exact label name/contents or accept a partial match. # @option options [Boolean] multiple Match field which allows multiple file selection + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute # def attach_file(locator, path, options={}) + locator, path, options = nil, locator, path if path.is_a? Hash Array(path).each do |p| raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s) end diff --git a/lib/capybara/node/finders.rb b/lib/capybara/node/finders.rb index ea231a27..a8e5ade8 100644 --- a/lib/capybara/node/finders.rb +++ b/lib/capybara/node/finders.rb @@ -67,6 +67,9 @@ module Capybara # @option options [String] with Value of field to match on # @option options [String] type Type of field to match on # @option options [Boolean] multiple Match fields that can have multiple values? + # @option options [String] id Match fields that match the id attribute + # @option options [String] name Match fields that match the name attribute + # @option options [String] placeholder Match fields that match the placeholder attribute # @return [Capybara::Node::Element] The found element # diff --git a/lib/capybara/selector.rb b/lib/capybara/selector.rb index f1c154b4..6c3b595d 100644 --- a/lib/capybara/selector.rb +++ b/lib/capybara/selector.rb @@ -80,10 +80,10 @@ module Capybara custom_filters[name] = Filter.new(name, block, options) end - def filter_set(name) + def filter_set(name, filters_to_use = nil) f_set = FilterSet.all[name] f_set.filters.each do | name, filter | - custom_filters[name] = filter + custom_filters[name] = filter if filters_to_use.nil? || filters_to_use.include?(name) end f_set.descriptions.each { |desc| @filter_set.describe &desc } end @@ -102,20 +102,6 @@ module Capybara locate_field += XPath.descendant(:label)[XPath.string.n.is(locator)].descendant(xpath) locate_field end - - FilterSet.add(:locate_field) do - filter(:id) { |node, id| node['id'] == id } - filter(:name) { |node, name| node['name'] == name } - filter(:placeholder) { |node, placeholder| node['placeholder'] == placeholder } - - describe do |options| - desc = String.new - [:id, :name, :placeholder].each do |opt| - desc << " with #{opt.to_s} #{options[opt]}" if options.has_key?(opt) - end - desc - end - end end end @@ -131,6 +117,30 @@ Capybara.add_selector(:id) do xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] } end +Capybara::Selector::FilterSet.add(:_field) do + filter(:id) { |node, id| node['id'] == id } + filter(:name) { |node, name| node['name'] == name } + filter(:placeholder) { |node, placeholder| node['placeholder'] == placeholder } + filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) } + filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) } + filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } + filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } + + describe do |options| + desc, states = String.new, [] + [:id, :name, :placeholder].each do |opt| + desc << " with #{opt.to_s} #{options[opt]}" if options.has_key?(opt) + end + states << 'checked' if options[:checked] || (options[:unchecked] === false) + states << 'not checked' if options[:unchecked] || (options[:checked] === false) + states << 'disabled' if options[:disabled] == true + desc << " that is #{states.join(' and ')}" unless states.empty? + desc << " with the multiple attribute" if options[:multiple] == true + desc << " without the multiple attribute" if options[:multiple] === false + desc + end +end + Capybara.add_selector(:field) do xpath do |locator| xpath = XPath.descendant(:input, :textarea, :select)[~XPath.attr(:type).one_of('submit', 'image', 'hidden')] @@ -138,11 +148,8 @@ Capybara.add_selector(:field) do xpath end - filter_set(:locate_field) + filter_set(:_field) - filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) } - filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) } - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } filter(:readonly, boolean: true) { |node, value| not(value ^ node[:readonly]) } filter(:with) { |node, with| node.value == with.to_s } filter(:type) do |node, type| @@ -152,17 +159,11 @@ Capybara.add_selector(:field) do node[:type] == type end end - filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } + # filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } describe do |options| desc, states = String.new, [] desc << " of type #{options[:type].inspect}" if options[:type] desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with) - states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked]) - states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked]) - states << 'disabled' if options[:disabled] == true - desc << " that is #{states.join(' and ')}" unless states.empty? - desc << " with the multiple attribute" if options[:multiple] == true - desc << " without the multiple attribute" if options[:multiple] === false desc end end @@ -226,7 +227,7 @@ Capybara.add_selector(:link_or_button) do self.class.all.values_at(:link, :button).map {|selector| selector.xpath.call(locator)}.reduce(:+) end - filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) } + filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) } describe { |options| " that is disabled" if options[:disabled] } end @@ -239,18 +240,7 @@ Capybara.add_selector(:fillable_field) do xpath end - filter_set(:locate_field) - - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } - filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } - - describe do |options| - desc = String.new - desc << " that is disabled" if options[:disabled] == true - desc << " with the multiple attribute" if options[:multiple] == true - desc << " without the multiple attribute" if options[:multiple] === false - desc - end + filter_set(:_field, [:id, :name, :placeholder, :disabled, :multiple]) end Capybara.add_selector(:radio_button) do @@ -261,20 +251,13 @@ Capybara.add_selector(:radio_button) do xpath end - filter_set(:locate_field) + filter_set(:_field, [:id, :name, :checked, :unchecked, :disabled]) - filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) } - filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) } filter(:option) { |node, value| node.value == value.to_s } - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } describe do |options| - desc, states = String.new, [] + desc = String.new desc << " with value #{options[:option].inspect}" if options[:option] - states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked]) - states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked]) - states << 'disabled' if options[:disabled] == true - desc << " that is #{states.join(' and ')}" unless states.empty? desc end end @@ -286,20 +269,13 @@ Capybara.add_selector(:checkbox) do xpath end - filter_set(:locate_field) + filter_set(:_field, [:id, :name, :checked, :unchecked, :disabled]) - filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) } - filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) } filter(:option) { |node, value| node.value == value.to_s } - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } describe do |options| - desc, states = String.new, [] + desc = String.new desc << " with value #{options[:option].inspect}" if options[:option] - states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked]) - states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked]) - states << 'disabled' if options[:disabled] == true - desc << " that is #{states.join(' and ')}" unless states.empty? desc end end @@ -312,7 +288,7 @@ Capybara.add_selector(:select) do xpath end - filter_set(:locate_field) + filter_set(:_field, [:id, :name, :placeholder, :disabled, :multiple]) filter(:options) do |node, options| if node.visible? @@ -333,17 +309,12 @@ Capybara.add_selector(:select) do actual = node.all(:xpath, './/option', visible: false).select { |option| option.selected? }.map { |option| option.text(:all) } [selected].flatten.sort == actual.sort end - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } - filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } describe do |options| desc = String.new desc << " with options #{options[:options].inspect}" if options[:options] desc << " with at least options #{options[:with_options].inspect}" if options[:with_options] desc << " with #{options[:selected].inspect} selected" if options[:selected] - desc << " that is disabled" if options[:disabled] == true - desc << " that allows multiple selection" if options[:multiple] == true - desc << " that only allows single selection" if options[:multiple] === false desc end end @@ -374,18 +345,7 @@ Capybara.add_selector(:file_field) do xpath end - filter_set(:locate_field) - - filter(:disabled, default: false, boolean: true, skip_if: :all) { |node, value| not(value ^ node.disabled?) } - filter(:multiple, boolean: true) { |node, value| !(value ^ node[:multiple]) } - - describe do |options| - desc = String.new - desc << " that is disabled" if options[:disabled] == true - desc << " that allows multiple files" if options[:multiple] == true - desc << " that only allows a single file" if options[:multiple] === false - desc - end + filter_set(:_field, [:id, :name, :disabled, :multiple]) end Capybara.add_selector(:label) do @@ -411,6 +371,7 @@ Capybara.add_selector(:label) do describe do |options| desc = String.new desc << " for #{options[:for]}" if options[:for] + desc end end