peck-on-rails 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +193 -2
- data/lib/peck_on_rails.rb +31 -323
- data/lib/peck_on_rails/assertions.rb +182 -0
- data/lib/peck_on_rails/backtrace_cleaning.rb +17 -0
- data/lib/peck_on_rails/controller.rb +152 -0
- data/lib/peck_on_rails/helper.rb +17 -0
- data/lib/peck_on_rails/model.rb +19 -0
- metadata +20 -22
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 983910938947f3b167357b8884551802c68f461b
|
4
|
+
data.tar.gz: 4257a34ffdab95c6d6593a5474ff4a49e42026ff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2da9e480923e109174e60bf29b1e9e18e944147398c1cea15f86949d45be1de947e0358b4c779493eb017b0e6ef4a19e9e7c56e001cbea87476ba2ad2b22d099
|
7
|
+
data.tar.gz: b51f97c1df464400c78878bc81dec1b8ba73cd96cd75c38a698d7777ac9a38bd42faef5835e13b145698bac105feade127aa6f9c5343b200277a9654bb0cfb0b
|
data/README.md
CHANGED
@@ -6,6 +6,197 @@ Peck-On-Rails is an extension for Peck to make testing Rails easier.
|
|
6
6
|
|
7
7
|
## Getting Started
|
8
8
|
|
9
|
-
You can install Peck
|
9
|
+
You can install Peck-On-Rails as a gem. Note that the Peck dependency will automatically be fulfilled.
|
10
10
|
|
11
|
-
$ gem install peck-on-rails
|
11
|
+
$ gem install peck-on-rails
|
12
|
+
|
13
|
+
You can also add it to your Rails application's `Gemfile`.
|
14
|
+
|
15
|
+
group :test do
|
16
|
+
gem 'peck-on-rails'
|
17
|
+
end
|
18
|
+
|
19
|
+
To use it in your Rails application, create or edit a file named `test/test_helper.rb`;
|
20
|
+
|
21
|
+
ENV["RAILS_ENV"] ||= "test"
|
22
|
+
require File.expand_path('../../config/environment', __FILE__)
|
23
|
+
|
24
|
+
require 'peck/flavors/vanilla'
|
25
|
+
require 'peck_on_rails'
|
26
|
+
|
27
|
+
You can read more about Peck formatting and flavors in [Peck's documentation](https://github.com/Fingertips/Peck).
|
28
|
+
|
29
|
+
Don't forget to require your test helper in your test files. We like to require relative to the file so you can use Ruby to run the tests.
|
30
|
+
|
31
|
+
require File.expand_path('../../test_helper', __FILE__)
|
32
|
+
|
33
|
+
describe AuthorsController do
|
34
|
+
should.find.get :index
|
35
|
+
end
|
36
|
+
|
37
|
+
Now just run your test:
|
38
|
+
|
39
|
+
$ ruby test/functional/authors_controller_test.rb
|
40
|
+
.
|
41
|
+
1 spec, 0 failures, finished in 0.13 seconds.
|
42
|
+
|
43
|
+
Alternatively Peck has a CLI tool to run tests for you:
|
44
|
+
|
45
|
+
$ peck test/models/book_test.rb
|
46
|
+
.
|
47
|
+
1 spec, 0 failures, finished in 0.0 seconds.
|
48
|
+
|
49
|
+
## Test types
|
50
|
+
|
51
|
+
Peck-On-Rails automatically supports model, controller, and helper specs. By default it figures out what kind of test your writing by the class found in the context. For example, POR will assume you're operating on an Active Record model when Book inherits from `ActiveRecord::Base`.
|
52
|
+
|
53
|
+
describe "A new", Book do
|
54
|
+
it "does not have any pages" do
|
55
|
+
book = Book.new
|
56
|
+
book.pages.count.should == 0
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
If POR has trouble figuring out what you're doing, you can force it:
|
61
|
+
|
62
|
+
describe Candle, :model do
|
63
|
+
end
|
64
|
+
|
65
|
+
Supported types are `:model`, `:controller`, and `:helper`.
|
66
|
+
|
67
|
+
## Model specs
|
68
|
+
|
69
|
+
POR loads fixtures for `:model` type tests. It also does this for `:controller` and `:helper` tests by the way. This also means it doesn't load fixtures for library specs.
|
70
|
+
|
71
|
+
There is one little matcher that you get in your model specs: a validation matcher.
|
72
|
+
|
73
|
+
describe Book do
|
74
|
+
it "requires a title" do
|
75
|
+
book = Book.new
|
76
|
+
|
77
|
+
book.should.not.validate_with(:title, nil)
|
78
|
+
book.should.not.validate_with(:title, '')
|
79
|
+
|
80
|
+
book.should.validate_with(:title, 'The Fault in Our Stars')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Note that this matcher is a bit dirty as it changes your instance:
|
85
|
+
|
86
|
+
describe Book do
|
87
|
+
it "requires a title" do
|
88
|
+
book = Book.new
|
89
|
+
book.should.not.validate_with(:title, nil)
|
90
|
+
p book.errors.full_messages
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
## Helper specs
|
95
|
+
|
96
|
+
In `:helper` specs you automatically get your helper module included.
|
97
|
+
|
98
|
+
describe BooksHelper do
|
99
|
+
it "formats titles" do
|
100
|
+
book = Book.new(title: 'Little Pinguin', :number = 12)
|
101
|
+
format_book_title(book).should == "12. Little Pinguin"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
## Controller specs
|
106
|
+
|
107
|
+
For controller specs you get a `@controller` instance, a `controller` accessor, routing, and some nifty helpers.
|
108
|
+
|
109
|
+
describe "On the", BooksController, "a visitor" do
|
110
|
+
it "sees an overview of recent books" do
|
111
|
+
get :index
|
112
|
+
status.should == :ok
|
113
|
+
templates.should.include 'books/index'
|
114
|
+
body.should.match_css 'h1' # Only possible when Nokogiri is installed
|
115
|
+
body.should.match_xpath '//h1' # Only possible when Nokogiri is installed
|
116
|
+
body.document # Returns the Nokogiri Document
|
117
|
+
end
|
118
|
+
|
119
|
+
it "sees and overview of recent books in JSON" do
|
120
|
+
get :index, :format => 'json'
|
121
|
+
body.should.not.be.blank
|
122
|
+
body.json.keys.should.include 'books'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
On top of that you get some macro's to generate specs which will test basic functions of the controller.
|
127
|
+
|
128
|
+
describe "On the", Manage::BooksController, "a visitor" do
|
129
|
+
should.require_login :index
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "On the", Manage::BooksController, "a regular member" do
|
133
|
+
should.disallow :index
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "On the", Manage::BooksController, "an admin" do
|
137
|
+
should.find :index
|
138
|
+
end
|
139
|
+
|
140
|
+
These generated specs assume there are three methods defined on the spec: `login_required?`, `disallowed?`, and `allowed?`. We usually define them as follows:
|
141
|
+
|
142
|
+
module TestHelper
|
143
|
+
module Authentication
|
144
|
+
def login_required?
|
145
|
+
if @request.format == 'text/html'
|
146
|
+
@response.location == new_session_url
|
147
|
+
else
|
148
|
+
@response.status == 401
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def allowed?
|
153
|
+
@response.status == 200
|
154
|
+
end
|
155
|
+
|
156
|
+
def disallowed?
|
157
|
+
@response.status == 403
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Auto include these methods on all `:controller` context instances.
|
163
|
+
Peck::Context.once do |context|
|
164
|
+
case context_type
|
165
|
+
when :controller
|
166
|
+
extend TestHelper::Authentication
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
## Writing matchers
|
171
|
+
|
172
|
+
Right now you have to open up the `Peck::Should` class to do this:
|
173
|
+
|
174
|
+
class Peck
|
175
|
+
class Should
|
176
|
+
def validate_with(attribute, value)
|
177
|
+
message = "Expected #{!@negated ? 'no' : ''}errors on" +
|
178
|
+
" #{attribute.inspect} with value `#{value.inspect}' after validation"
|
179
|
+
|
180
|
+
@this.send("#{attribute}=", value)
|
181
|
+
@this.valid?
|
182
|
+
if @this.errors[attribute].kind_of?(Array)
|
183
|
+
satisfy(message) { @this.errors[attribute].empty? }
|
184
|
+
else
|
185
|
+
satisfy(message) { @this.errors[attribute].nil? }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
## Adding before or after methods for all specs
|
192
|
+
|
193
|
+
You can use Peck's callback which is ran for each context once:
|
194
|
+
|
195
|
+
Peck::Context.once do |context|
|
196
|
+
class_eval do
|
197
|
+
before do
|
198
|
+
FileUtils.rm_rf(my_nice_directory)
|
199
|
+
FileUtils.mkdir_p(my_nice_directory)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
data/lib/peck_on_rails.rb
CHANGED
@@ -6,195 +6,25 @@ require 'active_support/core_ext/kernel/reporting'
|
|
6
6
|
require 'active_support/deprecation'
|
7
7
|
require 'rails/backtrace_cleaner'
|
8
8
|
|
9
|
+
require 'peck_on_rails/assertions'
|
10
|
+
require 'peck_on_rails/backtrace_cleaning'
|
11
|
+
require 'peck_on_rails/controller'
|
12
|
+
require 'peck_on_rails/helper'
|
13
|
+
require 'peck_on_rails/model'
|
14
|
+
|
9
15
|
class Peck
|
10
16
|
class Rails
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def clean_backtrace(backtrace)
|
15
|
-
super Peck::Rails::BacktraceCleaning.backtrace_cleaner.clean(backtrace)
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.backtrace_cleaner
|
19
|
-
::Rails.backtrace_cleaner
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Context
|
24
|
-
def self.init(context, context_type, subject)
|
25
|
-
Peck.log("Peck::Rails::Context.init")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Helper
|
30
|
-
def self.init(context, context_type, subject)
|
31
|
-
if [:helper].include?(context_type)
|
32
|
-
Peck.log("Peck::Rails::Helper.init")
|
33
|
-
context.class_eval do
|
34
|
-
include subject
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Model
|
41
|
-
def self.init(context, context_type, subject)
|
42
|
-
if [:model, :controller, :helper].include?(context_type)
|
43
|
-
Peck.log("Peck::Rails::Model.init")
|
44
|
-
context.class_eval do
|
45
|
-
include ::ActiveRecord::TestFixtures
|
46
|
-
self.fixture_path = File.join(::Rails.root, 'test', 'fixtures')
|
47
|
-
fixtures :all
|
48
|
-
define_method(:method_name) { self.class.label }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
17
|
+
def self.dev_null
|
18
|
+
@dev_null ||= File.open('/dev/null', 'w')
|
52
19
|
end
|
53
20
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
before do
|
62
|
-
@routes = ::Rails.application.routes
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.determine_default_controller_class(name)
|
66
|
-
self._controller_class = Peck::Rails.subject(self)
|
67
|
-
end
|
68
|
-
|
69
|
-
include ::ActionController::TestCase::Behavior
|
70
|
-
include ::Peck::Rails::Controller::Helpers
|
71
|
-
extend ::Peck::Rails::Controller::Fixtures
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Stores expression to be evaluated later in the correct context
|
77
|
-
class LazyValue
|
78
|
-
def initialize(expression)
|
79
|
-
@expression = expression
|
80
|
-
end
|
81
|
-
|
82
|
-
def to_param(spec)
|
83
|
-
spec.instance_eval(@expression)
|
84
|
-
end
|
85
|
-
|
86
|
-
def inspect
|
87
|
-
@expression
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
module Fixtures
|
92
|
-
# Returns true when the passed name is a known table, we assume known tables also have fixtures
|
93
|
-
def known_fixture?(name)
|
94
|
-
respond_to?(:fixture_table_names) && fixture_table_names.include?(name.to_s)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Filter calls to fixture methods so we can use them in the context definition
|
98
|
-
def method_missing(method, *arguments, &block)
|
99
|
-
if known_fixture?(method)
|
100
|
-
arguments = arguments.map { |a| a.inspect }
|
101
|
-
::Peck::Rails::Controller::LazyValue.new("#{method}(#{arguments.join(', ')})")
|
102
|
-
else
|
103
|
-
super
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
module Helpers
|
109
|
-
def status
|
110
|
-
Peck::Rails::Controller::Status.new(@response)
|
111
|
-
end
|
112
|
-
|
113
|
-
def templates
|
114
|
-
# Force body to be read in case the template is being streamed
|
115
|
-
response.body
|
116
|
-
(@templates || @_templates).keys
|
117
|
-
end
|
118
|
-
|
119
|
-
def body
|
120
|
-
Peck::Rails::Controller::Body.new(@response)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Interpret the non-immediate values in params and replace them
|
124
|
-
def immediate_values(params)
|
125
|
-
result = {}
|
126
|
-
params.each do |key, value|
|
127
|
-
result[key] = case value
|
128
|
-
when Hash
|
129
|
-
immediate_values(value)
|
130
|
-
when ::Peck::Rails::Controller::LazyValue
|
131
|
-
value.to_param(self)
|
132
|
-
when Proc
|
133
|
-
value.call
|
134
|
-
else
|
135
|
-
value
|
136
|
-
end
|
137
|
-
end
|
138
|
-
result
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
class Status
|
143
|
-
def initialize(response)
|
144
|
-
@response = response
|
145
|
-
end
|
146
|
-
|
147
|
-
def ==(other)
|
148
|
-
case other
|
149
|
-
when Numeric
|
150
|
-
@response.status == other
|
151
|
-
else
|
152
|
-
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[other]
|
153
|
-
@response.status === code
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
def inspect
|
158
|
-
"#<Peck::Rails::Controller::Status:#{@response.status}>"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
class Body
|
163
|
-
def initialize(response)
|
164
|
-
@response = response
|
165
|
-
end
|
166
|
-
|
167
|
-
def document
|
168
|
-
if defined?(:Nokogiri)
|
169
|
-
@document ||= Nokogiri::HTML.parse(@response.body)
|
170
|
-
else
|
171
|
-
raise RuntimeError, "Please install Nokogiri to use the CSS or Xpath matchers (gem install nokogiri)"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def json
|
176
|
-
if defined?(:JSON)
|
177
|
-
@json ||= JSON.parse(@response.body)
|
178
|
-
else
|
179
|
-
raise RuntimeError, "Please install a JSON gem to use the json accessor (gem install json)"
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def match_css?(query)
|
184
|
-
!document.css(query).empty?
|
185
|
-
end
|
186
|
-
|
187
|
-
def match_xpath?(query)
|
188
|
-
!document.xpath(query).empty?
|
189
|
-
end
|
190
|
-
|
191
|
-
def blank?
|
192
|
-
@response.body.blank?
|
193
|
-
end
|
194
|
-
|
195
|
-
def inspect
|
196
|
-
"#<html body=\"#{@response.body}\">"
|
197
|
-
end
|
21
|
+
def self.silence
|
22
|
+
stdout, stderr = $stdout, $stderr
|
23
|
+
$stdout, $stderr = dev_null, dev_null
|
24
|
+
begin
|
25
|
+
yield
|
26
|
+
ensure
|
27
|
+
$stdout, $stderr = stdout, stderr
|
198
28
|
end
|
199
29
|
end
|
200
30
|
|
@@ -213,9 +43,9 @@ class Peck
|
|
213
43
|
def self.context_type_for_subject(context, subject)
|
214
44
|
if subject.nil?
|
215
45
|
:plain
|
216
|
-
elsif subject < ActionController::Base
|
46
|
+
elsif defined?(ActionController) && subject < ActionController::Base
|
217
47
|
:controller
|
218
|
-
elsif subject < ActiveRecord::Base
|
48
|
+
elsif defined?(ActiveRecord) && subject < ActiveRecord::Base
|
219
49
|
:model
|
220
50
|
elsif subject.name =~ HELPER_RE
|
221
51
|
:helper
|
@@ -233,12 +63,15 @@ class Peck
|
|
233
63
|
end
|
234
64
|
|
235
65
|
def self.init
|
66
|
+
if defined?(ActiveRecord)
|
67
|
+
Peck.log("Migrate database if necessary")
|
68
|
+
ActiveRecord::Migration.load_schema_if_pending!
|
69
|
+
end
|
236
70
|
Peck.log("Setting up Peck::Rails")
|
237
71
|
proc do |context|
|
238
72
|
subject = Peck::Rails.subject(context)
|
239
73
|
context_type = Peck::Rails.context_type(context, subject)
|
240
74
|
[
|
241
|
-
Peck::Rails::Context,
|
242
75
|
Peck::Rails::Helper,
|
243
76
|
Peck::Rails::Model,
|
244
77
|
Peck::Rails::Controller
|
@@ -247,132 +80,23 @@ class Peck
|
|
247
80
|
end
|
248
81
|
|
249
82
|
context.before do
|
250
|
-
|
83
|
+
if respond_to?(:setup_fixtures)
|
84
|
+
begin
|
85
|
+
setup_fixtures
|
86
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
87
|
+
end
|
88
|
+
end
|
251
89
|
end
|
252
90
|
|
253
91
|
context.after do
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
class Peck
|
262
|
-
class Should
|
263
|
-
class ResponseRequirement < Peck::Should::Proxy
|
264
|
-
SUPPORTED_VERBS = [:get, :post, :put, :delete, :options]
|
265
|
-
|
266
|
-
attr_accessor :method, :exception, :expected
|
267
|
-
|
268
|
-
def define_specification(verb, action, params={})
|
269
|
-
_method = self.method
|
270
|
-
_negated = self.negated
|
271
|
-
_expected = self.expected
|
272
|
-
_exception = self.exception
|
273
|
-
context.it(description(verb, action, params)) do
|
274
|
-
begin
|
275
|
-
send(verb, action, immediate_values(params))
|
276
|
-
rescue => raised_exception
|
277
|
-
if _negated
|
278
|
-
raised_exception.should.be.kind_of(_exception)
|
279
|
-
else
|
280
|
-
raised_exception.should.not.be.kind_of(_exception)
|
281
|
-
end
|
282
|
-
else
|
283
|
-
if _negated
|
284
|
-
send(_method).should.not == _expected
|
285
|
-
else
|
286
|
-
send(_method).should == _expected
|
92
|
+
if respond_to?(:teardown_fixtures)
|
93
|
+
begin
|
94
|
+
teardown_fixtures
|
95
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
287
96
|
end
|
288
97
|
end
|
289
98
|
end
|
290
99
|
end
|
291
|
-
|
292
|
-
def method_missing(method, *attributes, &block)
|
293
|
-
verb = method.to_sym
|
294
|
-
if self.class.supported_verbs.include?(verb)
|
295
|
-
define_specification(verb, *attributes)
|
296
|
-
else
|
297
|
-
super
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
def self.supported_verbs
|
302
|
-
SUPPORTED_VERBS
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
class RequireLogin < ResponseRequirement
|
307
|
-
def description(verb, action, params={})
|
308
|
-
description = []
|
309
|
-
description << "should not" if (negated == false)
|
310
|
-
description << "#{verb.upcase}s `#{action}' without logging in"
|
311
|
-
description << "#{params.inspect}" unless params.blank?
|
312
|
-
description.join(' ')
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
class Disallow < ResponseRequirement
|
317
|
-
def description(verb, action, params={})
|
318
|
-
description = ["should"]
|
319
|
-
description << "not" if (negated == false)
|
320
|
-
description << "be allowed to #{verb.upcase}s `#{action}'"
|
321
|
-
description << "#{params.inspect}" unless params.blank?
|
322
|
-
description.join(' ')
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
class Response < ResponseRequirement
|
327
|
-
attr_accessor :verb_description
|
328
|
-
|
329
|
-
def description(verb, action, params={})
|
330
|
-
description = ["should"]
|
331
|
-
description << "not" if (negated == false)
|
332
|
-
description << "#{verb_description} `#{action}'"
|
333
|
-
description << "#{params.inspect}" unless params.blank?
|
334
|
-
description.join(' ')
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
class Specification
|
339
|
-
def require_login
|
340
|
-
requirement = RequireLogin.new(context)
|
341
|
-
requirement.negated = @negated
|
342
|
-
requirement.method = :login_required?
|
343
|
-
requirement.expected = true
|
344
|
-
requirement
|
345
|
-
end
|
346
|
-
|
347
|
-
def disallow
|
348
|
-
requirement = Disallow.new(context)
|
349
|
-
requirement.negated = @negated
|
350
|
-
requirement.method = :disallowed?
|
351
|
-
requirement.expected = true
|
352
|
-
requirement
|
353
|
-
end
|
354
|
-
|
355
|
-
def allow
|
356
|
-
requirement = Disallow.new(context)
|
357
|
-
requirement.negated = @negated
|
358
|
-
requirement.method = :allowed?
|
359
|
-
requirement.expected = true
|
360
|
-
requirement
|
361
|
-
end
|
362
|
-
|
363
|
-
def find
|
364
|
-
requirement = Response.new(context)
|
365
|
-
requirement.negated = @negated
|
366
|
-
requirement.verb_description = 'find'
|
367
|
-
requirement.method = :status
|
368
|
-
if @negated
|
369
|
-
requirement.expected = :not_found
|
370
|
-
requirement.exception = ActiveRecord::RecordNotFound
|
371
|
-
else
|
372
|
-
requirement.expected = :ok
|
373
|
-
end
|
374
|
-
requirement
|
375
|
-
end
|
376
100
|
end
|
377
101
|
end
|
378
102
|
end
|
@@ -385,20 +109,4 @@ class Peck
|
|
385
109
|
end
|
386
110
|
end
|
387
111
|
|
388
|
-
class Peck
|
389
|
-
class Should
|
390
|
-
def validate_with(attribute, value)
|
391
|
-
message = "Expected #{!@negated ? 'no' : ''}errors on #{attribute.inspect} with value `#{value.inspect}' after validation"
|
392
|
-
|
393
|
-
@this.send("#{attribute}=", value)
|
394
|
-
@this.valid?
|
395
|
-
if @this.errors[attribute].kind_of?(Array)
|
396
|
-
satisfy(message) { @this.errors[attribute].empty? }
|
397
|
-
else
|
398
|
-
satisfy(message) { @this.errors[attribute].nil? }
|
399
|
-
end
|
400
|
-
end
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
112
|
Peck::Context.once(&Peck::Rails.init)
|
@@ -0,0 +1,182 @@
|
|
1
|
+
class Peck
|
2
|
+
class Should
|
3
|
+
class ResponseRequirement < Peck::Should::Proxy
|
4
|
+
SUPPORTED_VERBS = [:get, :post, :put, :delete, :options]
|
5
|
+
|
6
|
+
attr_accessor :method, :allowed_exceptions, :expected
|
7
|
+
|
8
|
+
def define_specification(verb, action, params={})
|
9
|
+
_method = self.method
|
10
|
+
_negated = self.negated
|
11
|
+
_expected = self.expected
|
12
|
+
_allowed_exceptions = self.allowed_exceptions
|
13
|
+
context.it(description(verb, action, params)) do
|
14
|
+
begin
|
15
|
+
send(verb, action, immediate_values(params))
|
16
|
+
rescue => raised_exception
|
17
|
+
if _allowed_exceptions
|
18
|
+
_allowed_exceptions.any? { |exception| raised_exception.should.be.kind_of(exception) }
|
19
|
+
true.should == true # Force the expectations counter
|
20
|
+
else
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
else
|
24
|
+
if _negated
|
25
|
+
send(_method).should.not == _expected
|
26
|
+
else
|
27
|
+
send(_method).should == _expected
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method, *attributes, &block)
|
34
|
+
verb = method.to_sym
|
35
|
+
if self.class.supported_verbs.include?(verb)
|
36
|
+
define_specification(verb, *attributes)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.supported_verbs
|
43
|
+
SUPPORTED_VERBS
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class RequireLogin < ResponseRequirement
|
48
|
+
def description(verb, action, params={})
|
49
|
+
description = []
|
50
|
+
description << "should not" if (negated == false)
|
51
|
+
description << "#{verb.upcase}s `#{action}' without logging in"
|
52
|
+
description << "#{params.inspect}" unless params.blank?
|
53
|
+
description.join(' ')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Disallow < ResponseRequirement
|
58
|
+
def description(verb, action, params={})
|
59
|
+
description = ["should"]
|
60
|
+
description << "not" if (negated == false)
|
61
|
+
description << "be allowed to #{verb.upcase}s `#{action}'"
|
62
|
+
description << "#{params.inspect}" unless params.blank?
|
63
|
+
description.join(' ')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Response < ResponseRequirement
|
68
|
+
attr_accessor :verb_description
|
69
|
+
|
70
|
+
def description(verb, action, params={})
|
71
|
+
description = ["should"]
|
72
|
+
description << "not" if (negated == false)
|
73
|
+
description << "#{verb_description} `#{action}'"
|
74
|
+
description << "#{params.inspect}" unless params.blank?
|
75
|
+
description.join(' ')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Specification
|
80
|
+
def self.allowed_exceptions
|
81
|
+
allowed_exceptions = []
|
82
|
+
if defined?(ActiveRecord)
|
83
|
+
allowed_exceptions << ActiveRecord::RecordNotFound
|
84
|
+
end
|
85
|
+
if defined?(AbstractController)
|
86
|
+
allowed_exceptions << AbstractController::ActionNotFound
|
87
|
+
end
|
88
|
+
allowed_exceptions
|
89
|
+
end
|
90
|
+
ALLOWED_EXCEPTIONS = allowed_exceptions
|
91
|
+
|
92
|
+
def require_login
|
93
|
+
requirement = RequireLogin.new(context)
|
94
|
+
requirement.negated = @negated
|
95
|
+
requirement.method = :login_required?
|
96
|
+
requirement.expected = true
|
97
|
+
requirement
|
98
|
+
end
|
99
|
+
|
100
|
+
def disallow
|
101
|
+
requirement = Disallow.new(context)
|
102
|
+
requirement.negated = @negated
|
103
|
+
requirement.method = :disallowed?
|
104
|
+
requirement.expected = true
|
105
|
+
requirement
|
106
|
+
end
|
107
|
+
|
108
|
+
def allow
|
109
|
+
requirement = Disallow.new(context)
|
110
|
+
requirement.negated = @negated
|
111
|
+
requirement.method = :allowed?
|
112
|
+
requirement.expected = true
|
113
|
+
requirement
|
114
|
+
end
|
115
|
+
|
116
|
+
def find
|
117
|
+
requirement = Response.new(context)
|
118
|
+
requirement.verb_description = 'find'
|
119
|
+
requirement.method = :status
|
120
|
+
if @negated
|
121
|
+
requirement.expected = :not_found
|
122
|
+
requirement.allowed_exceptions = ALLOWED_EXCEPTIONS
|
123
|
+
else
|
124
|
+
requirement.expected = :ok
|
125
|
+
end
|
126
|
+
requirement
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def equal_record_set(*others)
|
131
|
+
left = @this.flatten
|
132
|
+
right = others.flatten
|
133
|
+
|
134
|
+
message = "Expected the record set to be #{!@negated ? 'equal' : 'unequal'}: #{left.map(&:id).inspect} - #{right.map(&:id).inspect}"
|
135
|
+
satisfy(message) { Set.new(left) == Set.new(right) }
|
136
|
+
end
|
137
|
+
|
138
|
+
def equal_record_array(*others)
|
139
|
+
left = @this.flatten
|
140
|
+
right = others.flatten
|
141
|
+
|
142
|
+
message = "Expected the array of records to be #{!@negated ? 'equal' : 'unequal'}: #{left.map(&:id).inspect} - #{right.map(&:id).inspect}"
|
143
|
+
satisfy(message) { left == right }
|
144
|
+
end
|
145
|
+
|
146
|
+
def equal_set(*others)
|
147
|
+
left = @this.flatten
|
148
|
+
right = others.flatten
|
149
|
+
|
150
|
+
message = "Expected sets to be #{!@negated ? 'equal' : 'unequal'}: #{left.inspect} - #{right.inspect}"
|
151
|
+
satisfy(message) { Set.new(left) == Set.new(right) }
|
152
|
+
end
|
153
|
+
|
154
|
+
def equal_keys(other)
|
155
|
+
left = @this.keys.map(&:to_s).sort
|
156
|
+
right = other.map(&:to_s).sort
|
157
|
+
|
158
|
+
missing_from_left = left - right
|
159
|
+
missing_from_right = right - left
|
160
|
+
message = "Expected the object to #{!@negated ? 'have' : 'not have'} the same keys:\n#{left.inspect}\n#{right.inspect}\n>>> #{missing_from_left.inspect}\n<<< #{missing_from_right.inspect}"
|
161
|
+
satisfy(message) { left == right }
|
162
|
+
end
|
163
|
+
|
164
|
+
def redirect_to(somewhere)
|
165
|
+
message = "Expected to redirect to `#{somewhere}'"
|
166
|
+
response = @this.send(:response)
|
167
|
+
satisfy(message) { [301, 302].include?(response.status.to_i) && response.location == somewhere }
|
168
|
+
end
|
169
|
+
|
170
|
+
def validate_with(attribute, value)
|
171
|
+
message = "Expected #{!@negated ? 'no' : ''}errors on #{attribute.inspect} with value `#{value.inspect}' after validation"
|
172
|
+
|
173
|
+
@this.send("#{attribute}=", value)
|
174
|
+
@this.valid?
|
175
|
+
if @this.errors[attribute].kind_of?(Array)
|
176
|
+
satisfy(message) { @this.errors[attribute].empty? }
|
177
|
+
else
|
178
|
+
satisfy(message) { @this.errors[attribute].nil? }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Peck
|
4
|
+
class Rails
|
5
|
+
module BacktraceCleaning
|
6
|
+
protected
|
7
|
+
|
8
|
+
def clean_backtrace(backtrace)
|
9
|
+
super Peck::Rails::BacktraceCleaning.backtrace_cleaner.clean(backtrace)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.backtrace_cleaner
|
13
|
+
::Rails.backtrace_cleaner
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
class Peck
|
2
|
+
class Rails
|
3
|
+
class Controller
|
4
|
+
def self.init(context, context_type, subject)
|
5
|
+
if context_type == :controller
|
6
|
+
Peck.log("Peck::Rails::Controller.init")
|
7
|
+
context.class_eval do
|
8
|
+
attr_accessor :controller
|
9
|
+
|
10
|
+
before do
|
11
|
+
@routes = ::Rails.application.routes
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.determine_default_controller_class(name)
|
15
|
+
self._controller_class = Peck::Rails.subject(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
Peck::Rails.silence do
|
19
|
+
include ::ActionController::TestCase::Behavior
|
20
|
+
end
|
21
|
+
include ::Peck::Rails::Controller::Helpers
|
22
|
+
extend ::Peck::Rails::Controller::Fixtures
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Stores expression to be evaluated later in the correct context
|
28
|
+
class LazyValue
|
29
|
+
def initialize(expression)
|
30
|
+
@expression = expression
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_param(spec)
|
34
|
+
spec.instance_eval(@expression)
|
35
|
+
end
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
@expression
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Fixtures
|
43
|
+
# Returns true when the passed name is a known table, we assume known tables also have fixtures
|
44
|
+
def known_fixture?(name)
|
45
|
+
respond_to?(:fixture_table_names) && fixture_table_names.include?(name.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Filter calls to fixture methods so we can use them in the context definition
|
49
|
+
def method_missing(method, *arguments, &block)
|
50
|
+
if known_fixture?(method)
|
51
|
+
arguments = arguments.map { |a| a.inspect }
|
52
|
+
::Peck::Rails::Controller::LazyValue.new("#{method}(#{arguments.join(', ')})")
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module Helpers
|
60
|
+
def status
|
61
|
+
Peck::Rails::Controller::Status.new(@response)
|
62
|
+
end
|
63
|
+
|
64
|
+
def templates
|
65
|
+
# Force body to be read in case the template is being streamed
|
66
|
+
response.body
|
67
|
+
(@templates || @_templates).keys
|
68
|
+
end
|
69
|
+
|
70
|
+
def body
|
71
|
+
Peck::Rails::Controller::Body.new(@response)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Interpret the non-immediate values in params and replace them
|
75
|
+
def immediate_values(params)
|
76
|
+
result = {}
|
77
|
+
params.each do |key, value|
|
78
|
+
result[key] = case value
|
79
|
+
when Hash
|
80
|
+
immediate_values(value)
|
81
|
+
when ::Peck::Rails::Controller::LazyValue
|
82
|
+
value.to_param(self)
|
83
|
+
when Proc
|
84
|
+
value.call
|
85
|
+
else
|
86
|
+
value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Status
|
94
|
+
def initialize(response)
|
95
|
+
@response = response
|
96
|
+
end
|
97
|
+
|
98
|
+
def ==(other)
|
99
|
+
case other
|
100
|
+
when Numeric
|
101
|
+
@response.status == other
|
102
|
+
else
|
103
|
+
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[other]
|
104
|
+
@response.status === code
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def inspect
|
109
|
+
"#<Peck::Rails::Controller::Status:#{@response.status}>"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Body
|
114
|
+
def initialize(response)
|
115
|
+
@response = response
|
116
|
+
end
|
117
|
+
|
118
|
+
def document
|
119
|
+
if defined?(Nokogiri)
|
120
|
+
@document ||= Nokogiri::HTML.parse(@response.body)
|
121
|
+
else
|
122
|
+
raise RuntimeError, "Please install Nokogiri to use the CSS or Xpath matchers (gem install nokogiri)"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def json
|
127
|
+
if defined?(JSON)
|
128
|
+
@json ||= JSON.parse(@response.body)
|
129
|
+
else
|
130
|
+
raise RuntimeError, "Please install a JSON gem to use the json accessor (gem install json)"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def match_css?(query)
|
135
|
+
!document.css(query).empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
def match_xpath?(query)
|
139
|
+
!document.xpath(query).empty?
|
140
|
+
end
|
141
|
+
|
142
|
+
def blank?
|
143
|
+
@response.body.blank?
|
144
|
+
end
|
145
|
+
|
146
|
+
def inspect
|
147
|
+
"#<html body=\"#{@response.body}\">"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
class Peck
|
5
|
+
class Rails
|
6
|
+
class Helper
|
7
|
+
def self.init(context, context_type, subject)
|
8
|
+
if [:helper].include?(context_type)
|
9
|
+
Peck.log("Peck::Rails::Helper.init")
|
10
|
+
context.class_eval do
|
11
|
+
include subject
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Peck
|
2
|
+
class Rails
|
3
|
+
class Model
|
4
|
+
def self.init(context, context_type, subject)
|
5
|
+
if [:model, :controller, :helper].include?(context_type)
|
6
|
+
Peck.log("Peck::Rails::Model.init")
|
7
|
+
context.class_eval do
|
8
|
+
if defined?(::ActiveRecord)
|
9
|
+
include ::ActiveRecord::TestFixtures
|
10
|
+
end
|
11
|
+
self.fixture_path = File.join(::Rails.root, 'test', 'fixtures')
|
12
|
+
fixtures :all
|
13
|
+
define_method(:method_name) { self.class.label }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,84 +1,82 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peck-on-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Manfred Stienstra
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-10-23 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: peck
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rails
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
|
-
description:
|
47
|
-
|
48
|
-
'
|
41
|
+
description: |2
|
42
|
+
Peck-On-Rails is an extension for Peck to make testing Rails easier.
|
49
43
|
email: manfred@fngtps.com
|
50
44
|
executables: []
|
51
45
|
extensions: []
|
52
46
|
extra_rdoc_files:
|
53
47
|
- COPYING
|
54
48
|
files:
|
55
|
-
- lib/peck_on_rails.rb
|
56
49
|
- COPYING
|
57
50
|
- README.md
|
51
|
+
- lib/peck_on_rails.rb
|
52
|
+
- lib/peck_on_rails/assertions.rb
|
53
|
+
- lib/peck_on_rails/backtrace_cleaning.rb
|
54
|
+
- lib/peck_on_rails/controller.rb
|
55
|
+
- lib/peck_on_rails/helper.rb
|
56
|
+
- lib/peck_on_rails/model.rb
|
58
57
|
homepage:
|
59
58
|
licenses: []
|
59
|
+
metadata: {}
|
60
60
|
post_install_message:
|
61
61
|
rdoc_options:
|
62
|
-
- --charset=utf-8
|
62
|
+
- "--charset=utf-8"
|
63
63
|
require_paths:
|
64
64
|
- lib
|
65
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
66
|
requirements:
|
68
|
-
- -
|
67
|
+
- - ">="
|
69
68
|
- !ruby/object:Gem::Version
|
70
69
|
version: '0'
|
71
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
71
|
requirements:
|
74
|
-
- -
|
72
|
+
- - ">="
|
75
73
|
- !ruby/object:Gem::Version
|
76
74
|
version: '0'
|
77
75
|
requirements: []
|
78
76
|
rubyforge_project:
|
79
|
-
rubygems_version:
|
77
|
+
rubygems_version: 2.2.2
|
80
78
|
signing_key:
|
81
|
-
specification_version:
|
79
|
+
specification_version: 4
|
82
80
|
summary: Peck-On-Rails adds useful helpers and context extensions to make testing
|
83
81
|
Ruby on Rails apps easier.
|
84
82
|
test_files: []
|