1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
|
require File.dirname(__FILE__) + '/assertions'
require File.dirname(__FILE__) + '/deprecated_assertions'
module ActionController #:nodoc:
class Base
# Process a test request called with a +TestRequest+ object.
def self.process_test(request)
new.process_test(request)
end
def process_test(request) #:nodoc:
process(request, TestResponse.new)
end
def process_with_test(*args)
returning process_without_test(*args) do
add_variables_to_assigns
end
end
alias_method :process_without_test, :process
alias_method :process, :process_with_test
end
class TestRequest < AbstractRequest #:nodoc:
attr_accessor :cookies, :session_options
attr_accessor :query_parameters, :request_parameters, :path, :session, :env
attr_accessor :host
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
@query_parameters = query_parameters || {}
@request_parameters = request_parameters || {}
@session = session || TestSession.new
initialize_containers
initialize_default_values
super()
end
def reset_session
@session = {}
end
def port=(number)
@env["SERVER_PORT"] = number.to_i
@port_as_int = nil
end
def action=(action_name)
@query_parameters.update({ "action" => action_name })
@parameters = nil
end
# Used to check AbstractRequest's request_uri functionality.
# Disables the use of @path and @request_uri so superclass can handle those.
def set_REQUEST_URI(value)
@env["REQUEST_URI"] = value
@request_uri = nil
@path = nil
end
def request_uri=(uri)
@request_uri = uri
@path = uri.split("?").first
end
def remote_addr=(addr)
@env['REMOTE_ADDR'] = addr
end
def remote_addr
@env['REMOTE_ADDR']
end
def request_uri
@request_uri || super()
end
def path
@path || super()
end
def assign_parameters(controller_path, action, parameters)
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
non_path_parameters = get? ? query_parameters : request_parameters
parameters.each do |key, value|
if value.is_a? Fixnum
value = value.to_s
elsif value.is_a? Array
value = ActionController::Routing::PathComponent::Result.new(value)
end
if extra_keys.include?(key.to_sym)
non_path_parameters[key] = value
else
path_parameters[key.to_s] = value
end
end
end
def recycle!
self.request_parameters = {}
self.query_parameters = {}
self.path_parameters = {}
end
private
def initialize_containers
@env, @cookies = {}, {}
end
def initialize_default_values
@host = "test.host"
@request_uri = "/"
self.remote_addr = "0.0.0.0"
@env["SERVER_PORT"] = 80
@env['REQUEST_METHOD'] = "GET"
end
end
class TestResponse < AbstractResponse #:nodoc:
# the response code of the request
def response_code
headers['Status'][0,3].to_i rescue 0
end
# returns a String to ensure compatibility with Net::HTTPResponse
def code
headers['Status'].to_s.split(' ')[0]
end
def message
headers['Status'].to_s.split(' ',2)[1]
end
# was the response successful?
def success?
response_code == 200
end
# was the URL not found?
def missing?
response_code == 404
end
# were we redirected?
def redirect?
(300..399).include?(response_code)
end
# was there a server-side error?
def error?
(500..599).include?(response_code)
end
alias_method :server_error?, :error?
# returns the redirection location or nil
def redirect_url
redirect? ? headers['location'] : nil
end
# does the redirect location match this regexp pattern?
def redirect_url_match?( pattern )
return false if redirect_url.nil?
p = Regexp.new(pattern) if pattern.class == String
p = pattern if pattern.class == Regexp
return false if p.nil?
p.match(redirect_url) != nil
end
# returns the template path of the file which was used to
# render this response (or nil)
def rendered_file(with_controller=false)
unless template.first_render.nil?
unless with_controller
template.first_render
else
template.first_render.split('/').last || template.first_render
end
end
end
# was this template rendered by a file?
def rendered_with_file?
!rendered_file.nil?
end
# a shortcut to the flash (or an empty hash if no flash.. hey! that rhymes!)
def flash
session['flash'] || {}
end
# do we have a flash?
def has_flash?
!session['flash'].empty?
end
# do we have a flash that has contents?
def has_flash_with_contents?
!flash.empty?
end
# does the specified flash object exist?
def has_flash_object?(name=nil)
!flash[name].nil?
end
# does the specified object exist in the session?
def has_session_object?(name=nil)
!session[name].nil?
end
# a shortcut to the template.assigns
def template_objects
template.assigns || {}
end
# does the specified template object exist?
def has_template_object?(name=nil)
!template_objects[name].nil?
end
# Returns the response cookies, converted to a Hash of (name => CGI::Cookie) pairs
# Example:
#
# assert_equal ['AuthorOfNewPage'], r.cookies['author'].value
def cookies
headers['cookie'].inject({}) { |hash, cookie| hash[cookie.name] = cookie; hash }
end
# Returns binary content (downloadable file), converted to a String
def binary_content
raise "Response body is not a Proc: #{body.inspect}" unless body.kind_of?(Proc)
require 'stringio'
sio = StringIO.new
begin
$stdout = sio
body.call
ensure
$stdout = STDOUT
end
sio.rewind
sio.read
end
end
class TestSession #:nodoc:
def initialize(attributes = {})
@attributes = attributes
end
def [](key)
@attributes[key]
end
def []=(key, value)
@attributes[key] = value
end
def session_id
""
end
def update() end
def close() end
def delete() @attributes = {} end
end
module TestProcess
private
def self.included(base)
# execute the request simulating a specific http method and set/volley the response
%w( get post put delete head ).each do |method|
base.class_eval <<-EOV, __FILE__, __LINE__
def #{method}(action, parameters = nil, session = nil, flash = nil)
@request.env['REQUEST_METHOD'] = "#{method.upcase}" if @request
process(action, parameters, session, flash)
end
EOV
end
end
# execute the request and set/volley the response
def process(action, parameters = nil, session = nil, flash = nil)
# Sanity check for required instance variables so we can give an
# understandable error message.
%w(controller request response).each do |iv_name|
raise "@#{iv_name} is nil: make sure you set it in your test's setup method." if instance_variable_get("@#{iv_name}").nil?
end
@request.recycle!
@html_document = nil
@request.env['REQUEST_METHOD'] ||= "GET"
@request.action = action.to_s
parameters ||= {}
@request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
build_request_uri(action, parameters)
@controller.process(@request, @response)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
returning self.send(request_method, action, parameters, session, flash) do
@request.env.delete 'HTTP_X_REQUESTED_WITH'
end
end
alias xhr :xml_http_request
def follow_redirect
if @response.redirected_to[:controller]
raise "Can't follow redirects outside of current controller (#{@response.redirected_to[:controller]})"
end
get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys)
end
def assigns(key = nil)
if key.nil?
@response.template.assigns
else
@response.template.assigns[key.to_s]
end
end
def build_request_uri(action, parameters)
options = @controller.send(:rewrite_options, parameters)
options.update(:only_path => true, :action => action)
url = ActionController::UrlRewriter.new(@request, parameters)
@request.set_REQUEST_URI(url.rewrite(options))
end
def session
@response.session
end
def flash
@response.flash
end
def cookies
@response.cookies
end
def redirect_to_url
@response.redirect_url
end
def build_request_uri(action, parameters)
unless @request.env['REQUEST_URI']
options = @controller.send(:rewrite_options, parameters)
options.update(:only_path => true, :action => action)
url = ActionController::UrlRewriter.new(@request, parameters)
@request.set_REQUEST_URI(url.rewrite(options))
end
end
def html_document
@html_document ||= HTML::Document.new(@response.body)
end
def find_tag(conditions)
html_document.find(conditions)
end
def find_all_tag(conditions)
html_document.find_all(conditions)
end
def method_missing(selector, *args)
return @controller.send(selector, *args) if ActionController::Routing::NamedRoutes::Helpers.include?(selector)
return super
end
# A helper to make it easier to test different route configurations.
# This method temporarily replaces ActionController::Routing::Routes
# with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
# will create some routes using map.draw { map.connect ... }:
#
# with_routing do |set|
# set.draw { set.connect ':controller/:id/:action' }
# assert_equal(
# ['/content/10/show', {}],
# set.generate(:controller => 'content', :id => 10, :action => 'show')
# )
# end
#
def with_routing
real_routes = ActionController::Routing::Routes
ActionController::Routing.send :remove_const, :Routes
temporary_routes = ActionController::Routing::RouteSet.new
ActionController::Routing.send :const_set, :Routes, temporary_routes
yield temporary_routes
ensure
if ActionController::Routing.const_defined? :Routes
ActionController::Routing.send(:remove_const, :Routes)
end
ActionController::Routing.const_set(:Routes, real_routes) if real_routes
end
end
end
module Test
module Unit
class TestCase #:nodoc:
include ActionController::TestProcess
end
end
end
|