on-test-spec 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg/
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008, Fingertips
2
+ - Manfred Stienstra <manfred@fngtps.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,12 @@
1
+ On Test Spec
2
+ ------------
3
+
4
+ Makes testing Rails apps with test/spec easier and more fun. Created with a
5
+ half eye on the test_spec_on_rails plugin, thanks!
6
+
7
+ Autotest
8
+ --------
9
+
10
+ To use autotest with test/spec on Rails, copy the "dot.autotest" sample file to
11
+ your project's root directory and name it ".autotest". Edit the file if you
12
+ need any custom mappings.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'rake/testtask'
2
+
3
+ desc "By default run all tests"
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/**/*_test.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ begin
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |s|
15
+ s.name = "on-test-spec"
16
+ s.homepage = "http://github.com/Fingertips/on-test-spec"
17
+ s.email = "eloy.de.enige@gmail.com"
18
+ s.authors = ["Manfred Stienstra", "Eloy Duran", "Cristi Balan"]
19
+ s.summary = s.description = "Rails plugin to make testing Rails on test/spec easier."
20
+ end
21
+ rescue LoadError
22
+ end
23
+
24
+ begin
25
+ require 'jewelry_portfolio/tasks'
26
+ JewelryPortfolio::Tasks.new do |p|
27
+ p.account = 'Fingertips'
28
+ end
29
+ rescue LoadError
30
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,5 @@
1
+ ---
2
+ :minor: 2
3
+ :patch: 4
4
+ :major: 0
5
+ :build:
data/dot.autotest ADDED
@@ -0,0 +1,93 @@
1
+ Autotest.add_hook :initialize do |at|
2
+ at.unit_diff = 'cat'
3
+ at.failed_results_re = /^\s+\d+\) (?:Failure|Error):\n(.*?)\((.*?)\)\n\[([^:]*):.*\]/
4
+
5
+ at.add_exception %r%^\./(?:db|doc|log|public|script|tmp|vendor|data|content|config)%
6
+ at.add_exception %r%\.svn%
7
+
8
+ at.clear_mappings
9
+
10
+ # Add your custom mappings:
11
+ #
12
+ # at.add_mapping %r%^app/(concerns)/(.*)\.rb$% do |_, match|
13
+ # "test/unit/#{match[1]}/#{match[2]}_test.rb"
14
+ # end
15
+ #
16
+ # at.add_mapping %r%^test/unit/(concerns)/.*rb$% do |filename, _|
17
+ # filename
18
+ # end
19
+ #
20
+ # at.add_mapping %r%^lib/(.*).rb% do |_, match|
21
+ # sqwat = match[1].gsub('/', '_')
22
+ # "test/lib/#{sqwat}_test.rb"
23
+ # end
24
+ #
25
+ # at.add_mapping %r%^test/lib/.*rb$% do |filename, _|
26
+ # filename
27
+ # end
28
+
29
+ # Standard Rails
30
+
31
+ at.add_mapping %r%^test/fixtures/(.*)s.yml% do |_, m|
32
+ ["test/unit/#{m[1]}_test.rb",
33
+ "test/controllers/#{m[1]}_controller_test.rb",
34
+ "test/views/#{m[1]}_view_test.rb",
35
+ "test/functional/#{m[1]}_controller_test.rb"]
36
+ end
37
+
38
+ at.add_mapping %r%^test/(unit|integration|controllers|views|functional|helpers)/.*rb$% do |filename, _|
39
+ filename
40
+ end
41
+
42
+ at.add_mapping %r%^app/models/(.*)\.rb$% do |_, m|
43
+ "test/unit/#{m[1]}_test.rb"
44
+ end
45
+
46
+ at.add_mapping %r%^app/helpers/application_helper.rb% do
47
+ ["test/helpers/applications_helper_test.rb"] + at.files_matching(%r%^test/(views|functional)/.*_test\.rb$%)
48
+ end
49
+
50
+ at.add_mapping %r%^app/helpers/(.*)_helper.rb% do |_, m|
51
+ if m[1] == "application" then
52
+ at.files_matching %r%^test/(views|functional)/.*_test\.rb$%
53
+ else
54
+ ["test/helpers/#{m[1]}_helper_test.rb",
55
+ "test/views/#{m[1]}_view_test.rb",
56
+ "test/functional/#{m[1]}_controller_test.rb"]
57
+ end
58
+ end
59
+
60
+ at.add_mapping %r%^app/views/(.*)/% do |_, m|
61
+ ["test/views/#{m[1]}_view_test.rb",
62
+ "test/functional/#{m[1]}_controller_test.rb"]
63
+ end
64
+
65
+ at.add_mapping %r%^app/controllers/(.*)\.rb$% do |_, m|
66
+ if m[1] == "application" then
67
+ at.files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
68
+ else
69
+ ["test/controllers/#{m[1]}_test.rb",
70
+ "test/functional/#{m[1]}_test.rb"]
71
+ end
72
+ end
73
+
74
+ at.add_mapping %r%^app/views/layouts/% do
75
+ "test/views/layouts_view_test.rb"
76
+ end
77
+
78
+ at.add_mapping %r%^config/routes.rb$% do
79
+ at.files_matching %r%^test/(controllers|views|functional)/.*_test\.rb$%
80
+ end
81
+
82
+ at.add_mapping %r%^test/test_helper.rb|config/((boot|environment(s/test)?).rb|database.yml)% do
83
+ at.files_matching %r%^test/(unit|controllers|views|functional)/.*_test\.rb$%
84
+ end
85
+
86
+ class << at
87
+ def consolidate_failures(failed)
88
+ failed.inject(new_hash_of_arrays) do |filters, (method, klass, filename)|
89
+ filters[File.expand_path(filename)] << method; filters
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,53 @@
1
+ require 'active_support'
2
+
3
+ class Class
4
+ def add_allow_switch(method, options={})
5
+ default = options[:default] || false
6
+
7
+ class_eval do
8
+ cattr_accessor "allow_#{method}"
9
+ self.send("allow_#{method}=", default)
10
+
11
+ alias_method "original_#{method}", method
12
+
13
+ eval %{
14
+ def #{method}(*args)
15
+ if allow_#{method}
16
+ original_#{method}(*args)
17
+ else
18
+ raise RuntimeError, "You're trying to call `#{method}' on `#{self}', which you probably don't want in a test."
19
+ end
20
+ end
21
+ }, binding, __FILE__, __LINE__
22
+ end
23
+ end
24
+ end
25
+
26
+ class Module
27
+ def add_allow_switch(method, options={})
28
+ default = options[:default] || false
29
+
30
+ mattr_accessor "allow_#{method}"
31
+ send("allow_#{method}=", default)
32
+
33
+ unless respond_to?(:__metaclass___)
34
+ def __metaclass__
35
+ class << self; self; end
36
+ end
37
+ end
38
+
39
+ __metaclass__.class_eval do
40
+ alias_method "original_#{method}", method
41
+
42
+ eval %{
43
+ def #{method}(*args)
44
+ if allow_#{method}
45
+ original_#{method}(*args)
46
+ else
47
+ raise RuntimeError, "You're trying to call `#{method}' on `#{self}', which you probably don't want in a test."
48
+ end
49
+ end
50
+ }, binding, __FILE__, __LINE__
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,11 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module ControllerHelpers
5
+ attr_reader :controller
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ ActionController::TestCase.send(:include, Test::Spec::Rails::ControllerHelpers)
@@ -0,0 +1,192 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module Helpers
5
+ def self.inspect_records(records)
6
+ "[#{records.map { |record| "#{record.class}[#{record.id}]" }.join(', ')}]"
7
+ end
8
+ end
9
+
10
+ module ShouldExpectations
11
+ # Test that we were redirected somewhere:
12
+ # should.redirect
13
+ #
14
+ # Test that we were redirected to a specific url:
15
+ # should.redirect :controller => 'foo', :action => 'bar'
16
+ # or:
17
+ # should.redirect_to :controller => 'foo', :action => 'bar', :secure => true
18
+ def redirect(*args)
19
+ if args.empty?
20
+ test_case.assert_response @object.response.redirected_to, :redirect
21
+ elsif args.length == 1 and args.first.is_a?(String)
22
+ test_case.assert_equal args.first, @object.response.redirected_to
23
+ else
24
+ options = args.extract_options!
25
+ if secure = options.delete(:secure)
26
+ unless secure == true or secure == false
27
+ raise ArgumentError, ":secure option should be a boolean"
28
+ end
29
+ end
30
+
31
+ @object.instance_eval { test_case.assert_redirected_to *args }
32
+ if secure == true
33
+ test_case.assert @object.response.redirected_to.starts_with?('https:')
34
+ elsif secure == false
35
+ test_case.assert @object.response.redirected_to.starts_with?('http:')
36
+ end
37
+ end
38
+ end
39
+ alias :redirect_to :redirect
40
+
41
+ # Tests whether a redirect back to the HTTP_REFERER was send.
42
+ #
43
+ # lambda { delete :destroy, :id => 1 }.should.redirect_back_to(articles_url)
44
+ # lambda { delete :destroy, :id => 1 }.should.redirect_back_to(:action => :index)
45
+ def redirect_back_to(url_options)
46
+ test_case = eval("self", @object.binding)
47
+ url = test_case.controller.url_for(url_options)
48
+ test_case.controller.request.env["HTTP_REFERER"] = url
49
+
50
+ block_result = @object.call
51
+ test_case.should.redirect_to(url)
52
+
53
+ block_result
54
+ end
55
+
56
+ # Test that the object is valid
57
+ def validate
58
+ test_case.assert_valid @object
59
+ end
60
+
61
+ # Tests whether the evaluation of the expression changes.
62
+ #
63
+ # lambda { Norm.create }.should.differ('Norm.count')
64
+ # lambda { Norm.create; Norm.create }.should.differ('Norm.count', +2)
65
+ # lambda { Norm.create; Option.create }.should.differ('Norm.count', +2, 'Option.count', +1)
66
+ #
67
+ # norm = lambda { Norm.create }.should.differ('Norm.count')
68
+ # norm.name.should == 'Latency'
69
+ def differ(*expected)
70
+ block_binding = @object.send(:binding)
71
+ before = expected.in_groups_of(2).map do |expression, _|
72
+ eval(expression, block_binding)
73
+ end
74
+
75
+ block_result = @object.call
76
+
77
+ expected.in_groups_of(2).each_with_index do |(expression, difference), index|
78
+ difference = 1 if difference.nil?
79
+ error = "#{expression.inspect} didn't change by #{difference}"
80
+ test_case.assert_equal(before[index] + difference, eval(expression, block_binding), error)
81
+ end
82
+
83
+ block_result
84
+ end
85
+ alias change differ
86
+
87
+ # Tests whether certain pages are cached.
88
+ #
89
+ # lambda { get :index }.should.cache_pages(posts_path)
90
+ # lambda { get :show, :id => post }.should.cache_pages(post_path(post), formatted_posts_path(:js, post))
91
+ def cache_pages(*pages, &block)
92
+ if block
93
+ block.call
94
+ else
95
+ @object.call
96
+ end
97
+ cache_dir = ActionController::Base.page_cache_directory
98
+ files = Dir.glob("#{cache_dir}/**/*").map do |filename|
99
+ filename[cache_dir.length..-1]
100
+ end
101
+ test_case.assert pages.all? { |page| files.include?(page) }
102
+ end
103
+
104
+ # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
105
+ def dom_equal(expected)
106
+ test_case.assert_dom_equal expected, @object
107
+ end
108
+
109
+ # Tests if the array of records is the same, order may vary
110
+ def equal_set(expected)
111
+ message = "#{Helpers.inspect_records(@object)} does not have the same records as #{Helpers.inspect_records(expected)}"
112
+
113
+ left = @object.map(&:id).sort
114
+ right = expected.map(&:id).sort
115
+
116
+ test_case.assert(left == right, message)
117
+ end
118
+
119
+ # Tests if the array of records is the same, order must be the same
120
+ def equal_list(expected)
121
+ message = "#{Helpers.inspect_records(@object)} does not have the same records as #{Helpers.inspect_records(expected)}"
122
+
123
+ left = @object.map(&:id)
124
+ right = expected.map(&:id)
125
+
126
+ test_case.assert(left == right, message)
127
+ end
128
+ end
129
+
130
+ module ShouldNotExpectations
131
+ # Test that an object is not valid
132
+ def validate
133
+ test_case.assert !@object.valid?
134
+ end
135
+
136
+ # Tests that the evaluation of the expression shouldn't change
137
+ #
138
+ # lambda { Norm.new }.should.not.differ('Norm.count')
139
+ # lambda { Norm.new }.should.not.differ('Norm.count', 'Option.count')
140
+ #
141
+ # norm = lambda { Norm.new }.should.not.differ('Norm.count')
142
+ # norm.token.should.match /(\d\w){4}/
143
+ def differ(*expected)
144
+ block_binding = @object.send(:binding)
145
+ before = expected.map do |expression|
146
+ eval(expression, block_binding)
147
+ end
148
+
149
+ block_result = @object.call
150
+
151
+ expected.each_with_index do |expression, index|
152
+ difference = eval(expression, block_binding) - before[index]
153
+ error = "#{expression.inspect} changed by #{difference}, expected no change"
154
+ test_case.assert_equal(0, difference, error)
155
+ end
156
+
157
+ block_result
158
+
159
+ end
160
+ alias change differ
161
+
162
+ # Test that two HTML strings are not equivalent
163
+ def dom_equal(expected)
164
+ test_case.assert_dom_not_equal expected, @object
165
+ end
166
+
167
+ # Tests if the array of records is not the same, order may vary
168
+ def equal_set(expected)
169
+ message = "#{Helpers.inspect_records(@object)} has the same records as #{Helpers.inspect_records(expected)}"
170
+
171
+ left = @object.map(&:id).sort
172
+ right = expected.map(&:id).sort
173
+
174
+ test_case.assert(left != right, message)
175
+ end
176
+
177
+ # Tests if the array of records is not the same, order may vary
178
+ def equal_list(expected)
179
+ message = "#{Helpers.inspect_records(@object)} has the same records as #{Helpers.inspect_records(expected)}"
180
+
181
+ left = @object.map(&:id)
182
+ right = expected.map(&:id)
183
+
184
+ test_case.assert(left != right, message)
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ Test::Spec::Should.send(:include, Test::Spec::Rails::ShouldExpectations)
192
+ Test::Spec::ShouldNot.send(:include, Test::Spec::Rails::ShouldNotExpectations)
@@ -0,0 +1,80 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module Macros
5
+ class Should
6
+ # Generates a test which tests authorization code. It assumes a method called <code>access_denied?</code>
7
+ # on the test case. The <code>access_denied?</code> method should return true when access is denied
8
+ # (ie. a 403 status code) and false in other cases.
9
+ #
10
+ # Example:
11
+ # should.disallow.get :index
12
+ def disallow
13
+ Test::Spec::Rails::Macros::Authorization::TestGenerator.new(test_case,
14
+ :access_denied?, true,
15
+ 'Expected access to be denied'
16
+ )
17
+ end
18
+
19
+ # Generates a test which tests authorization code. It assumes a method called <code>access_denied?</code>
20
+ # on the test case. The <code>access_denied?</code> method should return true when access is denied
21
+ # (ie. a 403 status code) and false in other cases.
22
+ #
23
+ # Example:
24
+ # should.allow.get :index
25
+ def allow
26
+ Test::Spec::Rails::Macros::Authorization::TestGenerator.new(test_case,
27
+ :access_denied?, false,
28
+ 'Expected access to be allowed'
29
+ )
30
+ end
31
+
32
+ # Generates a test which tests authorization code. It assumes a method called <code>access_denied?</code>
33
+ # on the test case. The <code>login_required?</code> method should return true when the visitor was
34
+ # asked for credentials (ie. a 401 status code or a redirect to a login page) and false in other cases.
35
+ #
36
+ # Example:
37
+ # should.require_login.get :index
38
+ def require_login
39
+ Test::Spec::Rails::Macros::Authorization::TestGenerator.new(test_case,
40
+ :login_required?, true,
41
+ 'Expected login to be required'
42
+ )
43
+ end
44
+ end
45
+
46
+ module Authorization
47
+ class TestGenerator < Proxy
48
+ attr_accessor :validation_method, :message, :expected
49
+
50
+ def initialize(test_case, validation_method, expected, message)
51
+ self.validation_method = validation_method
52
+ self.expected = expected
53
+ self.message = message
54
+
55
+ super(test_case)
56
+ end
57
+
58
+ def method_missing(verb, action, params={})
59
+ if [:get, :post, :put, :delete, :options].include?(verb.to_sym)
60
+ description = "should disallow #{verb.to_s.upcase} on `#{action}'"
61
+ description << " #{params.inspect}" unless params.blank?
62
+
63
+ validation_method = self.validation_method
64
+ expected = self.expected
65
+ message = self.message
66
+
67
+ test_case.it description do
68
+ send(verb, action, immediate_values(params))
69
+ send(validation_method).should.messaging(message) == expected
70
+ end
71
+ else
72
+ super
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,57 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module Macros
5
+ class Should
6
+ def not_find
7
+ Test::Spec::Rails::Macros::Response::TestGenerator.new(test_case,
8
+ :not_found,
9
+ 'Expected to not find a resource'
10
+ )
11
+ end
12
+ end
13
+
14
+ module Response
15
+ class TestGenerator < Proxy
16
+ attr_accessor :status, :message, :expected
17
+
18
+ def initialize(test_case, status, message)
19
+ self.status = status
20
+ self.message = message
21
+
22
+ super(test_case)
23
+ end
24
+
25
+ def method_missing(verb, action, params={})
26
+ if [:get, :post, :put, :delete, :options].include?(verb.to_sym)
27
+ description = "should not find resource with #{verb.to_s.upcase} on `#{action}'"
28
+ description << " #{params.inspect}" unless params.blank?
29
+
30
+ status = self.status
31
+ message = self.message
32
+
33
+ if defined?(:ActiveRecord)
34
+ test_case.it description do
35
+ begin
36
+ send(verb, action, immediate_values(params))
37
+ status.should.messaging(message) == status
38
+ rescue ActiveRecord::RecordNotFound
39
+ :not_found.should.messaging(message) == status
40
+ end
41
+ end
42
+ else
43
+ test_case.it description do
44
+ send(verb, action, immediate_values(params))
45
+ status.should.messaging(message) == status
46
+ end
47
+ end
48
+ else
49
+ super
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,79 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module Macros
5
+ # Base class for all the proxy classes defined in the macros
6
+ class Proxy
7
+ attr_accessor :test_case
8
+
9
+ def initialize(test_case)
10
+ self.test_case = test_case
11
+ end
12
+ end
13
+
14
+ # Macros define methods on the Should class if they want to be called from the test case.
15
+ class Should < Proxy
16
+ end
17
+
18
+ # Stores expression to be evaluated later in the correct binding
19
+ class LazyValue
20
+ attr_accessor :value
21
+ def initialize(value)
22
+ self.value = value
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ # Returns an instance of the Should class, this allows you to call macros from the test
28
+ # case is a nice way:
29
+ #
30
+ # should.disallow.get :index
31
+ def should
32
+ Test::Spec::Rails::Macros::Should.new(self)
33
+ end
34
+
35
+ # Returns true when the passed name is a known table, we assume known tables also have fixtures
36
+ def known_fixture?(name)
37
+ respond_to?(:fixture_table_names) && fixture_table_names.include?(name.to_s)
38
+ end
39
+
40
+ # Filter calls to fixture methods so we can use them in the definitions
41
+ def method_missing(method, *arguments, &block)
42
+ if known_fixture?(method)
43
+ arguments = arguments.map { |a| a.inspect }
44
+ Test::Spec::Rails::Macros::LazyValue.new("#{method}(#{arguments.join(', ')})")
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ module InstanceMethods
52
+ # Interpret the non-immediate values in params and replace them
53
+ def immediate_values(params)
54
+ result = {}
55
+ params.each do |key, value|
56
+ result[key] = case value
57
+ when Hash
58
+ immediate_values(value)
59
+ when Test::Spec::Rails::Macros::LazyValue
60
+ eval(value.value).to_param
61
+ when Proc
62
+ value.call
63
+ else
64
+ value
65
+ end
66
+ end
67
+ result
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ Test::Spec::TestCase::ClassMethods.send(:include, Test::Spec::Rails::Macros::ClassMethods)
76
+ Test::Spec::TestCase::InstanceMethods.send(:include, Test::Spec::Rails::Macros::InstanceMethods)
77
+
78
+ require 'test/spec/rails/macros/response'
79
+ require 'test/spec/rails/macros/authorization'
@@ -0,0 +1,11 @@
1
+ module Test
2
+ module Spec
3
+ module Rails
4
+ module RequestHelpers
5
+ attr_reader :request
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ ActionController::TestCase.send(:include, Test::Spec::Rails::RequestHelpers)