ZenTest 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,64 @@
1
+ ##
2
+ # FunctionalTestCase is an abstract class that sets up a controller instance
3
+ # for its subclasses.
4
+
5
+ class Test::Rails::FunctionalTestCase < Test::Rails::TestCase
6
+
7
+ ##
8
+ # Sets up instance variables to allow tests depending on a controller work.
9
+ #
10
+ # setup uses the instance variable @controller_class_name to determine which
11
+ # controller class to instantiate.
12
+ #
13
+ # setup also instantiates a new @request and @response object.
14
+
15
+ def setup
16
+ return if self.class.name =~ /TestCase$/
17
+
18
+ @controller_class = Object.path2class @controller_class_name
19
+ raise "Can't determine controller class for #{self.class}" if @controller_class.nil?
20
+
21
+ @controller = @controller_class.new
22
+
23
+ @session = ActionController::TestSession.new
24
+
25
+ @flash = ActionController::Flash::FlashHash.new
26
+ @session['flash'] = @flash
27
+
28
+ @request = ActionController::TestRequest.new
29
+ @request.session = @session
30
+
31
+ @response = ActionController::TestResponse.new
32
+ end
33
+
34
+ ##
35
+ # Flash accessor. The flash can be assigned to before calling process or
36
+ # render and it will Just Work (yay!)
37
+ #
38
+ # view:
39
+ # <div class="error"><%= flash[:error] %></div>
40
+ #
41
+ # test:
42
+ # flash[:error] = 'You did a bad thing.'
43
+ # render
44
+ # assert_tag :tag => 'div', :attributes => { :class => 'error' },
45
+ # :content => 'You did a bad thing.'
46
+
47
+ attr_reader :flash
48
+
49
+ ##
50
+ # Session accessor. The session can be assigned to before calling process
51
+ # or render and it will Just Work (yay!)
52
+ #
53
+ # test:
54
+ #
55
+ # def test_logout
56
+ # session[:user] = users(:herbert)
57
+ # post :logout
58
+ # assert_equal nil, session[:user]
59
+ # end
60
+
61
+ attr_reader :session
62
+
63
+ end
64
+
@@ -0,0 +1,31 @@
1
+ require 'test/rails'
2
+
3
+ ##
4
+ # A wrapper that allows instance variables to be manipulated using +[]+ and
5
+ # +[]=+
6
+
7
+ class Test::Rails::IvarProxy
8
+
9
+ ##
10
+ # Wraps +object+ allowing its instance variables to be manipulated.
11
+
12
+ def initialize(object)
13
+ @object = object
14
+ end
15
+
16
+ ##
17
+ # Retrieves +ivar+ from the wrapped object.
18
+
19
+ def [](ivar)
20
+ @object.instance_variable_get "@#{ivar}"
21
+ end
22
+
23
+ ##
24
+ # Sets +ivar+ to +val+ on the wrapped object.
25
+
26
+ def []=(ivar, val)
27
+ @object.instance_variable_set "@#{ivar}", val
28
+ end
29
+
30
+ end
31
+
@@ -0,0 +1,49 @@
1
+ require 'code_statistics'
2
+
3
+ namespace :test do
4
+ desc 'Run the view tests in test/views'
5
+ Rake::TestTask.new :views => [ 'db:test:prepare' ] do |t|
6
+ t.libs << 'test'
7
+ t.pattern = 'test/views/**/*_test.rb'
8
+ t.verbose = true
9
+ end
10
+
11
+ desc 'Run the controller tests in test/controllers'
12
+ Rake::TestTask.new :controllers => [ 'db:test:prepare' ] do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/controllers/**/*_test.rb'
15
+ t.verbose = true
16
+ end
17
+ end
18
+
19
+ desc 'Run all tests'
20
+ task :test => %w[
21
+ test:units
22
+ test:controllers
23
+ test:views
24
+ test:functionals
25
+ test:integration
26
+ ]
27
+
28
+ dirs = [
29
+ %w[Libraries lib/],
30
+ %w[Models app/models],
31
+ %w[Unit\ tests test/unit],
32
+ %w[Components components],
33
+ %w[Controllers app/controllers],
34
+ %w[Controller\ tests test/controllers],
35
+ %w[View\ tests test/views],
36
+ %w[Functional\ tests test/functional],
37
+ %w[Integration\ tests test/integration],
38
+ %w[APIs app/apis],
39
+ %w[Helpers app/helpers],
40
+ ]
41
+
42
+ dirs = dirs.map { |name, dir| [name, File.join(RAILS_ROOT, dir)] }
43
+ dirs = dirs.select { |name, dir| File.directory? dir }
44
+
45
+ STATS_DIRECTORIES.replace dirs
46
+
47
+ CodeStatistics::TEST_TYPES << 'View tests'
48
+ CodeStatistics::TEST_TYPES << 'Controller tests'
49
+
@@ -0,0 +1,12 @@
1
+ ##
2
+ # Test::Rails::TestCase is an abstract test case for Test::Rails test cases.
3
+ #--
4
+ # Eventually this will hold the fixture setup stuff.
5
+
6
+ class Test::Rails::TestCase < Test::Unit::TestCase
7
+
8
+ def test_stupid # :nodoc:
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,431 @@
1
+ ##
2
+ # ViewTestCase allows views to be tested independent of their controllers.
3
+ # Testcase implementors must set up the instance variables the view needs to
4
+ # render itself.
5
+ #
6
+ # = Features
7
+ #
8
+ # * Allows testing of individual AJAX templates.
9
+ # * Allows testing of individual partials.
10
+ # * Large library of helful assertions.
11
+ #
12
+ # = Naming
13
+ #
14
+ # The test class must be named after your controller class name, so if
15
+ # you're testing views for the +RouteController+ you would name your
16
+ # test case +RouteViewTest+. The test case will expect to find your
17
+ # view files in <tt>app/views/route</tt>.
18
+ #
19
+ # The test names should be in the form of +test_view_edgecase+ where
20
+ # 'view' corresponds to the name of the view file, and 'edgecase'
21
+ # describes the scenario you are testing.
22
+ #
23
+ # If you are testing a view file named 'show.rhtml' your test should
24
+ # be named +test_show+. If your view is behaves differently depending
25
+ # upon its parameters then you can make the test name descriptive like
26
+ # +test_show_photos+ and +test_show_no_photos+.
27
+ #
28
+ # = Examples
29
+ #
30
+ # == Typical View Test
31
+ #
32
+ # class RouteViewTest < Test::Rails::ViewTestCase
33
+ #
34
+ # fixtures :users, :routes, :points, :photos
35
+ #
36
+ # def test_delete
37
+ # # Set up instance variables for template
38
+ # assigns[:loggedin_user] = users(:herbert)
39
+ # assigns[:route] = routes(:work)
40
+ #
41
+ # # render template for the delete action in RouteController
42
+ # render
43
+ #
44
+ # # assert that there's a form with an action of "/route/destroy"
45
+ # form_url = '/route/destroy'
46
+ # assert_post_form form_url
47
+ # # with a hidden id field
48
+ # assert_input form_url, :hidden, :id
49
+ # # And a submit button that says 'Delete!'
50
+ # assert_submit form_url, 'Delete!'
51
+ # # And a link back to the route so you don't delete it
52
+ # assert_links_to "/route/show/#{routes(:work).id}", 'No, I do not!'
53
+ # end
54
+ #
55
+ # end
56
+ #
57
+ # == Typical Layout Test
58
+ #
59
+ # require 'test/test_helper'
60
+ #
61
+ # # Create a dummy controller for layout views. This lets the setup use the
62
+ # # right path with minimum fuss.
63
+ # class LayoutsController < ApplicationController; end
64
+ #
65
+ # class LayoutsViewTest < Test::Rails::ViewTestCase
66
+ #
67
+ # fixtures :users, :routes, :points, :photos
68
+ #
69
+ # def test_default
70
+ # # Template set-up
71
+ # @request.request_uri = '/foo'
72
+ # assigns[:action_title] = 'Hello & Goodbye'
73
+ #
74
+ # # Render an empty string with the 'default' layout.
75
+ # render :text => '', :layout => 'default'
76
+ #
77
+ # # Assert content just like a regular view test.
78
+ # assert_links_to '/', 'Home'
79
+ # assert_links_to '/user', 'Login'
80
+ # deny_links_to '/user/logout', 'Logout'
81
+ # assert_tag :tag => 'title', :content => 'Hello &amp; Goodbye'
82
+ # assert_tag :tag => 'h1', :content => 'Hello &amp; Goodbye'
83
+ # end
84
+ #
85
+ # end
86
+
87
+ class Test::Rails::ViewTestCase < Test::Rails::FunctionalTestCase
88
+
89
+ ##
90
+ # Sets up the test case.
91
+
92
+ def setup
93
+ return if self.class == Test::Rails::ViewTestCase
94
+
95
+ klass_name = self.class.name.sub(/View/, 'Controller')
96
+ @controller_class_name ||= klass_name.sub 'Test', ''
97
+
98
+ super
99
+
100
+ @ivar_proxy = Test::Rails::IvarProxy.new @controller
101
+
102
+ # these go here so that flash and session work as they should.
103
+ @controller.send :initialize_template_class, @response
104
+ @controller.send :assign_shortcuts, @request, @response
105
+ @controller.send :reset_session
106
+
107
+ assigns[:session] = @controller.session
108
+ @controller.class.send :public, :flash # make flash accessible to the test
109
+ end
110
+
111
+ ##
112
+ # Allows the view instance variables to be set like flash:
113
+ #
114
+ # test:
115
+ # def test_show
116
+ # assigns[:route] = routes(:work)
117
+
118
+ def assigns
119
+ @ivar_proxy
120
+ end
121
+
122
+ ##
123
+ # Renders the template. The template is determined from the test name. If
124
+ # you have multiple tests for the same view render will try to Do The Right
125
+ # Thing and remove parts of the name looking for the template file.
126
+ #
127
+ # By default, render has the added option <tt>:layout => false</tt>,
128
+ # so if want to test behavior in your layout add <tt>:layout => true</tt>.
129
+ #
130
+ # The action can be forced by using the options:
131
+ #
132
+ # render :action => 'new'
133
+ #
134
+ # render :template => 'profile/index'
135
+ #
136
+ # For this test:
137
+ #
138
+ # class RouteViewTest < Test::Rails::ViewTestCase
139
+ # def test_show_photos
140
+ # render
141
+ # end
142
+ # def test_show_no_photos
143
+ # render
144
+ # end
145
+ # end
146
+ #
147
+ # For test_show_photos will look for:
148
+ # * app/views/route/show_photos.rhtml
149
+ # * app/views/route/show_photos.rxml
150
+ # * app/views/route/show.rhtml
151
+ # * app/views/route/show.rxml
152
+ #
153
+ # And test_show_no_photos will look for:
154
+ # * app/views/route/show_no_photos.rhtml
155
+ # * app/views/route/show_no_photos.rxml
156
+ # * app/views/route/show_no.rhtml
157
+ # * app/views/route/show_no.rxml
158
+ # * app/views/route/show.rhtml
159
+ # * app/views/route/show.rxml
160
+ #
161
+ # If a view cannot be found the test will flunk.
162
+
163
+ def render(options = {}, deprecated_status = nil)
164
+ @action_name = action_name caller[0] if options.empty?
165
+ assigns[:action_name] = @action_name
166
+
167
+ @request.path_parameters = {
168
+ :controller => @controller.controller_name,
169
+ :action => @action_name,
170
+ }
171
+
172
+ defaults = { :layout => false }
173
+ options = defaults.merge options
174
+
175
+ @controller.instance_variable_set :@params, @request.parameters
176
+ @controller.send :initialize_current_url
177
+
178
+ # Rails 1.0
179
+ @controller.send :assign_names rescue nil
180
+ @controller.send :fire_flash rescue nil
181
+
182
+ # Rails 1.1
183
+ @controller.send :forget_variables_added_to_assigns rescue nil
184
+
185
+ # Do the render
186
+ @controller.render options, deprecated_status
187
+
188
+ # Rails 1.1
189
+ @controller.send :process_cleanup rescue nil
190
+ end
191
+
192
+ ##
193
+ # Asserts that there is an error on +field+ of type +type+.
194
+
195
+ def assert_error_on(field, type)
196
+ error_message = ActiveRecord::Errors.default_error_messages[type]
197
+ assert_tag :tag => 'div', :attributes => { :class => 'errorExplanation' },
198
+ :descendant => {
199
+ :tag => 'li',
200
+ :content => /^#{field} #{error_message}/i }
201
+ end
202
+
203
+ ##
204
+ # Asserts that a form with +form_action+ has an input element of +type+ with
205
+ # a name of "+model+[+column+]" and has a label with a for attribute of
206
+ # "+model+_+column+".
207
+ #
208
+ # view:
209
+ # <%= start_form_tag :controller => 'game', :action => 'save' %>
210
+ # <label for="game_amount">Amount:</label>
211
+ # <% text_field 'game', 'amount' %>
212
+ #
213
+ # test:
214
+ # assert_field '/game/save', :text, :game, :amount
215
+
216
+ def assert_field(form_action, type, model, column, value = nil)
217
+ assert_input form_action, type, "#{model}[#{column}]", value
218
+ assert_label form_action, "#{model}_#{column}", false
219
+ end
220
+
221
+ ##
222
+ # Asserts that an image exists with a src of +src+.
223
+ #
224
+ # view:
225
+ # <img src="/images/bucket.jpg" alt="Bucket">
226
+ #
227
+ # test:
228
+ # assert_image '/images/bucket.jpg'
229
+
230
+ def assert_image(src)
231
+ assert_tag :tag => 'img', :attributes => { :src => src }
232
+ end
233
+
234
+ ##
235
+ # Asserts that a form with +form_action+ has an input element of +type+ with
236
+ # a name of +name+.
237
+ #
238
+ # view:
239
+ # <%= start_form_tag :controller => 'game', :action => 'save' %>
240
+ # <%= text_field 'game', 'amount' %>
241
+ #
242
+ # test:
243
+ # assert_input '/game/save', :text, "game[amount]"
244
+
245
+ def assert_input(form_action, type, name, value = nil)
246
+ attrs = { :type => type.to_s, :name => name.to_s }
247
+ attrs[:value] = value unless value.nil?
248
+ assert_tag_in_form form_action, :tag => 'input', :attributes => attrs
249
+ end
250
+
251
+ ##
252
+ # Asserts that a form with +form_action+ has a label with a for attribute of
253
+ # "+model+_+column+".
254
+ #
255
+ # view:
256
+ # <%= start_form_tag :controller => 'game', :action => 'save' %>
257
+ # <label for="game_amount">Amount:</label>
258
+ #
259
+ # test:
260
+ # assert_label '/game/save', :game, :amount
261
+
262
+ def assert_label(form_action, name, include_f = true)
263
+ for_attribute = (include_f ? 'f_' : '') << name
264
+ assert_tag_in_form form_action, :tag => 'label', :attributes => {
265
+ :for => for_attribute }
266
+ end
267
+
268
+ ##
269
+ # Asserts that there is an anchor tag with an href of +href+ and optionally
270
+ # has +content+.
271
+ #
272
+ # view:
273
+ # <%= link_to 'drbrain', :model => user %>
274
+ #
275
+ # test:
276
+ # assert_links_to '/players/show/1', 'drbrain'
277
+
278
+ def assert_links_to(href, content = nil)
279
+ assert_tag links_to_options_for(href, content)
280
+ end
281
+
282
+ ##
283
+ # Denies the existence of an anchor tag with an href of +href+ and
284
+ # optionally +content+.
285
+ #
286
+ # view (for /players/show/1):
287
+ # <%= link_to_unless_current 'drbrain', :model => user %>
288
+ #
289
+ # test:
290
+ # deny_links_to '/players/show/1'
291
+
292
+ def deny_links_to(href, content = nil)
293
+ assert_no_tag links_to_options_for(href, content)
294
+ end
295
+
296
+ ##
297
+ # Asserts that there is a form using the 'POST' method whose action is
298
+ # +form_action+ and uses the multipart content type.
299
+ #
300
+ # view:
301
+ # <%= start_form_tag({ :action => 'create_file' }, :multipart => true) %>
302
+ #
303
+ # test:
304
+ # assert_multipart_form '/game/save'
305
+
306
+ def assert_multipart_form(form_action)
307
+ assert_tag :tag => 'form', :attributes => { :action => form_action,
308
+ :method => 'post', :enctype => 'multipart/form-data' }
309
+ end
310
+
311
+ ##
312
+ # Asserts that there is a form using the 'POST' method whose action is
313
+ # +form_action+.
314
+ #
315
+ # view:
316
+ # <%= start_form_tag :action => 'create_file' %>
317
+ #
318
+ # test:
319
+ # assert_post_form '/game/save'
320
+
321
+ def assert_post_form(form_action)
322
+ assert_tag :tag => 'form', :attributes => { :action => form_action,
323
+ :method => 'post' }
324
+ end
325
+
326
+ ##
327
+ # Asserts that a form with +form_action+ has a select element with a name of
328
+ # "+model+[+column+]" and options with specified names and values.
329
+ #
330
+ # view:
331
+ # <%= start_form_tag :action => 'save' %>
332
+ # <%= collection_select :game, :location_id, @locations, :id, :name %>
333
+ #
334
+ # test:
335
+ # assert_select '/games/save', :game, :location_id,
336
+ # 'Ballet' => 1, 'Guaymas' => 2
337
+
338
+ def assert_select(form_action, model, column, options)
339
+ assert_kind_of Hash, options, "options needs to be a Hash"
340
+ deny options.empty?, "options must not be empty"
341
+ options.each do |option_name, option_id|
342
+ assert_tag_in_form(form_action,
343
+ :tag => 'select',
344
+ :attributes => { :name => "#{model}[#{column}]" },
345
+ :child => {
346
+ :tag => 'option',
347
+ :attributes => { :value => option_id },
348
+ :content => option_name
349
+ })
350
+ end
351
+ end
352
+
353
+ ##
354
+ # Asserts that a form with +form_action+ has a submit element with a value
355
+ # of +value+.
356
+ #
357
+ # view:
358
+ # <%= start_form_tag :action => 'save' %>
359
+ # <input type="submit" value="Create!" %>
360
+ #
361
+ # test:
362
+ # assert_submit '/route/save', 'Create!'
363
+
364
+ def assert_submit(form_action, value)
365
+ assert_tag_in_form form_action, :tag => 'input', :attributes => {
366
+ :type => "submit", :value => value }
367
+ end
368
+
369
+ ##
370
+ # Asserts that a form with +form_action+ has a descendent that matches
371
+ # +options+.
372
+ #
373
+ # Typically this is not used directly in tests. Instead use it to build
374
+ # expressive tests that assert which fields are in what form.
375
+ #
376
+ # view:
377
+ # <%= start_form_tag :action => 'save' %>
378
+ # <table>
379
+ #
380
+ # test:
381
+ # assert_tag_in_form '/route/save', :tag => 'table'
382
+
383
+ def assert_tag_in_form(form_action, options)
384
+ assert_tag :tag => 'form', :attributes => { :action => form_action },
385
+ :descendant => options
386
+ end
387
+
388
+ ##
389
+ # Creates a new Paginator that uses the current controller. +item_count+,
390
+ # +items_per_page+ and +page_number+ are passed straight through.
391
+
392
+ def util_make_paginator(item_count, items_per_page, page_number)
393
+ ActionController::Pagination::Paginator.new(@controller, item_count,
394
+ items_per_page, page_number)
395
+ end
396
+
397
+ protected
398
+
399
+ ##
400
+ # Creates an assertion options hash for +href+ and +content+.
401
+
402
+ def links_to_options_for(href, content = nil)
403
+ options = { :tag => 'a', :attributes => { :href => href } }
404
+ options[:content] = content unless content.nil?
405
+ return options
406
+ end
407
+
408
+ private
409
+
410
+ ##
411
+ # Returns the action_name based on a backtrace line passed in as +test+.
412
+
413
+ def action_name(test)
414
+ orig_name = test = test.sub(/.*in `test_(.*)'/, '\1')
415
+ controller = @controller.class.name.sub('Controller', '')
416
+ controller = controller.gsub(/([A-Z])/, '_\1'.downcase).sub('_', '')
417
+
418
+ while test =~ /_/ do
419
+ return test if File.file? "app/views/#{controller}/#{test}.rhtml"
420
+ return test if File.file? "app/views/#{controller}/#{test}.rxml"
421
+ test = test.sub(/_[^_]+$/, '')
422
+ end
423
+
424
+ return test if File.file? "app/views/#{controller}/#{test}.rhtml"
425
+ return test if File.file? "app/views/#{controller}/#{test}.rxml"
426
+
427
+ flunk "Couldn't find view for test_#{orig_name}"
428
+ end
429
+
430
+ end
431
+