redmine_rate 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/COPYRIGHT.txt +18 -0
  2. data/CREDITS.txt +5 -0
  3. data/GPL.txt +339 -0
  4. data/README.rdoc +93 -0
  5. data/Rakefile +37 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/rate_caches_controller.rb +35 -0
  8. data/app/controllers/rates_controller.rb +154 -0
  9. data/app/models/rate.rb +161 -0
  10. data/app/views/rate_caches/index.html.erb +21 -0
  11. data/app/views/rates/_form.html.erb +36 -0
  12. data/app/views/rates/_list.html.erb +42 -0
  13. data/app/views/rates/create.js.rjs +2 -0
  14. data/app/views/rates/create_error.js.rjs +5 -0
  15. data/app/views/rates/edit.html.erb +3 -0
  16. data/app/views/rates/index.html.erb +5 -0
  17. data/app/views/rates/new.html.erb +3 -0
  18. data/app/views/rates/show.html.erb +23 -0
  19. data/app/views/users/_membership_rate.html.erb +23 -0
  20. data/app/views/users/_rates.html.erb +17 -0
  21. data/assets/images/database_refresh.png +0 -0
  22. data/config/locales/de.yml +18 -0
  23. data/config/locales/en.yml +18 -0
  24. data/config/locales/fr.yml +20 -0
  25. data/config/locales/ru.yml +9 -0
  26. data/config/routes.rb +4 -0
  27. data/init.rb +49 -0
  28. data/lang/de.yml +9 -0
  29. data/lang/en.yml +9 -0
  30. data/lang/fr.yml +8 -0
  31. data/lib/rate_conversion.rb +16 -0
  32. data/lib/rate_memberships_hook.rb +15 -0
  33. data/lib/rate_project_hook.rb +106 -0
  34. data/lib/rate_sort_helper_patch.rb +102 -0
  35. data/lib/rate_time_entry_patch.rb +66 -0
  36. data/lib/rate_users_helper_patch.rb +37 -0
  37. data/lib/redmine_rate/hooks/plugin_timesheet_view_timesheets_report_header_tags_hook.rb +11 -0
  38. data/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_group_header_hook.rb +9 -0
  39. data/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_time_entry_hook.rb +18 -0
  40. data/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_time_entry_sum_hook.rb +17 -0
  41. data/lib/redmine_rate/hooks/plugin_timesheet_views_timesheets_time_entry_row_class_hook.rb +21 -0
  42. data/lib/redmine_rate/hooks/timesheet_hook_helper.rb +14 -0
  43. data/lib/redmine_rate/hooks/view_layouts_base_html_head_hook.rb +9 -0
  44. data/lib/tasks/cache.rake +13 -0
  45. data/lib/tasks/data.rake +163 -0
  46. data/rails/init.rb +1 -0
  47. data/test/functional/rates_controller_test.rb +401 -0
  48. data/test/integration/admin_panel_test.rb +81 -0
  49. data/test/integration/routing_test.rb +16 -0
  50. data/test/test_helper.rb +43 -0
  51. data/test/unit/lib/rate_time_entry_patch_test.rb +77 -0
  52. data/test/unit/lib/rate_users_helper_patch_test.rb +37 -0
  53. data/test/unit/lib/redmine_rate/hooks/plugin_timesheet_view_timesheets_report_header_tags_hook_test.rb +26 -0
  54. data/test/unit/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_group_header_hook_test.rb +26 -0
  55. data/test/unit/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_time_entry_hook_test.rb +47 -0
  56. data/test/unit/lib/redmine_rate/hooks/plugin_timesheet_views_timesheet_time_entry_sum_hook_test.rb +48 -0
  57. data/test/unit/lib/redmine_rate/hooks/plugin_timesheet_views_timesheets_time_entry_row_class_hook_test.rb +60 -0
  58. data/test/unit/rate_for_test.rb +74 -0
  59. data/test/unit/rate_test.rb +333 -0
  60. metadata +137 -0
