Permalink
Browse files

initial corner_stones import

  • Loading branch information...
0 parents commit c51110890cea87638a1fd592816ac0a5506d37c9 @senny committed Feb 19, 2012
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
1 .rvmrc
@@ -0,0 +1 @@
+rvm --create ruby-1.9.3@corner_stones
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in corner_stones.gemspec
+gemspec
@@ -0,0 +1,82 @@
+# Corner Stones
+
+assists you in building PageObjects to make your acceptance tests more object oriented.
+
+## Installation
+
+``` terminal
+$ gem install corner_stones
+```
+
+or in your **Gemfile**
+
+``` ruby
+gem 'corner_stones'
+```
+
+## Examples
+
+a lot of examples can be found in the [integration specs](https://github.com/senny/corner_stones/tree/master/spec/integration).
+Some features of corner_stones are listed below.
+
+### Tabs
+
+```ruby
+tabs = CornerStones::Tabs.new('.tab-navigation')
+tabs.open('Details') # open a tab
+```
+
+```ruby
+tabs = CornerStones::Tabs.new('.tab-navigation').tap do |t|
+ t.extend(CornerStones::Tabs::ActiveTracking)
+end
+
+tabs.open('About') # open a tab and verify that the opened tab is active
+tabs.assert_current_tab_is('Main') # verify that the tab 'Main' is active
+```
+
+### Flash Messages
+
+```ruby
+flash = CornerStones::FlashMessages.new
+flash.assert_flash_is_present(:notice, 'Article saved') # verify that a given flash message is present
+```
+
+### Tables
+
+```ruby
+table = CornerStones::Table.new('.articles')
+table.rows # returns an array of rows. Each row is represented as a Hash {header} => {value}
+table.row('Title' => 'Management') # returns the row-hash for the row with 'Management' in the 'Title' column
+```
+
+```ruby
+table = CornerStones::Table.new('.articles').tap do |t|
+ t.extend(CornerStones::Table::SelectableRows)
+ t.extend(CornerStones::Table::DeletableRows)
+ end
+table.select_row('Created at' => '01.12.2001') # select the row, which has '01.12.2001' in the 'Created at' column
+table.delete_row('ID' => '9') # delete the row, which contains '9' in the 'ID' column
+```
+
+### Forms
+
+```ruby
+form = CornerStones::Form.new('.new-article', :select_fields => ['Author'])
+form.fill_in_with('Title' => 'Some Article', 'Author' => 'C. J.') # fill out the form
+form.submit # submit the form using the 'Save' button
+form.submit(:button => 'Save Article') # submit the form using the 'Save Article' button
+
+form.process(:fill_in => {'Title' => 'Some Article', 'Author' => 'C. J.'},
+ :button => 'Save Article) # fill out + submit
+```
+
+```ruby
+form = CornerStones::Form.new('.update-article').tap do |f|
+ f.extend(CornerStones::Form::WithInlineErrors)
+end
+form.errors # returns an Array of form errors
+form.assert_has_no_errors # verify that the form was submitted correctly
+form.submit # verifies that the form has no errors
+form.submit(:assert_valid => false) # do not veirfy that no errors were present
+```
@@ -0,0 +1,5 @@
+require "bundler/gem_tasks"
+
+FileList['tasks/**/*.rake'].each { |task| import task }
+
+task :default => :test
@@ -0,0 +1,25 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "corner_stones/version"
+
+Gem::Specification.new do |s|
+ s.name = "corner_stones"
+ s.version = CornerStones::VERSION
+ s.authors = ["Yves Senn"]
+ s.email = ["yves.senn@gmail.com"]
+ s.homepage = ""
+ s.summary = %q{capybara building blocks for acceptance tests}
+ s.description = %q{This gem makes it easy to build PageObjects and make your acceptance tests more object oriented. It includes a implementations for common elements like tables, tabs, navigations etc.}
+
+ s.rubyforge_project = "corner_stones"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ # specify any dependencies here; for example:
+ # s.add_development_dependency "rspec"
+ s.add_runtime_dependency "capybara"
+
+end
@@ -0,0 +1,8 @@
+require "capybara"
+require "capybara/dsl"
+
+require "corner_stones/version"
+
+module CornerStones
+
+end
@@ -0,0 +1,5 @@
+require 'corner_stones'
+require 'corner_stones/tabs'
+require 'corner_stones/table'
+require 'corner_stones/form'
+require 'corner_stones/flash_messages'
@@ -0,0 +1,35 @@
+module CornerStones
+ class FlashMessages
+
+ class FlashMessageMissingError < StandardError; end
+
+ include Capybara::DSL
+
+ def initialize(options = {})
+ @options = options
+ end
+
+ def message(type, text)
+ messages[type].detect {|message| message[:text] == text}
+ end
+
+ def messages
+ message_types.inject(Hash.new {|hash, key| hash[key] = []}) do |present_messages, type|
+ all(".#{type} p").map do |message|
+ present_messages[type] << {:text => message.text}
+ end
+ present_messages
+ end
+ end
+
+ def message_types
+ @options.fetch(:message_types) { [:notice, :error, :alert] }
+ end
+
+ def assert_flash_is_present(type, message)
+ unless message(type, message)
+ raise FlashMessageMissingError, "the flash message: '#{message}' with type: #{type} was not found"
+ end
+ end
+ end
+end
@@ -0,0 +1,57 @@
+require 'corner_stones/form/with_inline_errors'
+
+module CornerStones
+ class Form
+
+ include Capybara::DSL
+
+ def initialize(scope, options = {})
+ @scope = scope
+ @options = options
+ end
+
+ def process(params)
+ fill_in_with(params[:fill_in])
+ submit(params)
+ end
+
+ def submit(submit_options = {})
+ submit_text = submit_options.fetch(:button) { 'Save' }
+ within @scope do
+ click_button(submit_text)
+ end
+ end
+
+ def fill_in_with(attributes)
+ within @scope do
+ attributes.each do |name, value|
+ if select_fields.include?(name)
+ select(value, :from => name)
+ elsif autocomplete_fields.include?(name)
+ autocomplete(value, :in => name)
+ else
+ fill_in(name, :with => value)
+ end
+ end
+ end
+ end
+
+ def autocomplete(value, options)
+ autocomplete_id = find_field(options[:in])[:id]
+ fill_in(options[:in], :with => value)
+ page.execute_script %Q{ $('##{autocomplete_id}').trigger("focus") }
+ page.execute_script %Q{ $('##{autocomplete_id}').trigger("keydown") }
+ sleep 1
+ page.execute_script %Q{ $('.ui-menu-item a:contains("#{value}")').trigger("mouseenter").trigger("click"); }
+ end
+
+ def select_fields
+ @options.fetch(:select_fields) { [] }
+ end
+
+ def autocomplete_fields
+ @options.fetch(:autocomplete_fields) { [] }
+ end
+ end
+
+end
@@ -0,0 +1,33 @@
+module CornerStones
+ class Form
+ module WithInlineErrors
+
+ class FormHasErrorsError < StandardError; end
+
+ def submit(options = {})
+ assert_valid = options.fetch(:assert_valid) { true }
+ super
+ assert_has_no_errors if assert_valid
+ end
+
+ def errors
+ all('.error').map do |container|
+ label = container.all('label').first
+ input = container.all('input, textarea, select').first
+ error = container.all('.help-inline').first
+
+ { 'Field' => label && label.text,
+ 'Value' => input && input.value,
+ 'Error' => error && error.text }
+ end
+ end
+
+ def assert_has_no_errors
+ unless errors == []
+ raise FormHasErrorsError
+ end
+ end
+
+ end
+ end
+end
@@ -0,0 +1,47 @@
+require 'corner_stones/table/selectable_rows'
+require 'corner_stones/table/deletable_rows'
+
+module CornerStones
+
+ class Table
+ include Capybara::DSL
+
+ def initialize(scope, options = {})
+ @scope = scope
+ @data_selector = options.fetch(:data_selector) { 'td' }
+ @options = options
+ end
+
+ def row(options)
+ rows.detect { |row|
+ identity = row.select { |key, value| options.has_key?(key) }
+ identity == options
+ }
+ end
+
+ def rows
+ within @scope do
+ all('tbody tr').map do |row|
+ attributes_for_row(row)
+ end
+ end
+ end
+
+ def headers
+ @options[:headers] || detect_table_headers
+ end
+
+ def detect_table_headers
+ all('thead th').map(&:text)
+ end
+
+ def attributes_for_row(row)
+ data = row.all(@data_selector)
+
+ real_data = data[0...headers.size].map(&:text)
+
+ Hash[headers.zip(real_data)]
+ end
+ end
+
+end
@@ -0,0 +1,15 @@
+module CornerStones
+ class Table
+ module DeletableRows
+
+ def delete_row(options)
+ row(options)['Delete-Link'].click
+ end
+
+ def attributes_for_row(row)
+ super.merge('Delete-Link' => row.find('.delete-action a'))
+ end
+
+ end
+ end
+end
@@ -0,0 +1,15 @@
+module CornerStones
+ class Table
+ module SelectableRows
+
+ def select_row(options)
+ visit row(options)['Selected-Link']
+ end
+
+ def attributes_for_row(row)
+ super.merge('Selected-Link' => row['data-selected-url'])
+ end
+
+ end
+ end
+end
@@ -0,0 +1,19 @@
+require 'corner_stones/tabs/active_tracking'
+
+module CornerStones
+ class Tabs
+
+ include Capybara::DSL
+
+ def initialize(element_scope)
+ @element_scope = element_scope
+ end
+
+ def open(tab)
+ within(@element_scope) do
+ click_link(tab)
+ end
+ end
+
+ end
+end
@@ -0,0 +1,24 @@
+module CornerStones
+ class Tabs
+ module ActiveTracking
+
+ class ActiveTabMismatchError < StandardError; end
+
+ def open(tab)
+ super
+ assert_current_tab_is(tab)
+ end
+
+ def assert_current_tab_is(tab)
+ current_tab = nil
+ wait_until do
+ current_tab = find(@element_scope).find('.active').text
+ current_tab == tab
+ end
+ rescue Capybara::TimeoutError
+ raise ActiveTabMismatchError, "the active tab is '#{current_tab}' instead of '#{tab}'"
+ end
+
+ end
+ end
+end
@@ -0,0 +1,3 @@
+module CornerStones
+ VERSION = "0.0.1"
+end
Oops, something went wrong.

0 comments on commit c511108

Please sign in to comment.