ZenTest 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+