@@ -0,0 +1,401 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class RatesControllerTest < ActionController::TestCase
4
+
5
+ def self.should_be_unauthorized(&block)
6
+ should 'should return a forbidden status code' do
7
+ instance_eval(&block)
8
+ assert_response :forbidden
9
+ end
10
+
11
+ should 'should display the standard unauthorized page' do
12
+ instance_eval(&block)
13
+ assert_template 'common/error'
14
+ end
15
+
16
+ context "with mime type of xml" do
17
+
18
+ should "should return an forbidden error" do
19
+ @request.env["HTTP_ACCEPT"] = "application/xml"
20
+ instance_eval(&block)
21
+ assert_response :forbidden
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ def mock_rate(stubs={})
28
+ @project = Project.generate!
29
+ stubs = {
30
+ :date_in_effect => Date.today,
31
+ :project => @project,
32
+ :amount => 100.0,
33
+ :user => @user
34
+ }.merge(stubs)
35
+ @mock_rate = Rate.generate(stubs)
36
+ end
37
+
38
+ def mock_locked_rate(stubs={})
39
+ @mock_rate = mock_rate
40
+ @mock_rate.time_entries << TimeEntry.generate!
41
+ @mock_rate
42
+ end
43
+
44
+ context "as regular user" do
45
+ setup do
46
+ @user = User.generate!
47
+ @request.session[:user_id] = @user.id
48
+ end
49
+
50
+ context "responding to GET index" do
51
+ should_be_unauthorized { get :index }
52
+ end
53
+
54
+ context "responding to GET show" do
55
+ should_be_unauthorized { get :show, :id => "37" }
56
+ end
57
+
58
+ context "responding to GET new" do
59
+ should_be_unauthorized { get :new }
60
+ end
61
+
62
+ context "responding to GET edit" do
63
+ should_be_unauthorized { get :edit, :id => "37" }
64
+ end
65
+
66
+ context "responding to POST create" do
67
+ should_be_unauthorized { post :create, :rate => {:these => 'params'} }
68
+ end
69
+
70
+ context "responding to PUT update" do
71
+ should_be_unauthorized { put :update, :id => "37", :rate => {:these => 'params'} }
72
+ end
73
+
74
+ context "responding to DELETE destroy" do
75
+ should_be_unauthorized { delete :destroy, :id => "37" }
76
+ end
77
+ end
78
+
79
+
80
+ context "as an administrator" do
81
+
82
+ setup do
83
+ @user = User.generate!(:admin => true)
84
+ @request.session[:user_id] = @user.id
85
+ end
86
+
87
+ context "responding to GET index" do
88
+
89
+ should "should redirect to the homepage" do
90
+ get :index
91
+ assert_redirected_to home_url
92
+ end
93
+
94
+ should "should display an error flash message" do
95
+ get :index
96
+ assert_match /not found/, flash[:error]
97
+ end
98
+
99
+ context "with mime type of xml" do
100
+
101
+ should "should return a 404 error" do
102
+ @request.env["HTTP_ACCEPT"] = "application/xml"
103
+ get :index
104
+ assert_response :not_found
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ context "responding to GET index with user" do
112
+ setup do
113
+ mock_rate
114
+ end
115
+
116
+ should "should expose all historic rates for the user as @rates" do
117
+ get :index, :user_id => @user.id
118
+ assert_equal assigns(:rates), [@mock_rate]
119
+ end
120
+
121
+ context "with mime type of xml" do
122
+
123
+ should "should render all rates as xml" do
124
+ @request.env["HTTP_ACCEPT"] = "application/xml"
125
+ get :index, :user_id => @user.id
126
+
127
+ assert_select 'rates' do
128
+ assert_select 'rate' do
129
+ assert_select 'id', :text => @mock_rate.id
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ context "responding to GET show" do
140
+ setup do
141
+ mock_rate
142
+ end
143
+
144
+ should "should expose the @requested rate as @rate" do
145
+ get :show, :id => @mock_rate.id
146
+ assert_equal assigns(:rate), @mock_rate
147
+ end
148
+
149
+ context "with mime type of xml" do
150
+
151
+ should "should render the requested rate as xml" do
152
+ @request.env["HTTP_ACCEPT"] = "application/xml"
153
+ get :show, :id => @mock_rate.id
154
+
155
+ assert_select 'rate' do
156
+ assert_select 'id', :text => @mock_rate.id
157
+ assert_select 'amount', :text => /100/
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+
166
+ context "responding to GET new" do
167
+
168
+ should "should redirect to the homepage" do
169
+ get :new
170
+ assert_redirected_to home_url
171
+ end
172
+
173
+ should "should display an error flash message" do
174
+ get :new
175
+ assert_match /not found/, flash[:error]
176
+ end
177
+
178
+ context "with mime type of xml" do
179
+
180
+ should "should return a 404 error" do
181
+ @request.env["HTTP_ACCEPT"] = "application/xml"
182
+ get :new
183
+ assert_response :not_found
184
+ end
185
+
186
+ end
187
+ end
188
+
189
+ context "responding to GET new with user" do
190
+ should 'should be successful' do
191
+ get :new, :user_id => @user.id
192
+ assert_response :success
193
+ end
194
+
195
+ should "should expose a new rate as @rate" do
196
+ get :new, :user_id => @user.id
197
+ assert assigns(:rate)
198
+ assert assigns(:rate).new_record?
199
+ end
200
+
201
+ end
202
+
203
+ context "responding to GET edit" do
204
+ setup do
205
+ mock_rate
206
+ end
207
+
208
+ should "should expose the requested rate as @rate" do
209
+ get :edit, :id => @mock_rate.id
210
+ assert_equal assigns(:rate), @mock_rate
211
+ end
212
+
213
+ context "on a locked rate" do
214
+ setup do
215
+ mock_locked_rate
216
+ end
217
+
218
+ should 'should not have a Update button' do
219
+ get :edit, :id => @mock_rate.id
220
+ assert_select "input[type=submit]", :count => 0
221
+ end
222
+
223
+ should 'should show the locked icon' do
224
+ get :edit, :id => @mock_rate.id
225
+ assert_select "img[src*=locked.png]"
226
+ end
227
+ end
228
+
229
+ end
230
+
231
+ context "responding to POST create" do
232
+
233
+ context "with valid params" do
234
+ setup do
235
+ @project = Project.generate!
236
+ end
237
+
238
+ should "should expose a newly created rate as @rate" do
239
+ post :create, :rate => {:project_id => @project.id, :amount => '50', :date_in_effect => Date.today.to_s, :user_id => @user.id}
240
+ assert assigns(:rate)
241
+ end
242
+
243
+ should "should redirect to the rate list" do
244
+ post :create, :rate => {:project_id => @project.id, :amount => '50', :date_in_effect => Date.today.to_s, :user_id => @user.id}
245
+
246
+ assert_redirected_to rates_url(:user_id => @user.id)
247
+ end
248
+
249
+ should 'should redirect to the back_url if set' do
250
+ back_url = '/rates'
251
+ post :create, :rate => {:project_id => @project.id, :amount => '50', :date_in_effect => Date.today.to_s, :user_id => @user.id}, :back_url => back_url
252
+
253
+ assert_redirected_to back_url
254
+ end
255
+
256
+ end
257
+
258
+ context "with invalid params" do
259
+ should "should expose a newly created but unsaved rate as @rate" do
260
+ post :create, :rate => {}
261
+ assert assigns(:rate).new_record?
262
+ end
263
+
264
+ should "should re-render the 'new' template" do
265
+ post :create, :rate => {}
266
+ assert_template 'new'
267
+ end
268
+
269
+ end
270
+
271
+ end
272
+
273
+ context "responding to PUT udpate" do
274
+
275
+ context "with valid params" do
276
+ setup do
277
+ mock_rate
278
+ end
279
+
280
+ should "should update the requested rate" do
281
+ put :update, :id => @mock_rate.id, :rate => {:amount => '150'}
282
+
283
+ assert_equal 150.0, @mock_rate.reload.amount
284
+ end
285
+
286
+ should "should expose the requested rate as @rate" do
287
+ put :update, :id => @mock_rate.id
288
+
289
+ assert_equal assigns(:rate), @mock_rate
290
+ end
291
+
292
+ should "should redirect to the rate list" do
293
+ put :update, :id => "1"
294
+
295
+ assert_redirected_to rates_url(:user_id => @user.id)
296
+ end
297
+
298
+ should 'should redirect to the back_url if set' do
299
+ back_url = '/rates'
300
+ put :update, :id => "1", :back_url => back_url
301
+
302
+ assert_redirected_to back_url
303
+ end
304
+
305
+ end
306
+
307
+ context "with invalid params" do
308
+ setup do
309
+ mock_rate
310
+ end
311
+
312
+ should "should not update the requested rate" do
313
+ put :update, :id => @mock_rate.id, :rate => {:amount => 'asdf'}
314
+
315
+ assert_equal 100.0, @mock_rate.reload.amount
316
+ end
317
+
318
+ should "should expose the rate as @rate" do
319
+ put :update, :id => @mock_rate.id, :rate => {:amount => 'asdf'}
320
+
321
+ assert_equal assigns(:rate), @mock_rate
322
+ end
323
+
324
+ should "should re-render the 'edit' template" do
325
+ put :update, :id => @mock_rate.id, :rate => {:amount => 'asdf'}
326
+
327
+ assert_template 'edit'
328
+ end
329
+
330
+ end
331
+
332
+ context "on a locked rate" do
333
+ setup do
334
+ mock_locked_rate
335
+ end
336
+
337
+ should "should not save the rate" do
338
+ put :update, :id => @mock_rate.id, :rate => {:amount => '150'}
339
+
340
+ assert_equal 100, @mock_rate.reload.amount
341
+ end
342
+
343
+ should "should set the locked rate as @rate" do
344
+ put :update, :id => @mock_rate.id, :rate => { :amount => 200.0 }
345
+
346
+ assert_equal assigns(:rate), @mock_rate
347
+ end
348
+
349
+ should "should re-render the 'edit' template" do
350
+ put :update, :id => @mock_rate.id
351
+
352
+ assert_template 'edit'
353
+ end
354
+
355
+ should "should render an error message" do
356
+ put :update, :id => @mock_rate.id
357
+
358
+ assert_match /locked/, flash[:error]
359
+ end
360
+ end
361
+
362
+ end
363
+
364
+ context "responding to DELETE destroy" do
365
+ setup do
366
+ mock_rate
367
+ end
368
+
369
+ should "should destroy the requested rate" do
370
+ assert_difference('Rate.count', -1) do
371
+ delete :destroy, :id => @mock_rate.id
372
+ end
373
+ end
374
+
375
+ should "should redirect to the user's rates list" do
376
+ delete :destroy, :id => @mock_rate.id
377
+ assert_redirected_to rates_url(:user_id => @user.id)
378
+ end
379
+
380
+ should 'should redirect to the back_url if set' do
381
+ back_url = '/rates'
382
+ delete :destroy, :id => "1", :back_url => back_url
383
+
384
+ assert_redirected_to back_url
385
+ end
386
+
387
+ context "on a locked rate" do
388
+ setup do
389
+ mock_locked_rate
390
+ end
391
+
392
+ should "should display an error message" do
393
+ delete :destroy, :id => @mock_rate.id
394
+ assert_match /locked/, flash[:error]
395
+ end
396
+ end
397
+
398
+ end
399
+
400
+ end
401
+ end
@@ -0,0 +1,81 @@
1
+ require 'test_helper'
2
+
3
+ class AdminPanelTest < ActionController::IntegrationTest
4
+ include Redmine::I18n
5
+
6
+ def setup
7
+ @last_caching_run = 4.days.ago.to_s
8
+ @last_cache_clearing_run = 7.days.ago.to_s
9
+
10
+ Setting.plugin_redmine_rate = {
11
+ 'last_caching_run' => @last_caching_run,
12
+ 'last_cache_clearing_run' => @last_cache_clearing_run
13
+ }
14
+
15
+ @user = User.generate!(:admin => true, :password => 'rates', :password_confirmation => 'rates')
16
+
17
+ login_as(@user.login, 'rates')
18
+ end
19
+
20
+ context "Rate Caches admin panel" do
21
+ should "be listed in the main Admin section" do
22
+ click_link "Administration"
23
+ assert_response :success
24
+
25
+ assert_select "#admin-menu" do
26
+ assert_select "a.rate-caches"
27
+ end
28
+
29
+ end
30
+
31
+ should "show the last run timestamp for the last caching run" do
32
+ click_link "Administration"
33
+ click_link "Rate Caches"
34
+
35
+ assert_select '#caching-run' do
36
+ assert_select 'p', :text => /#{format_time(@last_caching_run)}/
37
+ end
38
+
39
+ end
40
+
41
+ should "show the last run timestamp for the last cache clearing run" do
42
+ click_link "Administration"
43
+ click_link "Rate Caches"
44
+
45
+ assert_select '#cache-clearing-run' do
46
+ assert_select 'p', :text => /#{format_time(@last_cache_clearing_run)}/
47
+ end
48
+
49
+ end
50
+
51
+ should "have a button to force a caching run" do
52
+ click_link "Administration"
53
+ click_link "Rate Caches"
54
+ click_button "Load Missing Caches"
55
+
56
+ assert_response :success
57
+
58
+ appx_clear_time = Date.today.strftime("%m/%d/%Y")
59
+
60
+ assert_select '#caching-run' do
61
+ assert_select 'p', :text => /#{appx_clear_time}/
62
+ end
63
+
64
+ end
65
+
66
+ should "have a button to force a cache clearing run" do
67
+ click_link "Administration"
68
+ click_link "Rate Caches"
69
+ click_button "Clear and Load All Caches"
70
+
71
+ assert_response :success
72
+
73
+ appx_clear_time = Date.today.strftime("%m/%d/%Y")
74
+
75
+ assert_select '#cache-clearing-run' do
76
+ assert_select 'p', :text => /#{appx_clear_time}/
77
+ end
78
+
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,16 @@
1
+ require "#{File.dirname(__FILE__)}/../test_helper"
2
+
3
+ class RoutingTest < ActionController::IntegrationTest
4
+ context "routing rates" do
5
+ should_route :get, "/rates", :controller => "rates", :action => "index"
6
+ should_route :get, "/rates/new", :controller => "rates", :action => "new"
7
+ should_route :get, "/rates/1", :controller => "rates", :action => "show", :id => "1"
8
+ should_route :get, "/rates/1/edit", :controller => "rates", :action => "edit", :id => "1"
9
+
10
+ should_route :post, "/rates", :controller => "rates", :action => "create"
11
+
12
+ should_route :put, "/rates/1", :controller => "rates", :action => "update", :id => "1"
13
+
14
+ should_route :delete, "/rates/1", :controller => "rates", :action => "destroy", :id => "1"
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ # Load the normal Rails helper
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper')
3
+
4
+ # Ensure that we are using the temporary fixture path
5
+ Engines::Testing.set_fixture_path
6
+
7
+ require "webrat"
8
+
9
+ Webrat.configure do |config|
10
+ config.mode = :rails
11
+ end
12
+
13
+ module IntegrationTestHelper
14
+ def login_as(user="existing", password="existing")
15
+ visit "/login"
16
+ fill_in 'Login', :with => user
17
+ fill_in 'Password', :with => password
18
+ click_button 'login'
19
+ assert_response :success
20
+ assert User.current.logged?
21
+ end
22
+
23
+ def logout
24
+ visit '/logout'
25
+ assert_response :success
26
+ assert !User.current.logged?
27
+ end
28
+
29
+ def assert_forbidden
30
+ assert_response :forbidden
31
+ assert_template 'common/error'
32
+ end
33
+
34
+ def assert_requires_login
35
+ assert_response :success
36
+ assert_template 'account/login'
37
+ end
38
+
39
+ end
40
+
41
+ class ActionController::IntegrationTest
42
+ include IntegrationTestHelper
43
+ end
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ class RateTimeEntryPatchTest < ActiveSupport::TestCase
4
+ def setup
5
+ @user = User.generate!
6
+ @project = Project.generate!
7
+ @date = Date.today.to_s
8
+ @time_entry = TimeEntry.new({:user => @user, :project => @project, :spent_on => @date, :hours => 10.0, :activity => TimeEntryActivity.generate!})
9
+ @rate = Rate.generate!(:user => @user, :project => @project, :date_in_effect => @date, :amount => 200.0)
10
+ end
11
+
12
+ should 'should return 0.0 if there are no rates for the user' do
13
+ @rate.destroy
14
+ assert_equal 0.0, @time_entry.cost
15
+ end
16
+
17
+ context 'should return the product of hours by' do
18
+ should 'the results of Rate.amount_for' do
19
+ assert_equal((200.0 * @time_entry.hours), @time_entry.cost)
20
+ end
21
+
22
+ should 'the assigned rate' do
23
+ rate = Rate.generate!(:user => @user, :project => @project, :date_in_effect => @date, :amount => 100.0)
24
+ @time_entry.rate = rate
25
+ assert_equal rate.amount * @time_entry.hours, @time_entry.cost
26
+ end
27
+
28
+ end
29
+
30
+ context "#cost" do
31
+ setup do
32
+ @time_entry.save!
33
+ end
34
+
35
+ context "without a cache" do
36
+ should "return the calculated cost" do
37
+ @time_entry.update_attribute(:cost, nil)
38
+ assert_equal 2000.0, @time_entry.cost
39
+ end
40
+
41
+ should "cache the cost to the field" do
42
+ @time_entry.update_attribute(:cost, nil)
43
+ @time_entry.cost
44
+
45
+ assert_equal 2000.0, @time_entry.read_attribute(:cost)
46
+ assert_equal 2000.0, @time_entry.reload.read_attribute(:cost)
47
+ end
48
+
49
+ end
50
+
51
+ context "with a cache" do
52
+ setup do
53
+ @time_entry.update_attribute(:cost, 2000.0)
54
+ @time_entry.reload
55
+ end
56
+
57
+ should "return the cached cost" do
58
+ assert_equal 2000.0, @time_entry.read_attribute(:cost)
59
+ assert_equal 2000.0, @time_entry.cost
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ context "before save" do
67
+ should "clear and recalculate the cache" do
68
+ assert_equal nil, @time_entry.read_attribute(:cost)
69
+
70
+ assert @time_entry.save
71
+
72
+ assert_equal 2000.0, @time_entry.read_attribute(:cost)
73
+ end
74
+ end
75
+
76
+
77
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ class UsersHelperWrapper
4
+ include UsersHelper
5
+ end
6
+
7
+ class RateUsersHelperPatchTest < ActiveSupport::TestCase
8
+ should 'should return 3 tabs' do
9
+ helper = UsersHelperWrapper.new
10
+ assert_equal 3, helper.user_settings_tabs.length
11
+ end
12
+
13
+ should 'should include a rate tab at the end' do
14
+ helper = UsersHelperWrapper.new
15
+ rate_tab = helper.user_settings_tabs[-1]
16
+ assert_not_nil rate_tab
17
+ end
18
+
19
+ context 'rate tab' do
20
+ setup do
21
+ helper = UsersHelperWrapper.new
22
+ @rate_tab = helper.user_settings_tabs[-1]
23
+ end
24
+
25
+ should 'should have the name of "rates"' do
26
+ assert_equal 'rates', @rate_tab[:name]
27
+ end
28
+
29
+ should 'should use the rates partial' do
30
+ assert_equal 'users/rates', @rate_tab[:partial]
31
+ end
32
+
33
+ should 'should use the i18n rates label' do
34
+ assert_equal :rate_label_rate_history, @rate_tab[:label]
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require File.dirname(__FILE__) + '/../../../../test_helper'
2
+
3
+ class RedmineRate::Hooks::PluginTimesheetViewTimesheetsReportHeaderTagsTest < ActionController::TestCase
4
+ include Redmine::Hook::Helper
5
+
6
+ def controller
7
+ @controller ||= ApplicationController.new
8
+ @controller.response ||= ActionController::TestResponse.new
9
+ @controller
10
+ end
11
+
12
+ def request
13
+ @request ||= ActionController::TestRequest.new
14
+ end
15
+
16
+ def hook(args={})
17
+ call_hook :plugin_timesheet_view_timesheets_report_header_tags, args
18
+ end
19
+
20
+ context "#plugin_timesheet_view_timesheets_report_header_tags" do
21
+ should "return a css string" do
22
+ @response.body = hook
23
+ assert_select "style", :text => /missing-rate/
24
+ end
25
+ end
26
+ end