corner_stones 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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