omghax-test_rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+