mojotech/capybara-ui

View on GitHub
lib/capybara/ui/widgets/list.rb

Summary

Maintainability
A
0 mins
Test Coverage
C
79%
module Capybara
  module UI
    # Use a List when you want to treat repeating elements as a unit.
    #
    # === Usage
    #
    # Consider the following HTML:
    #
    #   <ul id="colors">
    #     <li>Red <span class="pt">Vermelho</span></li>
    #     <li>Green <span class="pt">Verde</span></li>
    #     <li>Blue <span class="pt">Azul</span></li>
    #   </ul>
    #
    # You can then define the following widget:
    #
    #   class Colors < Capybara::UI::List
    #     root '#colors'
    #     item 'li'
    #   end
    #
    # Now you'll be able to iterate over each item:
    #
    #   # prints:
    #   # Red Vermelho
    #   # Green Verde
    #   # Blue Azul
    #   widget(:colors).each do |e|
    #     puts e
    #   end
    #
    # This is the same as doing the following in Capybara:
    #
    #   all('#colors li').each do |e|
    #     puts e.text.strip
    #   end
    #
    # Note that, by default, the root selector of a List is +ul+ and the list
    # item selector is +li+. So you could wrap the +<ul>+ above simply by using
    # the following:
    #
    #   class Colors < Capybara::UI::List
    #   end
    #
    # ==== Narrowing items
    #
    # You can define the root selector for your list items using the ::item macro:
    #
    #   class PortugueseColors < Capybara::UI::List
    #     root '#colors
    #     item '.pt'
    #   end
    #
    # If you iterate over this list you get the following:
    #
    #   # prints:
    #   # Vermelho
    #   # Verde
    #   # Azul
    #   widget(:portuguese_colors).each do |e|
    #     puts e
    #   end
    #
    # You can make a list out of any repeating elements, as long as you can define
    # parent and child selectors.
    #
    #   <div id="not-a-list-colors">
    #     <div class="child">Red</div>
    #     <div class="child">Green</div>
    #     <div class="child">Blue</div>
    #   </div>
    #
    # You can define the following widget:
    #
    #   class NotAListColors < Capybara::UI::List
    #     root '#not-a-list-colors'
    #     item '.child'
    #   end
    class List < Widget
      include Enumerable

      def_delegators :items, :each, :first, :last

      root 'ul' unless filter?

      class << self
        # Configures the List item selector and class.
        #
        # === Usage
        #
        # Given the following HTML:
        #
        #   <ul>
        #     <li>One</li>
        #     <li>Two</li>
        #     <li>Three</li>
        #   </ul>
        #
        # In its most basic form, allows you to configure the list item selector,
        # using the default list item class (Capybara::UI::ListItem):
        #
        #   class Numbers < Capybara::UI::List
        #     root 'ul'
        #     item 'li'
        #   end
        #
        # ==== Extending the list item class
        #
        # You can define the list item class for the current List:
        #
        #   class Number < Capybara::UI::Widget
        #     # ...
        #   end
        #
        #   class Numbers < Capybara::UI::List
        #     root 'ul'
        #     item 'li', Number
        #   end
        #
        #   widget(:numbers).first.class < Number #=> true
        #
        # Alternatively, you can extend the list item type inline. This is useful
        # when you want to add small extensions to the default list item class.
        # The extensions will apply only to list items of the current List.
        #
        #   class Numbers < Capybara::UI::List
        #     root 'ul'
        #
        #     item 'li' do
        #       def upcase
        #         text.upcase
        #       end
        #     end
        #
        #     widget(:numbers).first.upcase #=> "ONE"
        #   end
        def item(selector, type = ListItem, &block)
          self.item_factory = WidgetClass.new(selector, type, &block)
        end

        attr_writer :item_factory

        def item_factory
          @item_factory ||= WidgetClass.new('li', ListItem)
        end
      end

      def count
        items.count
      end

      # TODO: Convert value to primitive data structures.
      def empty?
        items.empty?
      end

      def exclude?(element)
        ! include?(element)
      end

      def include?(element)
        value.include?(element)
      end

      def length
        items.length
      end

      def size
        items.size
      end

      def to_row
        items.map(&:to_cell)
      end

      def to_table
        items.map(&:to_row)
      end

      def value
        items.map(&:value)
      end

      protected

      def_delegator 'self.class', :item_factory

      def item_for(node)
        item_factory.new(node)
      end

      def item_filter
        item_factory.filter
      end

      def items
        Capybara.using_wait_time(0) do
          item_filter.nodes(self).map { |node| item_for(node) }
        end
      end
    end
  end
end