Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit ef39670c5877470dd593d1ab8ecc5f119558ee8f Andy Waite committed Mar 18, 2012
7 Gemfile
@@ -0,0 +1,7 @@
+# A sample Gemfile
+source "https://rubygems.org"
+
+gem 'capybara'
+gem 'rspec'
+gem 'relish'
+gem 'cucumber'
63 Gemfile.lock
@@ -0,0 +1,63 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ archive-tar-minitar (0.5.2)
+ builder (3.0.0)
+ capybara (1.1.2)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (~> 2.0)
+ xpath (~> 0.1.4)
+ childprocess (0.3.1)
+ ffi (~> 1.0.6)
+ cucumber (1.1.9)
+ builder (>= 2.1.2)
+ diff-lcs (>= 1.1.2)
+ gherkin (~> 2.9.0)
+ json (>= 1.4.6)
+ term-ansicolor (>= 1.0.6)
+ diff-lcs (1.1.3)
+ ffi (1.0.11)
+ gherkin (2.9.0)
+ json (>= 1.4.6)
+ json (1.6.1)
+ mime-types (1.17.2)
+ multi_json (1.0.3)
+ nokogiri (1.5.2)
+ rack (1.4.1)
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ relish (0.5.3)
+ archive-tar-minitar (>= 0.5.2)
+ json (>= 1.4.6)
+ rest-client (>= 1.6.1)
+ rest-client (1.6.7)
+ mime-types (>= 1.16)
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.8.0)
+ rubyzip (0.9.6.1)
+ selenium-webdriver (2.20.0)
+ childprocess (>= 0.2.5)
+ ffi (~> 1.0)
+ multi_json (~> 1.0)
+ rubyzip
+ term-ansicolor (1.0.7)
+ xpath (0.1.4)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ capybara
+ cucumber
+ relish
+ rspec
68 auto_page_object.rb
@@ -0,0 +1,68 @@
+module CapybaraExtensions
+ def data
+ top_level.merge collections(self)
+ end
+
+ private
+
+ def top_level
+ matches = all('body > *')
+ result = {}
+ matches.each do |match|
+ result.merge! data_attributes(match)
+ end
+ result
+ end
+
+ def collections(node)
+ collection_nodes = node.all('*[@data-collection]')
+ collections = {}
+ collection_nodes.each do |collection_node|
+ collection_name = collection_node['data-collection'].to_sym
+ members = []
+ node.all("*[data-collection='#{collection_name}'] > *").each do |child|
+ members << data_attributes(child)
+ end
+ collections[collection_name] = members
+ end
+ collections
+ end
+
+end
+
+def data_attributes(node)
+ result = {}
+ name = nil
+ node.native.attributes.each_pair do |key, v|
+ next unless key.start_with?('data-')
+ next if key == 'data-collection'
+ if key == 'data-name'
+ name = node.text.strip
+ else
+ sym_key = key.gsub('data-', '').gsub('-', '_').to_sym
+ if v.value != ""
+ result[sym_key] = v.value
+ else
+ result[sym_key] = node.text
+ end
+ end
+ end
+ if name
+ if result == {}
+ name
+ else
+ {name => result}
+ end
+ else
+ result
+ end
+end
+
+# TODO not sure if including in both modules if the best approach
+module Capybara
+ module Node
+ module Finders
+ include CapybaraExtensions
+ end
+ end
+end
16 auto_page_object_spec.rb
@@ -0,0 +1,16 @@
+require 'capybara'
+require 'rspec'
+require_relative 'auto_page_object'
+
+describe "Capybara#data" do
+
+ # context "single tag with preceding text" do
+ # let(:html) { 'Hello <span data-closing-time>6pm</span>!' }
+ #
+ # it do
+ # Capybara.string(html).data.should == {:closing_time => '6pm'}
+ # end
+ # end
+
+
+end
1 features/README.markdown
@@ -0,0 +1 @@
+This project is an experiment in auto generating page object models by annotating HTML markup with some HTML5 data attributes.
114 features/collections.feature
@@ -0,0 +1,114 @@
+Feature: Collections
+
+ Tags annotated with the special 'data-collection' attribute, such as tables and lists, will be treated as
+ collections.
+
+ It's assumed that the member elements of that collection are the direct child descendants of the collection tag.
+
+ Scenario: A list
+
+ Given the following HTML fragment:
+ """html
+ <ul data-collection="products">
+ <li data-condition="new">Car</span>
+ <li data-condition="used">Boat</span>
+ </ul>
+ """
+ Then the page should have the representation:
+ """ruby
+ {:products => [{:condition => 'new'}, {:condition => 'used'}]}
+ """
+
+ Scenario: A table
+
+ Given the following HTML fragment:
+ """html
+ <table data-collection="products">
+ <tr data-condition="new">
+ <td>Car</td>
+ </tr>
+ <tr data-condition="used">
+ <td>Boat</td>
+ </tr>
+ </table>
+ """
+ Then the page should have the representation:
+ """ruby
+ {:products => [{:condition => 'new'}, {:condition => 'used'}]}
+ """
+
+ Scenario: A list, with names specified
+
+ The special 'data-name' attribute is used as the key for the returned set.
+
+ Given the following HTML fragment:
+ """html
+ <ul data-collection="products">
+ <li data-condition="new" data-name>Car</span>
+ <li data-condition="used" data-name>Boat</span>
+ </ul>
+ """
+ Then the page should have the representation:
+ """ruby
+ {
+ :products => [
+ {"Car" => {
+ :condition => "new"}
+ },
+ {"Boat" => {
+ :condition => "used"}
+ }
+ ]
+ }
+ """
+
+ Scenario: single collection with name indicators but no data attributes
+
+ Given the following HTML fragment:
+ """html
+ <ul data-collection="products">
+ <li data-name>Car</span>
+ <li data-name>Boat</span>
+ </ul>
+ """
+ Then the page should have the representation:
+ """ruby
+ {
+ :products => ['Car', 'Boat']
+ }
+ """
+
+ Scenario: Multiple collections
+
+ Given the following HTML fragment:
+ """html
+ <ul data-collection="products">
+ <li data-condition="new" data-name>Car</span>
+ <li data-condition="used" data-name>Boat</span>
+ </ul>
+ <ul data-collection="services">
+ <li data-available="yes" data-name>Wash</span>
+ <li data-available="no" data-name>Engine Inspection</span>
+ </ul>
+ """
+ Then the page should have the representation:
+ """ruby
+ {
+ :products => [
+ {"Car" => {
+ :condition => "new"}
+ },
+ {"Boat" => {
+ :condition => "used"}
+ }
+ ],
+ :services => [
+ {"Wash" => {
+ :available => "yes"}
+ },
+ {"Engine Inspection" => {
+ :available => "no"}
+ }
+ ]
+ }
+ """
55 features/individual.feature
@@ -0,0 +1,55 @@
+Feature: Individual
+
+ Scenario: Multiple data attributes
+
+ Any 'valueless' data attribute will be used as the key for the data in the return set
+
+ Given the following HTML fragment:
+ """html
+ <span data-opening-time class="foo">9am</span>
+ <span data-closing-time class="bar">6pm</span>
+ """
+ Then the page should have the representation:
+ """ruby
+ {
+ :opening_time => '9am',
+ :closing_time => '6pm'
+ }
+ """
+
+ Scenario: Additional inline data attributes
+
+ Sometimes it can be useful to attach additional data attributes to a tag.
+
+ Given the following HTML fragment:
+ """html
+ <span data-product-name data-sku="12345">6pm</span>
+ """
+ Then the page should have the representation:
+ """ruby
+ {:product_name => '6pm', :sku => '12345'}
+ """
+
+ Scenario: Empty HTML fragment
+
+ Given an empty HTML fragment
+ Then the page should have the representation:
+ """ruby
+ {}
+ """
+
+ Scenario: Element fragment with no data attributes
+
+ Any context not annotated with data attributes will be ignored.
+
+ Given the following HTML fragment:
+ """html
+ <ul>
+ <li>Car</span>
+ <li>Boat</span>
+ </ul>
+ """
+ Then the page should have the representation:
+ """ruby
+ {}
+ """
12 features/step_definitions/steps.rb
@@ -0,0 +1,12 @@
+Given /^the following HTML fragment:$/ do |string|
+ @html = string
+end
+
+Given /^an empty HTML fragment$/ do
+ @html = ''
+end
+
+Then /^the page should have the representation:$/ do |string|
+ expected = eval(string)
+ Capybara.string(@html).data.should == expected
+end
2 features/support/env.rb
@@ -0,0 +1,2 @@
+require 'capybara'
+require_relative '../../auto_page_object.rb'
20 tree.rb
@@ -0,0 +1,20 @@
+require 'nokogiri'
+
+html = <<END
+<span data-age="50" data-location="London" class="highlight">Joe Bloggs</span>
+
+<span data-age="70" data-location="London" class="highlight">John Smith</span>
+END
+
+#doc = Nokogiri(html)
+
+matches = Nokogiri(html).css('span:regex_attrs("^data-.*")', Class.new {
+ def regex_attrs node_set, regex
+ node_set.find_all { |node| node.attributes.keys.any? {|k| k =~ /#{regex}/ } }
+ end
+}.new)
+
+matches.each do |match|
+ p match
+ puts
+end

0 comments on commit ef39670

Please sign in to comment.
Something went wrong with that request. Please try again.