omghax-test_rails 1.0.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,597 @@
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 helpful assertions.
11
+ #
12
+ # = Naming
13
+ #
14
+ # The test class must be named after your controller class name, so if you're
15
+ # testing views for the +RouteController+ you would name your test case
16
+ # +RouteViewTest+. The test case will expect to find your view files in
17
+ # <tt>app/views/route</tt>.
18
+ #
19
+ # The test names should be in the form of +test_view_edgecase+ where 'view'
20
+ # corresponds to the name of the view file, and 'edgecase' describes the
21
+ # scenario you are testing.
22
+ #
23
+ # If you are testing a view file named 'show.rhtml' your test should be named
24
+ # +test_show+. If your view is behaves differently depending upon its
25
+ # 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
+ # assert_form form_url, :post do
46
+ # # with a hidden id field
47
+ # assert_input :hidden, :id
48
+ # # And a submit button that says 'Delete!'
49
+ # assert_submit 'Delete!'
50
+ # end
51
+ #
52
+ # # And a link back to the route so you don't delete it
53
+ # assert_links_to "/route/show/#{routes(:work).id}", 'No, I do not!'
54
+ # end
55
+ #
56
+ # end
57
+ #
58
+ # == Typical Layout Test
59
+ #
60
+ # require 'test/test_helper'
61
+ #
62
+ # # Create a dummy controller for layout views. This lets the setup use the
63
+ # # right path with minimum fuss.
64
+ # class LayoutsController < ApplicationController; end
65
+ #
66
+ # class LayoutsViewTest < Test::Rails::ViewTestCase
67
+ #
68
+ # fixtures :users, :routes, :points, :photos
69
+ #
70
+ # def test_default
71
+ # # Template set-up
72
+ # @request.request_uri = '/foo'
73
+ # assigns[:action_title] = 'Hello & Goodbye'
74
+ #
75
+ # # Render an empty string with the 'application' layout.
76
+ # render :text => '', :layout => 'application'
77
+ #
78
+ # # Assert content just like a regular view test.
79
+ # assert_links_to '/', 'Home'
80
+ # assert_links_to '/user', 'Login'
81
+ # deny_links_to '/user/logout', 'Logout'
82
+ # assert_title 'Hello &amp; Goodbye'
83
+ # assert_h 1, 'Hello &amp; Goodbye'
84
+ # end
85
+ #
86
+ # end
87
+ #
88
+ # = Deprecated Features
89
+ #
90
+ # Form assertions are now using assert_select, so you don't need to pass URLs
91
+ # around everywhere and can instead use a block. (See above example).
92
+ #
93
+ # The form assertions will still work using the old syntax, but in a future
94
+ # release they will give warnings, then will be removed.
95
+
96
+ class Test::Rails::ViewTestCase < Test::Rails::FunctionalTestCase
97
+
98
+ self.use_transactional_fixtures = true
99
+ self.use_instantiated_fixtures = false
100
+
101
+ ##
102
+ # Sets up the test case.
103
+
104
+ def setup
105
+ return if self.class == Test::Rails::ViewTestCase
106
+
107
+ @path_parameters ||= {}
108
+
109
+ klass_name = self.class.name.sub(/View/, 'Controller')
110
+ @controller_class_name ||= klass_name.sub 'Test', ''
111
+
112
+ super
113
+
114
+ @ivar_proxy = Test::Rails::IvarProxy.new @controller
115
+
116
+ # these go here so that flash and session work as they should.
117
+ @controller.send :initialize_template_class, @response
118
+ @controller.send :assign_shortcuts, @request, @response
119
+
120
+ assigns[:session] = @controller.session
121
+ @controller.class.send :public, :flash # make flash accessible to the test
122
+ end
123
+
124
+ ##
125
+ # Allows the view instance variables to be set like flash:
126
+ #
127
+ # test:
128
+ # def test_show
129
+ # assigns[:route] = routes(:work)
130
+
131
+ def assigns
132
+ @ivar_proxy
133
+ end
134
+
135
+ ##
136
+ # Renders the template. The template is determined from the test name. If
137
+ # you have multiple tests for the same view render will try to Do The Right
138
+ # Thing and remove parts of the name looking for the template file.
139
+ #
140
+ # By default, render has the added option <tt>:layout => false</tt>,
141
+ # so if want to test behavior in your layout add <tt>:layout => true</tt>.
142
+ #
143
+ # The action can be forced by using the options:
144
+ #
145
+ # render :action => 'new'
146
+ #
147
+ # render :template => 'profile/index'
148
+ #
149
+ # A test's path parameters may be overridden, allowing routes with
150
+ # additional parameters to work.
151
+ #
152
+ # == Working with Routes
153
+ #
154
+ # By default, a view tests sets the controller and action of a test to the
155
+ # controller name and action name for the test. This may be overriden.
156
+ #
157
+ # A test involving routes like:
158
+ #
159
+ # map.workspace '/users/:owner/workspace/:action',
160
+ # :controller => 'workspace', :action => 'workspace'
161
+ #
162
+ # Can be invoked by setting @path_parameters like this:
163
+ #
164
+ # def test__app_entry
165
+ # @path_parameters[:owner] = 'bob'
166
+ # @path_parameters[:action] = 'apps'
167
+ #
168
+ # render :partial => 'apps/app_entry'
169
+ #
170
+ # # ...
171
+ # end
172
+ #
173
+ # == View Lookup
174
+ #
175
+ # render strips off words trailing an _ in the test name one at a time until
176
+ # it finds a matching action. It tries the extensions 'rhtml', 'rxml',
177
+ # 'rjs', and 'mab' in order for each action until a view is found.
178
+ #
179
+ # With this test case:
180
+ #
181
+ # class RouteViewTest < Test::Rails::ViewTestCase
182
+ # def test_show_photos
183
+ # render
184
+ # end
185
+ # def test_show_no_photos
186
+ # render
187
+ # end
188
+ # end
189
+ #
190
+ # In test_show_photos, render will look for:
191
+ # * app/views/route/show_photos.rhtml
192
+ # * app/views/route/show_photos.rxml
193
+ # * app/views/route/show_photos.rjs
194
+ # * app/views/route/show_photos.mab
195
+ # * app/views/route/show.[...]
196
+ #
197
+ # And in test_show_no_photos, render will look for:
198
+ # * app/views/route/show_no_photos.rhtml
199
+ # * app/views/route/show_no_photos.rxml
200
+ # * app/views/route/show_no_photos.rjs
201
+ # * app/views/route/show_no_photos.mab
202
+ # * app/views/route/show_no.[...]
203
+ # * app/views/route/show.[...]
204
+ #
205
+ # If a view cannot be found the test will flunk.
206
+
207
+ def render(options = {}, deprecated_status = nil)
208
+ @action_name = action_name caller[0] if options.empty?
209
+ assigns[:action_name] = @action_name
210
+
211
+ default_path_parameters = {
212
+ :controller => @controller.controller_name,
213
+ :action => @action_name
214
+ }
215
+
216
+ path_parameters = default_path_parameters.merge(@path_parameters)
217
+
218
+ @request.path_parameters = path_parameters
219
+
220
+ defaults = { :layout => false }
221
+ options = defaults.merge options
222
+
223
+ if Test::Rails.rails_version >= Test::Rails.v1_2 then
224
+ @controller.send :params=, @request.parameters
225
+ else
226
+ @controller.instance_variable_set :@params, @request.parameters
227
+ end
228
+ @controller.send :initialize_current_url
229
+ current_url = URI.parse @controller.url_for
230
+ @request.request_uri = current_url.request_uri
231
+
232
+ # Rails 1.0
233
+ @controller.send :assign_names rescue nil
234
+ @controller.send :fire_flash rescue nil
235
+
236
+ # Rails 1.1
237
+ @controller.send :forget_variables_added_to_assigns rescue nil
238
+
239
+ # Do the render
240
+ options[:TR_force] = true
241
+ @controller.render options, deprecated_status
242
+
243
+ # Rails 1.1
244
+ @controller.send :process_cleanup rescue nil
245
+ end
246
+
247
+ ##
248
+ # Asserts that there is an error on +field+ of type +type+.
249
+
250
+ def assert_error_on(field, type)
251
+ error_message = ActiveRecord::Errors.default_error_messages[type]
252
+ assert_select "div.errorExplanation li",
253
+ :text => /^#{field} #{error_message}/i
254
+ end
255
+
256
+ ##
257
+ # A wrapper assert that calls both assert_input and assert_label.
258
+ #
259
+ # view:
260
+ # <%= start_form_tag :controller => 'game', :action => 'save' %>
261
+ # <label for="game_amount">Amount:</label>
262
+ # <% text_field 'game', 'amount' %>
263
+ #
264
+ # test:
265
+ # assert_field '/game/save', :text, :game, :amount
266
+
267
+ def assert_field(*args)
268
+ form_action, type, model, column, value =
269
+ Symbol === args.first ? [nil, *args] : args
270
+
271
+ if form_action then # HACK deprecate
272
+ assert_input form_action, type, "#{model}[#{column}]", value
273
+ assert_label form_action, "#{model}_#{column}"
274
+ else
275
+ assert_input type, "#{model}[#{column}]", value
276
+ assert_label "#{model}_#{column}"
277
+ end
278
+ end
279
+
280
+ ##
281
+ # Asserts that there is a form whose action is +form_action+. Optionally,
282
+ # +method+ and +enctype+ may be specified. If a block is given, assert_form
283
+ # behaves like assert_select, so assert_input and friends may be scoped to
284
+ # the selected form.
285
+ #
286
+ # view:
287
+ # <%= start_form_tag :action => 'create_file' %>
288
+ # # ...
289
+ #
290
+ # test:
291
+ # assert_form '/game/save'
292
+ #
293
+ # or:
294
+ # assert_form '/game/save' do
295
+ # # ...
296
+ # end
297
+
298
+ def assert_form(form_action, method = nil, enctype = nil, &block)
299
+ selector = "form[action='#{form_action}']"
300
+ selector << "[method='#{method}']" if method
301
+ selector << "[enctype='#{enctype}']" if enctype
302
+ assert_select selector, &block
303
+ end
304
+
305
+ ##
306
+ # Asserts a hN tag of level +level+ exists and contains +content+.
307
+ #
308
+ # view:
309
+ # <h3>Recent Builds</h3>
310
+ #
311
+ # test:
312
+ # assert_h 3, 'Recent Builds'
313
+
314
+ def assert_h(level, content)
315
+ assert_select "h#{level}", :text => content
316
+ end
317
+
318
+ ##
319
+ # Asserts that an image exists with a src of +src+.
320
+ #
321
+ # view:
322
+ # <img src="/images/bucket.jpg" alt="Bucket">
323
+ #
324
+ # test:
325
+ # assert_image '/images/bucket.jpg'
326
+
327
+ def assert_image(src)
328
+ assert_select "img[src='#{src}']"
329
+ end
330
+
331
+ ##
332
+ # Asserts that an input element of +type+ with a name of +name+, and
333
+ # optionally a value of +value+ exists.
334
+ #
335
+ # view:
336
+ # <%= text_field 'game', 'amount' %>
337
+ #
338
+ # test:
339
+ # assert_input :text, "game[amount]"
340
+
341
+ def assert_input(*args)
342
+ action, type, name, value = Symbol === args.first ? [nil, *args] : args
343
+
344
+ raise ArgumentError, 'supply type and name' if type.nil? or name.nil?
345
+
346
+ input_selector = "input[type='#{type}'][name='#{name}']"
347
+ input_selector << "[value='#{value}']" if value
348
+
349
+ assert_select_in_form action do assert_select input_selector end
350
+ end
351
+
352
+ ##
353
+ # Asserts that a label with a for attribute of +for_attribute+ exists.
354
+ #
355
+ # view:
356
+ # <%= start_form_tag :controller => 'game', :action => 'save' %>
357
+ # <label for="game_amount">Amount:</label>
358
+ #
359
+ # test:
360
+ # assert_label 'game_amount'
361
+
362
+ def assert_label(*args)
363
+ action, for_attribute = args.length == 1 ? [nil, *args] : args
364
+
365
+ raise ArgumentError, 'supply for_attribute' if for_attribute.nil?
366
+
367
+ label_selector = "label[for='#{for_attribute}']"
368
+
369
+ assert_select_in_form action do assert_select label_selector end
370
+ end
371
+
372
+ ##
373
+ # Asserts that there is an anchor tag with an href of +href+ that optionally
374
+ # has +content+.
375
+ #
376
+ # view:
377
+ # <%= link_to 'drbrain', :model => user %>
378
+ #
379
+ # test:
380
+ # assert_links_to '/players/show/1', 'drbrain'
381
+
382
+ def assert_links_to(href, content = nil)
383
+ assert_select(*links_to_options_for(href, content))
384
+ end
385
+
386
+ ##
387
+ # Denies the existence of an anchor tag with an href of +href+ and
388
+ # optionally +content+.
389
+ #
390
+ # view (for /players/show/1):
391
+ # <%= link_to_unless_current 'drbrain', :model => user %>
392
+ #
393
+ # test:
394
+ # deny_links_to '/players/show/1'
395
+
396
+ def deny_links_to(href, content = nil)
397
+ selector, options = links_to_options_for(href, content)
398
+ options[:count] = 0
399
+
400
+ assert_select selector, options
401
+ end
402
+
403
+ ##
404
+ # Asserts that there is a form using the 'POST' method whose action is
405
+ # +form_action+ and uses the multipart content type. If passed a block,
406
+ # works like assert_form.
407
+ #
408
+ # view:
409
+ # <%= start_form_tag({ :action => 'create_file' }, :multipart => true) %>
410
+ #
411
+ # test:
412
+ # assert_multipart_form '/game/save'
413
+
414
+ def assert_multipart_form(form_action, &block)
415
+ assert_form(form_action, :post, 'multipart/form-data', &block)
416
+ end
417
+
418
+ ##
419
+ # Asserts that there is a form using the 'POST' method whose action is
420
+ # +form_action+. If passed a block, works like assert_form.
421
+ #
422
+ # view:
423
+ # <%= start_form_tag :action => 'create_file' %>
424
+ #
425
+ # test:
426
+ # assert_post_form '/game/save'
427
+
428
+ def assert_post_form(form_action, &block)
429
+ assert_form(form_action, :post, &block)
430
+ end
431
+
432
+ ##
433
+ # Asserts that a select element with a name of "+model+[+column+]" and
434
+ # +options+ with specified names and values exists.
435
+ #
436
+ # view:
437
+ # <%= collection_select :game, :location_id, @locations, :id, :name %>
438
+ #
439
+ # test:
440
+ # assert_select_tag :game, :location_id, 'Ballet' => 1, 'Guaymas' => 2
441
+
442
+ def assert_select_tag(*args)
443
+ action, model, column, options = Symbol === args.first ? [nil, *args] : args
444
+
445
+ assert_kind_of Hash, options, "options needs to be a Hash"
446
+ deny options.empty?, "options must not be empty"
447
+
448
+ select_selector = "select[name='#{model}[#{column}]']"
449
+
450
+ options.each do |option_name, option_value|
451
+ option_selector = "option[value='#{option_value}']"
452
+ selector = "#{select_selector} #{option_selector}"
453
+
454
+ assert_select_in_form action do
455
+ assert_select selector, :text => option_name
456
+ end
457
+ end
458
+ end
459
+
460
+ ##
461
+ # Asserts that a submit element with a value of +value+ exists.
462
+ #
463
+ # view:
464
+ # <input type="submit" value="Create!" %>
465
+ #
466
+ # test:
467
+ # assert_submit 'Create!'
468
+
469
+ def assert_submit(*args)
470
+ action, value = args.length == 1 ? [nil, *args] : args
471
+
472
+ submit_selector = "input[type='submit'][value='#{value}']"
473
+
474
+ assert_select_in_form action do assert_select submit_selector end
475
+ end
476
+
477
+ ##
478
+ # Asserts that a form with +form_action+ has a descendent that matches
479
+ # +options+ exists.
480
+ #
481
+ # Typically this is not used directly in tests. Instead use it to build
482
+ # expressive tests that assert which fields are in what form.
483
+ #
484
+ # view:
485
+ # <%= start_form_tag :action => 'save' %>
486
+ # [...]
487
+ #
488
+ # test:
489
+ # assert_tag_in_form '/route/save', :tag => 'table'
490
+
491
+ def assert_tag_in_form(form_action, options)
492
+ assert_tag :tag => 'form', :attributes => { :action => form_action },
493
+ :descendant => options
494
+ end
495
+
496
+ ##
497
+ # Asserts that a textarea with name +name+ and optionally +value+ exists.
498
+ #
499
+ # view:
500
+ # <%= text_area 'post', 'body' %>
501
+ #
502
+ # test:
503
+ # assert_text_area 'post[body]'
504
+ #
505
+ # view:
506
+ # <textarea id="post_body" name="post[body]">
507
+ # <%= @post.body %>
508
+ # </textarea>
509
+ #
510
+ # test:
511
+ # assert_text_area 'post[body]', posts(:post).body
512
+
513
+ def assert_text_area(*args)
514
+ action, name, value = args.first !~ /\A\// ? [nil, *args] : args
515
+
516
+ raise ArgumentError, 'supply name' if name.nil?
517
+
518
+ text_area_selector = ["textarea[name='#{name}']"]
519
+ text_area_selector << { :text => value } if value
520
+
521
+ assert_select_in_form action do assert_select(*text_area_selector) end
522
+ end
523
+
524
+ alias assert_textarea assert_text_area
525
+
526
+ ##
527
+ # Asserts that a title with +title+ exists.
528
+ #
529
+ # view:
530
+ # <title>some content</title>
531
+ #
532
+ # test:
533
+ # assert_title 'some content'
534
+
535
+ def assert_title(title)
536
+ assert_select 'title', :text => title
537
+ end
538
+
539
+ ##
540
+ # Opposite of assert_select.
541
+
542
+ def deny_select(selector)
543
+ assert_select selector, false
544
+ end
545
+
546
+ ##
547
+ # Creates a new Paginator that uses the current controller. +item_count+,
548
+ # +items_per_page+ and +page_number+ are passed straight through.
549
+
550
+ def util_make_paginator(item_count, items_per_page, page_number)
551
+ ActionController::Pagination::Paginator.new(@controller, item_count,
552
+ items_per_page, page_number)
553
+ end
554
+
555
+ ##
556
+ # Utility method for compatibility with old-style assert_tag form
557
+ # assertions.
558
+
559
+ def assert_select_in_form(action, &block) # :nodoc:
560
+ if action then
561
+ assert_form(action, &block)
562
+ else
563
+ block.call
564
+ end
565
+ end
566
+
567
+ ##
568
+ # Creates an assertion options hash for +href+ and +content+.
569
+
570
+ def links_to_options_for(href, content = nil)
571
+ selector = "a[href='#{href}']"
572
+ equality = content ? { :text => content } : {}
573
+ return selector, equality
574
+ end
575
+
576
+ ##
577
+ # Returns the action_name based on a backtrace line passed in as +test+.
578
+
579
+ def action_name(test)
580
+ orig_name = test = test.sub(/.*in `test_(.*)'/, '\1')
581
+ controller = @controller.class.name.sub('Controller', '').underscore
582
+
583
+ extensions = %w[rhtml rxml rjs mab]
584
+
585
+ while test =~ /_/ do
586
+ return test if extensions.any? { |ext| File.file? "app/views/#{controller}/#{test}.#{ext}" }
587
+
588
+ test = test.sub(/_[^_]+$/, '')
589
+ end
590
+
591
+ return test if extensions.any? { |ext| File.file? "app/views/#{controller}/#{test}.#{ext}" }
592
+
593
+ flunk "Couldn't find view for test_#{orig_name}"
594
+ end
595
+
596
+ end
597
+