corner_stones 0.0.1.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create ruby-1.9.3@corner_stones
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in corner_stones.gemspec
4
+ gemspec
@@ -0,0 +1,84 @@
1
+ # Corner Stones
2
+
3
+ [![Build Status](https://secure.travis-ci.org/senny/corner_stones.png)](http://travis-ci.org/senny/corner_stones)
4
+
5
+ assists you in building PageObjects to make your acceptance tests more object oriented.
6
+
7
+ ## Installation
8
+
9
+ ``` terminal
10
+ $ gem install corner_stones
11
+ ```
12
+
13
+ or in your **Gemfile**
14
+
15
+ ``` ruby
16
+ gem 'corner_stones'
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ a lot of examples can be found in the [integration specs](https://github.com/senny/corner_stones/tree/master/spec/integration).
22
+ Some features of corner_stones are listed below.
23
+
24
+ ### Tabs
25
+
26
+ ```ruby
27
+ tabs = CornerStones::Tabs.new('.tab-navigation')
28
+ tabs.open('Details') # open a tab
29
+ ```
30
+
31
+ ```ruby
32
+ tabs = CornerStones::Tabs.new('.tab-navigation').tap do |t|
33
+ t.extend(CornerStones::Tabs::ActiveTracking)
34
+ end
35
+
36
+ tabs.open('About') # open a tab and verify that the opened tab is active
37
+ tabs.assert_current_tab_is('Main') # verify that the tab 'Main' is active
38
+ ```
39
+
40
+ ### Flash Messages
41
+
42
+ ```ruby
43
+ flash = CornerStones::FlashMessages.new
44
+ flash.assert_flash_is_present(:notice, 'Article saved') # verify that a given flash message is present
45
+ ```
46
+
47
+ ### Tables
48
+
49
+ ```ruby
50
+ table = CornerStones::Table.new('.articles')
51
+ table.rows # returns an array of rows. Each row is represented as a Hash {header} => {value}
52
+ table.row('Title' => 'Management') # returns the row-hash for the row with 'Management' in the 'Title' column
53
+ ```
54
+
55
+ ```ruby
56
+ table = CornerStones::Table.new('.articles').tap do |t|
57
+ t.extend(CornerStones::Table::SelectableRows)
58
+ t.extend(CornerStones::Table::DeletableRows)
59
+ end
60
+ table.select_row('Created at' => '01.12.2001') # select the row, which has '01.12.2001' in the 'Created at' column
61
+ table.delete_row('ID' => '9') # delete the row, which contains '9' in the 'ID' column
62
+ ```
63
+
64
+ ### Forms
65
+
66
+ ```ruby
67
+ form = CornerStones::Form.new('.new-article', :select_fields => ['Author'])
68
+ form.fill_in_with('Title' => 'Some Article', 'Author' => 'C. J.') # fill out the form
69
+ form.submit # submit the form using the 'Save' button
70
+ form.submit(:button => 'Save Article') # submit the form using the 'Save Article' button
71
+
72
+ form.process(:fill_in => {'Title' => 'Some Article', 'Author' => 'C. J.'},
73
+ :button => 'Save Article) # fill out + submit
74
+ ```
75
+
76
+ ```ruby
77
+ form = CornerStones::Form.new('.update-article').tap do |f|
78
+ f.extend(CornerStones::Form::WithInlineErrors)
79
+ end
80
+ form.errors # returns an Array of form errors
81
+ form.assert_has_no_errors # verify that the form was submitted correctly
82
+ form.submit # verifies that the form has no errors
83
+ form.submit(:assert_valid => false) # do not veirfy that no errors were present
84
+ ```
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ FileList['tasks/**/*.rake'].each { |task| import task }
4
+
5
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "corner_stones/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "corner_stones"
7
+ s.version = CornerStones::VERSION
8
+ s.authors = ["Yves Senn"]
9
+ s.email = ["yves.senn@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{capybara building blocks for acceptance tests}
12
+ 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.}
13
+
14
+ s.rubyforge_project = "corner_stones"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "capybara"
24
+
25
+ end
@@ -0,0 +1,8 @@
1
+ require "capybara"
2
+ require "capybara/dsl"
3
+
4
+ require "corner_stones/version"
5
+
6
+ module CornerStones
7
+
8
+ end
@@ -0,0 +1,5 @@
1
+ require 'corner_stones'
2
+ require 'corner_stones/tabs'
3
+ require 'corner_stones/table'
4
+ require 'corner_stones/form'
5
+ require 'corner_stones/flash_messages'
@@ -0,0 +1,35 @@
1
+ module CornerStones
2
+ class FlashMessages
3
+
4
+ class FlashMessageMissingError < StandardError; end
5
+
6
+ include Capybara::DSL
7
+
8
+ def initialize(options = {})
9
+ @options = options
10
+ end
11
+
12
+ def message(type, text)
13
+ messages[type].detect {|message| message[:text] == text}
14
+ end
15
+
16
+ def messages
17
+ message_types.inject(Hash.new {|hash, key| hash[key] = []}) do |present_messages, type|
18
+ all(".#{type} p").map do |message|
19
+ present_messages[type] << {:text => message.text}
20
+ end
21
+ present_messages
22
+ end
23
+ end
24
+
25
+ def message_types
26
+ @options.fetch(:message_types) { [:notice, :error, :alert] }
27
+ end
28
+
29
+ def assert_flash_is_present(type, message)
30
+ unless message(type, message)
31
+ raise FlashMessageMissingError, "the flash message: '#{message}' with type: #{type} was not found"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ require 'corner_stones/form/with_inline_errors'
2
+
3
+ module CornerStones
4
+ class Form
5
+
6
+ include Capybara::DSL
7
+
8
+ def initialize(scope, options = {})
9
+ @scope = scope
10
+ @options = options
11
+ end
12
+
13
+ def process(params)
14
+ fill_in_with(params[:fill_in])
15
+ submit(params)
16
+ end
17
+
18
+ def submit(submit_options = {})
19
+ submit_text = submit_options.fetch(:button) { 'Save' }
20
+ within @scope do
21
+ click_button(submit_text)
22
+ end
23
+ end
24
+
25
+ def fill_in_with(attributes)
26
+ within @scope do
27
+ attributes.each do |name, value|
28
+ if select_fields.include?(name)
29
+ select(value, :from => name)
30
+ elsif autocomplete_fields.include?(name)
31
+ autocomplete(value, :in => name)
32
+ else
33
+ fill_in(name, :with => value)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def autocomplete(value, options)
40
+ autocomplete_id = find_field(options[:in])[:id]
41
+ fill_in(options[:in], :with => value)
42
+ page.execute_script %Q{ $('##{autocomplete_id}').trigger("focus") }
43
+ page.execute_script %Q{ $('##{autocomplete_id}').trigger("keydown") }
44
+ sleep 1
45
+ page.execute_script %Q{ $('.ui-menu-item a:contains("#{value}")').trigger("mouseenter").trigger("click"); }
46
+ end
47
+
48
+ def select_fields
49
+ @options.fetch(:select_fields) { [] }
50
+ end
51
+
52
+ def autocomplete_fields
53
+ @options.fetch(:autocomplete_fields) { [] }
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,33 @@
1
+ module CornerStones
2
+ class Form
3
+ module WithInlineErrors
4
+
5
+ class FormHasErrorsError < StandardError; end
6
+
7
+ def submit(options = {})
8
+ assert_valid = options.fetch(:assert_valid) { true }
9
+ super
10
+ assert_has_no_errors if assert_valid
11
+ end
12
+
13
+ def errors
14
+ all('.error').map do |container|
15
+ label = container.all('label').first
16
+ input = container.all('input, textarea, select').first
17
+ error = container.all('.help-inline').first
18
+
19
+ { 'Field' => label && label.text,
20
+ 'Value' => input && input.value,
21
+ 'Error' => error && error.text }
22
+ end
23
+ end
24
+
25
+ def assert_has_no_errors
26
+ unless errors == []
27
+ raise FormHasErrorsError
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,47 @@
1
+ require 'corner_stones/table/selectable_rows'
2
+ require 'corner_stones/table/deletable_rows'
3
+
4
+ module CornerStones
5
+
6
+ class Table
7
+ include Capybara::DSL
8
+
9
+ def initialize(scope, options = {})
10
+ @scope = scope
11
+ @data_selector = options.fetch(:data_selector) { 'td' }
12
+ @options = options
13
+ end
14
+
15
+ def row(options)
16
+ rows.detect { |row|
17
+ identity = row.select { |key, value| options.has_key?(key) }
18
+ identity == options
19
+ }
20
+ end
21
+
22
+ def rows
23
+ within @scope do
24
+ all('tbody tr').map do |row|
25
+ attributes_for_row(row)
26
+ end
27
+ end
28
+ end
29
+
30
+ def headers
31
+ @options[:headers] || detect_table_headers
32
+ end
33
+
34
+ def detect_table_headers
35
+ all('thead th').map(&:text)
36
+ end
37
+
38
+ def attributes_for_row(row)
39
+ data = row.all(@data_selector)
40
+
41
+ real_data = data[0...headers.size].map(&:text)
42
+
43
+ Hash[headers.zip(real_data)]
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,15 @@
1
+ module CornerStones
2
+ class Table
3
+ module DeletableRows
4
+
5
+ def delete_row(options)
6
+ row(options)['Delete-Link'].click
7
+ end
8
+
9
+ def attributes_for_row(row)
10
+ super.merge('Delete-Link' => row.find('.delete-action a'))
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module CornerStones
2
+ class Table
3
+ module SelectableRows
4
+
5
+ def select_row(options)
6
+ visit row(options)['Selected-Link']
7
+ end
8
+
9
+ def attributes_for_row(row)
10
+ super.merge('Selected-Link' => row['data-selected-url'])
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ require 'corner_stones/tabs/active_tracking'
2
+
3
+ module CornerStones
4
+ class Tabs
5
+
6
+ include Capybara::DSL
7
+
8
+ def initialize(element_scope)
9
+ @element_scope = element_scope
10
+ end
11
+
12
+ def open(tab)
13
+ within(@element_scope) do
14
+ click_link(tab)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module CornerStones
2
+ class Tabs
3
+ module ActiveTracking
4
+
5
+ class ActiveTabMismatchError < StandardError; end
6
+
7
+ def open(tab)
8
+ super
9
+ assert_current_tab_is(tab)
10
+ end
11
+
12
+ def assert_current_tab_is(tab)
13
+ current_tab = nil
14
+ wait_until do
15
+ current_tab = find(@element_scope).find('.active').text
16
+ current_tab == tab
17
+ end
18
+ rescue Capybara::TimeoutError
19
+ raise ActiveTabMismatchError, "the active tab is '#{current_tab}' instead of '#{tab}'"
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ module CornerStones
2
+ VERSION = "0.0.1.beta1"
3
+ end
@@ -0,0 +1,67 @@
1
+ require 'integration/spec_helper'
2
+
3
+ require 'corner_stones/flash_messages'
4
+
5
+ describe CornerStones::FlashMessages do
6
+ given_the_html <<-HTML
7
+ <div class="alert">
8
+ <p>Article was not saved. Please correct the errors.</p>
9
+ </div>
10
+ <div class="notice">
11
+ <p>Article saved.</p>
12
+ </div>
13
+ <div class="notice">
14
+ <p>Successfully logged in</p>
15
+ </div>
16
+ HTML
17
+
18
+ subject { CornerStones::FlashMessages.new}
19
+
20
+ it 'assebles present messages into a hash' do
21
+ subject.messages.must_equal(:alert => [{:text => 'Article was not saved. Please correct the errors.'}],
22
+ :notice => [{:text => 'Article saved.'}, {:text => 'Successfully logged in'}])
23
+ end
24
+
25
+ it 'you can select a message by type and content' do
26
+ subject.message(:notice, 'Article saved.').must_equal({:text => 'Article saved.'})
27
+ end
28
+
29
+ it 'nil is returned when no message was found' do
30
+ subject.message(:alert, 'Article saved.').must_equal(nil)
31
+ end
32
+
33
+ describe '#assert_flash_is_present' do
34
+ it 'passes when the flash is present' do
35
+ subject.assert_flash_is_present(:alert, 'Article was not saved. Please correct the errors.')
36
+ end
37
+
38
+ it 'fails when the flash is missing' do
39
+ lambda do
40
+ subject.assert_flash_is_present(:notice, 'I am not displayed')
41
+ end.must_raise(CornerStones::FlashMessages::FlashMessageMissingError)
42
+ end
43
+ end
44
+
45
+ describe 'custom message types' do
46
+ given_the_html <<-HTML
47
+ <div class="alert-error">
48
+ <p>Article was not saved. Please correct the errors.</p>
49
+ </div>
50
+ <div class="alert-info">
51
+ <p>Article saved.</p>
52
+ </div>
53
+ <div class="alert-info">
54
+ <p>Successfully logged in</p>
55
+ </div>
56
+ HTML
57
+
58
+ subject { CornerStones::FlashMessages.new(:message_types => [:'alert-info', :'alert-error', :'alert-warning'])}
59
+
60
+ it 'uses the :message_type option to determine the available messages' do
61
+ subject.messages.must_equal(:'alert-error' => [{:text => 'Article was not saved. Please correct the errors.'}],
62
+ :'alert-info' => [{:text => 'Article saved.'}, {:text => 'Successfully logged in'}])
63
+ end
64
+
65
+ end
66
+ end
67
+
@@ -0,0 +1,154 @@
1
+ require 'integration/spec_helper'
2
+
3
+ require 'corner_stones/form'
4
+ require 'corner_stones/form/with_inline_errors'
5
+
6
+ describe CornerStones::Form do
7
+
8
+ given_the_html <<-HTML
9
+ <form action="/articles" method="post" class="article-form">
10
+ <label for="title">Title</label>
11
+ <input type="text" name="title" id="title">
12
+
13
+ <label for="author">Author</label>
14
+ <select name="author" id="author">
15
+ <option value="1">Robert C. Martin</option>
16
+ <option value="2">Eric Evans</option>
17
+ <option value="3">Kent Beck</option>
18
+ </select>
19
+
20
+ <label for="body">Body</label>
21
+ <textarea name="body" id="body">
22
+ </textarea>
23
+
24
+ <input type="submit" name="button" value="Save">
25
+ <input type="submit" name="button" value="Save Article">
26
+
27
+ </form>
28
+ HTML
29
+
30
+ subject { CornerStones::Form.new('.article-form', :select_fields => ['Author']) }
31
+
32
+ it 'allows you to fill in the form' do
33
+ subject.fill_in_with('Title' => 'Domain Driven Design',
34
+ 'Author' => 'Eric Evans',
35
+ 'Body' => '...')
36
+
37
+ find('#title').value.must_equal 'Domain Driven Design'
38
+ find('#author').value.must_equal '2'
39
+ find('#body').value.must_equal '...'
40
+ end
41
+
42
+ it 'allows you to submit the form' do
43
+ subject.submit
44
+
45
+ current_path.must_equal '/articles'
46
+ page.driver.request.post?.must_equal true
47
+ end
48
+
49
+ it 'you can supply the submit-button text with the :button option' do
50
+ subject.submit(:button => 'Save Article')
51
+
52
+ page.driver.request.params['button'].must_equal 'Save Article'
53
+ end
54
+
55
+ it 'allows you to process (fill_in_with + submit) the form' do
56
+ subject.process(:fill_in => {
57
+ 'Title' => 'Domain Driven Design',
58
+ 'Author' => 'Eric Evans',
59
+ 'Body' => 'Some Content...'})
60
+
61
+ current_path.must_equal '/articles'
62
+ page.driver.request.post?.must_equal true
63
+
64
+ page.driver.request.params.must_equal({"title" => "Domain Driven Design", "author" => "2", "body" => "Some Content...", 'button' => 'Save'})
65
+ end
66
+
67
+ it 'allows you to process (fill_in_with + submit) the form using an alternate button' do
68
+ subject.process(:fill_in => {'Title' => 'Domain Driven Design',
69
+ 'Author' => 'Eric Evans',
70
+ 'Body' => 'Some Content...'},
71
+ :button => 'Save Article')
72
+
73
+ page.driver.request.params['button'].must_equal('Save Article')
74
+ end
75
+
76
+ describe 'mixins' do
77
+ describe 'form errors' do
78
+
79
+ before do
80
+ subject.extend(CornerStones::Form::WithInlineErrors)
81
+ end
82
+
83
+ describe 'with errors' do
84
+ given_the_html <<-HTML
85
+ <form action="/articles" method="post" class="form-with-errors article-form">
86
+ <div>
87
+ <label for="title">Title</label>
88
+ <input type="text" name="title" id="title">
89
+ </div>
90
+
91
+ <div class="error">
92
+ <label for="author">Author</label>
93
+ <select name="author" id="author">
94
+ <option value="1">Robert C. Martin</option>
95
+ <option value="2">Eric Evans</option>
96
+ <option value="3">Kent Beck</option>
97
+ </select>
98
+ <span class="help-inline">The author is not active</span>
99
+ </div>
100
+
101
+ <div class="error">
102
+ <label for="body">Body</label>
103
+ <textarea name="body" id="body">...</textarea>
104
+ <span class="help-inline">invalid body</span>
105
+ </div>
106
+
107
+ <input type="submit" value="Save">
108
+
109
+ </form>
110
+ HTML
111
+
112
+ it 'assembles the errors into a hash' do
113
+ subject.errors.must_equal([{"Field" => "Author", "Value" => "1", "Error" => "The author is not active"},
114
+ {"Field" => "Body", "Value" => "...", "Error" => "invalid body"}])
115
+ end
116
+
117
+ it '#assert_has_no_errors fails' do
118
+ lambda do
119
+ subject.assert_has_no_errors
120
+ end.must_raise(CornerStones::Form::WithInlineErrors::FormHasErrorsError)
121
+ end
122
+
123
+ it 'does not allow you to submit the form by default' do
124
+ lambda do
125
+ subject.submit
126
+ end.must_raise(CornerStones::Form::WithInlineErrors::FormHasErrorsError)
127
+ end
128
+
129
+ it 'bypass the auto-error-validation when passing :assert_valid => false' do
130
+ subject.submit(:assert_valid => false)
131
+ end
132
+ end
133
+
134
+ describe 'without errors' do
135
+ given_the_html <<-HTML
136
+ <form action="/articles" method="post" class="form-without-errors article-form">
137
+ <label for="title">Title</label>
138
+ <input type="text" name="title" id="title">
139
+
140
+ <input type="submit" value="Save">
141
+ <form>
142
+ HTML
143
+ end
144
+
145
+ it '#assert_has_no_errors passes' do
146
+ subject.assert_has_no_errors
147
+ end
148
+
149
+ it 'allows you to submit the form' do
150
+ subject.submit
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,189 @@
1
+ require 'integration/spec_helper'
2
+
3
+ require 'corner_stones/table'
4
+ require 'corner_stones/table/deletable_rows'
5
+ require 'corner_stones/table/selectable_rows'
6
+
7
+ describe CornerStones::Table do
8
+
9
+ given_the_html <<-HTML
10
+ <table class="articles">
11
+ <thead>
12
+ <tr>
13
+ <th>ID</th>
14
+ <th>Title</th>
15
+ <th>Author</th>
16
+ </tr>
17
+ </thead>
18
+ <tbody>
19
+ <tr>
20
+ <td>1</td>
21
+ <td>Clean Code</td>
22
+ <td>Robert C. Martin</td>
23
+ </tr>
24
+ <tr>
25
+ <td>2</td>
26
+ <td>Domain Driven Design</td>
27
+ <td>Eric Evans</td>
28
+ </tr>
29
+ </tbody>
30
+ </table
31
+ HTML
32
+
33
+ subject { CornerStones::Table.new('.articles') }
34
+
35
+ describe 'headers' do
36
+ it 'get detected automatically from the "th" tags' do
37
+ subject.headers.must_equal ['ID', 'Title', 'Author']
38
+ end
39
+
40
+ it 'can be supplied with the :headers option' do
41
+ table_with_custom_headers = CornerStones::Table.new('.articles', :headers => ['A-ID', 'strTitle', 'strAuthor'])
42
+ table_with_custom_headers.headers.must_equal ['A-ID', 'strTitle', 'strAuthor']
43
+ end
44
+ end
45
+
46
+ describe "data" do
47
+ it 'is read into an array of hashes ({header} => {data})' do
48
+ subject.rows.must_equal [{'ID' => '1',
49
+ 'Title' => 'Clean Code',
50
+ 'Author' => 'Robert C. Martin'},
51
+ {'ID' => '2',
52
+ 'Title' => 'Domain Driven Design',
53
+ 'Author' => 'Eric Evans'}]
54
+ end
55
+
56
+ it 'a row can be accessed with a single key' do
57
+ subject.row('Title' => 'Domain Driven Design').must_equal({ 'ID' => '2',
58
+ 'Title' => 'Domain Driven Design',
59
+ 'Author' => 'Eric Evans' })
60
+ end
61
+
62
+ it 'a row can be accessed with multiple keys' do
63
+ subject.row('ID' => '1',
64
+ 'Author' => 'Robert C. Martin').must_equal({'ID' => '1',
65
+ 'Title' => 'Clean Code',
66
+ 'Author' => 'Robert C. Martin'})
67
+ end
68
+
69
+ it 'nil is returned when no matching row was found' do
70
+ subject.row('ID' => '3').must_equal(nil)
71
+ end
72
+ end
73
+
74
+ describe 'custom tables' do
75
+ describe 'inline headers' do
76
+ given_the_html <<-HTML
77
+ <table class="articles">
78
+ <tbody>
79
+ <tr>
80
+ <th>Clean Code</th>
81
+ <td>Robert C. Martin</td>
82
+ </tr>
83
+ <tr>
84
+ <th>Domain Driven Design</th>
85
+ <td>Eric Evans</td>
86
+ </tr>
87
+ </tbody>
88
+ </table>
89
+ HTML
90
+
91
+ subject { CornerStones::Table.new('.articles', :headers => ['Book', 'Author'], :data_selector => 'th,td') }
92
+
93
+ it 'the option :data_selector can be used to widen the data to other elements' do
94
+ subject.rows.must_equal [{ 'Book' => 'Clean Code',
95
+ 'Author' => 'Robert C. Martin'},
96
+ { 'Book' => 'Domain Driven Design',
97
+ 'Author' => 'Eric Evans'}]
98
+ end
99
+
100
+ end
101
+ end
102
+
103
+ describe 'mixins' do
104
+ describe 'deletable rows' do
105
+ given_the_html <<-HTML
106
+ <table class="articles">
107
+ <thead>
108
+ <tr>
109
+ <th>ID</th>
110
+ <th>Title</th>
111
+ <th>Author</th>
112
+ </tr>
113
+ </thead>
114
+ <tbody>
115
+ <tr>
116
+ <td>1</td>
117
+ <td>Clean Code</td>
118
+ <td>Robert C. Martin</td>
119
+ <td class="delete-action"><a href="/delete/clean_code">X</a></td>
120
+ </tr>
121
+ <tr>
122
+ <td>2</td>
123
+ <td>Domain Driven Design</td>
124
+ <td>Eric Evans</td>
125
+ <td class="delete-action"><a href="/delete/domain_driven_design">X</a></td>
126
+ </tr>
127
+ </tbody>
128
+ </table>
129
+ HTML
130
+
131
+ before do
132
+ subject.extend(CornerStones::Table::DeletableRows)
133
+ end
134
+
135
+ it 'it includes the "Delete-Link" object in the data' do
136
+ subject.rows.each do |row|
137
+ row['Delete-Link'].must_be_kind_of(Capybara::Node::Element)
138
+ end
139
+ end
140
+
141
+ it 'allows you to trigger a deletion with a row selector' do
142
+ subject.delete_row('Title' => 'Domain Driven Design')
143
+ current_path.must_equal '/delete/domain_driven_design'
144
+ end
145
+ end
146
+
147
+ describe 'selectable rows' do
148
+ given_the_html <<-HTML
149
+ <table class="articles">
150
+ <thead>
151
+ <tr>
152
+ <th>ID</th>
153
+ <th>Title</th>
154
+ <th>Author</th>
155
+ </tr>
156
+ </thead>
157
+ <tbody>
158
+ <tr data-selected-url="/articles/clean_code">
159
+ <td>1</td>
160
+ <td>Clean Code</td>
161
+ <td>Robert C. Martin</td>
162
+ </tr>
163
+ <tr data-selected-url="/articles/domain_driven_design">
164
+ <td>2</td>
165
+ <td>Domain Driven Design</td>
166
+ <td>Eric Evans</td>
167
+ </tr>
168
+ </tbody>
169
+ </table>
170
+ HTML
171
+
172
+ before do
173
+ subject.extend(CornerStones::Table::SelectableRows)
174
+ end
175
+
176
+ it 'it includes the "Selected-Link" object in the data' do
177
+ subject.rows.map do |row|
178
+ row['Selected-Link']
179
+ end.must_equal ['/articles/clean_code', '/articles/domain_driven_design']
180
+ end
181
+
182
+ it 'allows you to select a row' do
183
+ subject.select_row('ID' => '1')
184
+ current_path.must_equal '/articles/clean_code'
185
+ end
186
+ end
187
+
188
+ end
189
+ end
@@ -0,0 +1,49 @@
1
+ require 'integration/spec_helper'
2
+
3
+ require 'corner_stones/tabs'
4
+ require 'corner_stones/tabs/active_tracking'
5
+
6
+ describe CornerStones::Tabs do
7
+
8
+ given_the_html <<-HTML
9
+ <ul class="main-tabs-nav">
10
+ <li class="active"><a href='/main'>Main</a></li>
11
+ <li><a href='/details'>Details</a></li>
12
+ <li><a href='/more_stuff'>More Stuff</a></li>
13
+ </ul>
14
+ HTML
15
+
16
+ subject { CornerStones::Tabs.new('.main-tabs-nav') }
17
+
18
+ it 'opens a given tab' do
19
+ subject.open('Details')
20
+ current_path.must_equal '/details'
21
+ end
22
+
23
+ describe 'mixins' do
24
+ describe 'active tab tracking' do
25
+ before do
26
+ subject.extend(CornerStones::Tabs::ActiveTracking)
27
+ end
28
+
29
+ it 'asserts the current tab after opening a new tab' do
30
+ lambda do
31
+ subject.open('More Stuff')
32
+ end.must_raise CornerStones::Tabs::ActiveTracking::ActiveTabMismatchError
33
+ end
34
+
35
+ describe '#assert_current_tab_is' do
36
+ it 'passes when the given tab is active' do
37
+ subject.assert_current_tab_is('Main')
38
+ end
39
+
40
+ it 'fails when the given tab is not active' do
41
+ lambda do
42
+ subject.assert_current_tab_is('More Stuff')
43
+ end.must_raise CornerStones::Tabs::ActiveTracking::ActiveTabMismatchError
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,12 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+
4
+ require 'capybara'
5
+ require 'capybara/dsl'
6
+
7
+ Capybara.default_wait_time = 0
8
+
9
+ require 'integration/support/response_macros'
10
+
11
+ include ResponseMacros
12
+ include Capybara::DSL
@@ -0,0 +1,12 @@
1
+ module ResponseMacros
2
+
3
+ def given_the_html(html)
4
+ before do
5
+ Capybara.app = lambda do |env|
6
+ [200, {}, html]
7
+ end
8
+ visit '/'
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,6 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.pattern = 'spec/**/*_spec.rb'
5
+ t.libs.push 'spec'
6
+ end
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ notifications:
6
+ email:
7
+ - yves.senn@gmail.com
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: corner_stones
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.beta1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Yves Senn
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: capybara
16
+ requirement: &70273546252500 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70273546252500
25
+ description: This gem makes it easy to build PageObjects and make your acceptance
26
+ tests more object oriented. It includes a implementations for common elements like
27
+ tables, tabs, navigations etc.
28
+ email:
29
+ - yves.senn@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - .gitignore
35
+ - .rvmrc
36
+ - Gemfile
37
+ - README.md
38
+ - Rakefile
39
+ - corner_stones.gemspec
40
+ - lib/corner_stones.rb
41
+ - lib/corner_stones/all.rb
42
+ - lib/corner_stones/flash_messages.rb
43
+ - lib/corner_stones/form.rb
44
+ - lib/corner_stones/form/with_inline_errors.rb
45
+ - lib/corner_stones/table.rb
46
+ - lib/corner_stones/table/deletable_rows.rb
47
+ - lib/corner_stones/table/selectable_rows.rb
48
+ - lib/corner_stones/tabs.rb
49
+ - lib/corner_stones/tabs/active_tracking.rb
50
+ - lib/corner_stones/version.rb
51
+ - spec/integration/corner_stones/flash_messages_spec.rb
52
+ - spec/integration/corner_stones/form_spec.rb
53
+ - spec/integration/corner_stones/table_spec.rb
54
+ - spec/integration/corner_stones/tabs_spec.rb
55
+ - spec/integration/spec_helper.rb
56
+ - spec/integration/support/response_macros.rb
57
+ - tasks/test.rake
58
+ - travis.yml
59
+ homepage: ''
60
+ licenses: []
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>'
75
+ - !ruby/object:Gem::Version
76
+ version: 1.3.1
77
+ requirements: []
78
+ rubyforge_project: corner_stones
79
+ rubygems_version: 1.8.10
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: capybara building blocks for acceptance tests
83
+ test_files:
84
+ - spec/integration/corner_stones/flash_messages_spec.rb
85
+ - spec/integration/corner_stones/form_spec.rb
86
+ - spec/integration/corner_stones/table_spec.rb
87
+ - spec/integration/corner_stones/tabs_spec.rb
88
+ - spec/integration/spec_helper.rb
89
+ - spec/integration/support/response_macros.rb