##// END OF EJS Templates
Fixed validation when logging time on issue (#19464)....
Jean-Philippe Lang -
r13780:3d1c40cd525f
parent child
Show More
@@ -1,126 +1,131
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class TimeEntry < ActiveRecord::Base
19 19 include Redmine::SafeAttributes
20 20 # could have used polymorphic association
21 21 # project association here allows easy loading of time entries at project level with one database trip
22 22 belongs_to :project
23 23 belongs_to :issue
24 24 belongs_to :user
25 25 belongs_to :activity, :class_name => 'TimeEntryActivity'
26 26
27 27 attr_protected :user_id, :tyear, :tmonth, :tweek
28 28
29 29 acts_as_customizable
30 30 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
31 31 :url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
32 32 :author => :user,
33 33 :group => :issue,
34 34 :description => :comments
35 35
36 36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
37 37 :author_key => :user_id,
38 38 :scope => joins(:project).preload(:project)
39 39
40 40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
41 41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
42 42 validates_length_of :comments, :maximum => 255, :allow_nil => true
43 43 validates :spent_on, :date => true
44 44 before_validation :set_project_if_nil
45 45 validate :validate_time_entry
46 46
47 47 scope :visible, lambda {|*args|
48 48 joins(:project).
49 49 where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
50 50 }
51 51 scope :on_issue, lambda {|issue|
52 52 joins(:issue).
53 53 where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
54 54 }
55 55
56 56 safe_attributes 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
57 57
58 58 def initialize(attributes=nil, *args)
59 59 super
60 60 if new_record? && self.activity.nil?
61 61 if default_activity = TimeEntryActivity.default
62 62 self.activity_id = default_activity.id
63 63 end
64 64 self.hours = nil if hours == 0
65 65 end
66 66 end
67 67
68 68 def safe_attributes=(attrs, user=User.current)
69 69 if attrs
70 70 attrs = super(attrs)
71 if issue_id_changed? && attrs[:project_id].blank? && issue && issue.project_id != project_id
71 if issue_id_changed? && issue
72 72 if user.allowed_to?(:log_time, issue.project)
73 self.project_id = issue.project_id
73 if attrs[:project_id].blank? && issue.project_id != project_id
74 self.project_id = issue.project_id
75 end
76 @invalid_issue_id = nil
77 else
78 @invalid_issue_id = issue_id
74 79 end
75 80 end
76 81 end
77 82 attrs
78 83 end
79 84
80 85 def set_project_if_nil
81 86 self.project = issue.project if issue && project.nil?
82 87 end
83 88
84 89 def validate_time_entry
85 90 errors.add :hours, :invalid if hours && (hours < 0 || hours >= 1000)
86 91 errors.add :project_id, :invalid if project.nil?
87 errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project)
92 errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project) || @invalid_issue_id
88 93 end
89 94
90 95 def hours=(h)
91 96 write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
92 97 end
93 98
94 99 def hours
95 100 h = read_attribute(:hours)
96 101 if h.is_a?(Float)
97 102 h.round(2)
98 103 else
99 104 h
100 105 end
101 106 end
102 107
103 108 # tyear, tmonth, tweek assigned where setting spent_on attributes
104 109 # these attributes make time aggregations easier
105 110 def spent_on=(date)
106 111 super
107 112 self.tyear = spent_on ? spent_on.year : nil
108 113 self.tmonth = spent_on ? spent_on.month : nil
109 114 self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
110 115 end
111 116
112 117 # Returns true if the time entry can be edited by usr, otherwise false
113 118 def editable_by?(usr)
114 119 (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
115 120 end
116 121
117 122 # Returns the custom_field_values that can be edited by the given user
118 123 def editable_custom_field_values(user=nil)
119 124 visible_custom_field_values
120 125 end
121 126
122 127 # Returns the custom fields that can be edited by the given user
123 128 def editable_custom_fields(user=nil)
124 129 editable_custom_field_values(user).map(&:custom_field).uniq
125 130 end
126 131 end
@@ -1,723 +1,773
1 1 # -*- coding: utf-8 -*-
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 4 #
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; either version 2
8 8 # of the License, or (at your option) any later version.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 18
19 19 require File.expand_path('../../test_helper', __FILE__)
20 20
21 21 class TimelogControllerTest < ActionController::TestCase
22 22 fixtures :projects, :enabled_modules, :roles, :members,
23 23 :member_roles, :issues, :time_entries, :users,
24 24 :trackers, :enumerations, :issue_statuses,
25 25 :custom_fields, :custom_values,
26 26 :projects_trackers, :custom_fields_trackers,
27 27 :custom_fields_projects
28 28
29 29 include Redmine::I18n
30 30
31 31 def test_new
32 32 @request.session[:user_id] = 3
33 33 get :new
34 34 assert_response :success
35 35 assert_template 'new'
36 36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
37 37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
38 38 assert_select 'select[name=?]', 'time_entry[project_id]' do
39 39 # blank option for project
40 40 assert_select 'option[value=""]'
41 41 end
42 42 end
43 43
44 44 def test_new_with_project_id
45 45 @request.session[:user_id] = 3
46 46 get :new, :project_id => 1
47 47 assert_response :success
48 48 assert_template 'new'
49 49 assert_select 'input[name=?][type=hidden]', 'project_id'
50 50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
51 51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
52 52 end
53 53
54 54 def test_new_with_issue_id
55 55 @request.session[:user_id] = 3
56 56 get :new, :issue_id => 2
57 57 assert_response :success
58 58 assert_template 'new'
59 59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
60 60 assert_select 'input[name=?][type=hidden]', 'issue_id'
61 61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
62 62 end
63 63
64 64 def test_new_without_project_should_prefill_the_form
65 65 @request.session[:user_id] = 3
66 66 get :new, :time_entry => {:project_id => '1'}
67 67 assert_response :success
68 68 assert_template 'new'
69 69 assert_select 'select[name=?]', 'time_entry[project_id]' do
70 70 assert_select 'option[value="1"][selected=selected]'
71 71 end
72 72 end
73 73
74 74 def test_new_without_project_should_deny_without_permission
75 75 Role.all.each {|role| role.remove_permission! :log_time}
76 76 @request.session[:user_id] = 3
77 77
78 78 get :new
79 79 assert_response 403
80 80 end
81 81
82 82 def test_new_should_select_default_activity
83 83 @request.session[:user_id] = 3
84 84 get :new, :project_id => 1
85 85 assert_response :success
86 86 assert_select 'select[name=?]', 'time_entry[activity_id]' do
87 87 assert_select 'option[selected=selected]', :text => 'Development'
88 88 end
89 89 end
90 90
91 91 def test_new_should_only_show_active_time_entry_activities
92 92 @request.session[:user_id] = 3
93 93 get :new, :project_id => 1
94 94 assert_response :success
95 95 assert_select 'option', :text => 'Inactive Activity', :count => 0
96 96 end
97 97
98 98 def test_get_edit_existing_time
99 99 @request.session[:user_id] = 2
100 100 get :edit, :id => 2, :project_id => nil
101 101 assert_response :success
102 102 assert_template 'edit'
103 103 assert_select 'form[action=?]', '/time_entries/2'
104 104 end
105 105
106 106 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
107 107 te = TimeEntry.find(1)
108 108 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
109 109 te.save!
110 110
111 111 @request.session[:user_id] = 1
112 112 get :edit, :project_id => 1, :id => 1
113 113 assert_response :success
114 114 assert_template 'edit'
115 115 # Blank option since nothing is pre-selected
116 116 assert_select 'option', :text => '--- Please select ---'
117 117 end
118 118
119 119 def test_post_create
120 120 @request.session[:user_id] = 3
121 121 assert_difference 'TimeEntry.count' do
122 122 post :create, :project_id => 1,
123 123 :time_entry => {:comments => 'Some work on TimelogControllerTest',
124 124 # Not the default activity
125 125 :activity_id => '11',
126 126 :spent_on => '2008-03-14',
127 127 :issue_id => '1',
128 128 :hours => '7.3'}
129 129 assert_redirected_to '/projects/ecookbook/time_entries'
130 130 end
131 131
132 132 t = TimeEntry.order('id DESC').first
133 133 assert_not_nil t
134 134 assert_equal 'Some work on TimelogControllerTest', t.comments
135 135 assert_equal 1, t.project_id
136 136 assert_equal 1, t.issue_id
137 137 assert_equal 11, t.activity_id
138 138 assert_equal 7.3, t.hours
139 139 assert_equal 3, t.user_id
140 140 end
141 141
142 142 def test_post_create_with_blank_issue
143 143 @request.session[:user_id] = 3
144 144 assert_difference 'TimeEntry.count' do
145 145 post :create, :project_id => 1,
146 146 :time_entry => {:comments => 'Some work on TimelogControllerTest',
147 147 # Not the default activity
148 148 :activity_id => '11',
149 149 :issue_id => '',
150 150 :spent_on => '2008-03-14',
151 151 :hours => '7.3'}
152 152 assert_redirected_to '/projects/ecookbook/time_entries'
153 153 end
154 154
155 155 t = TimeEntry.order('id DESC').first
156 156 assert_not_nil t
157 157 assert_equal 'Some work on TimelogControllerTest', t.comments
158 158 assert_equal 1, t.project_id
159 159 assert_nil t.issue_id
160 160 assert_equal 11, t.activity_id
161 161 assert_equal 7.3, t.hours
162 162 assert_equal 3, t.user_id
163 163 end
164 164
165 def test_create_on_project_with_time_tracking_disabled_should_fail
166 Project.find(1).disable_module! :time_tracking
167
168 @request.session[:user_id] = 2
169 assert_no_difference 'TimeEntry.count' do
170 post :create, :time_entry => {
171 :project_id => '1', :issue_id => '',
172 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
173 }
174 end
175 end
176
177 def test_create_on_project_without_permission_should_fail
178 Role.find(1).remove_permission! :log_time
179
180 @request.session[:user_id] = 2
181 assert_no_difference 'TimeEntry.count' do
182 post :create, :time_entry => {
183 :project_id => '1', :issue_id => '',
184 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
185 }
186 end
187 end
188
189 def test_create_on_issue_in_project_with_time_tracking_disabled_should_fail
190 Project.find(1).disable_module! :time_tracking
191
192 @request.session[:user_id] = 2
193 assert_no_difference 'TimeEntry.count' do
194 post :create, :time_entry => {
195 :project_id => '', :issue_id => '1',
196 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
197 }
198 assert_select_error /Issue is invalid/
199 end
200 end
201
202 def test_create_on_issue_in_project_without_permission_should_fail
203 Role.find(1).remove_permission! :log_time
204
205 @request.session[:user_id] = 2
206 assert_no_difference 'TimeEntry.count' do
207 post :create, :time_entry => {
208 :project_id => '', :issue_id => '1',
209 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
210 }
211 assert_select_error /Issue is invalid/
212 end
213 end
214
165 215 def test_create_and_continue_at_project_level
166 216 @request.session[:user_id] = 2
167 217 assert_difference 'TimeEntry.count' do
168 218 post :create, :time_entry => {:project_id => '1',
169 219 :activity_id => '11',
170 220 :issue_id => '',
171 221 :spent_on => '2008-03-14',
172 222 :hours => '7.3'},
173 223 :continue => '1'
174 224 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
175 225 end
176 226 end
177 227
178 228 def test_create_and_continue_at_issue_level
179 229 @request.session[:user_id] = 2
180 230 assert_difference 'TimeEntry.count' do
181 231 post :create, :time_entry => {:project_id => '',
182 232 :activity_id => '11',
183 233 :issue_id => '1',
184 234 :spent_on => '2008-03-14',
185 235 :hours => '7.3'},
186 236 :continue => '1'
187 237 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
188 238 end
189 239 end
190 240
191 241 def test_create_and_continue_with_project_id
192 242 @request.session[:user_id] = 2
193 243 assert_difference 'TimeEntry.count' do
194 244 post :create, :project_id => 1,
195 245 :time_entry => {:activity_id => '11',
196 246 :issue_id => '',
197 247 :spent_on => '2008-03-14',
198 248 :hours => '7.3'},
199 249 :continue => '1'
200 250 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
201 251 end
202 252 end
203 253
204 254 def test_create_and_continue_with_issue_id
205 255 @request.session[:user_id] = 2
206 256 assert_difference 'TimeEntry.count' do
207 257 post :create, :issue_id => 1,
208 258 :time_entry => {:activity_id => '11',
209 259 :issue_id => '1',
210 260 :spent_on => '2008-03-14',
211 261 :hours => '7.3'},
212 262 :continue => '1'
213 263 assert_redirected_to '/issues/1/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
214 264 end
215 265 end
216 266
217 267 def test_create_without_log_time_permission_should_be_denied
218 268 @request.session[:user_id] = 2
219 269 Role.find_by_name('Manager').remove_permission! :log_time
220 270 post :create, :project_id => 1,
221 271 :time_entry => {:activity_id => '11',
222 272 :issue_id => '',
223 273 :spent_on => '2008-03-14',
224 274 :hours => '7.3'}
225 275
226 276 assert_response 403
227 277 end
228 278
229 279 def test_create_without_project_and_issue_should_fail
230 280 @request.session[:user_id] = 2
231 281 post :create, :time_entry => {:issue_id => ''}
232 282
233 283 assert_response :success
234 284 assert_template 'new'
235 285 end
236 286
237 287 def test_create_with_failure
238 288 @request.session[:user_id] = 2
239 289 post :create, :project_id => 1,
240 290 :time_entry => {:activity_id => '',
241 291 :issue_id => '',
242 292 :spent_on => '2008-03-14',
243 293 :hours => '7.3'}
244 294
245 295 assert_response :success
246 296 assert_template 'new'
247 297 end
248 298
249 299 def test_create_without_project
250 300 @request.session[:user_id] = 2
251 301 assert_difference 'TimeEntry.count' do
252 302 post :create, :time_entry => {:project_id => '1',
253 303 :activity_id => '11',
254 304 :issue_id => '',
255 305 :spent_on => '2008-03-14',
256 306 :hours => '7.3'}
257 307 end
258 308
259 309 assert_redirected_to '/projects/ecookbook/time_entries'
260 310 time_entry = TimeEntry.order('id DESC').first
261 311 assert_equal 1, time_entry.project_id
262 312 end
263 313
264 314 def test_create_without_project_should_fail_with_issue_not_inside_project
265 315 @request.session[:user_id] = 2
266 316 assert_no_difference 'TimeEntry.count' do
267 317 post :create, :time_entry => {:project_id => '1',
268 318 :activity_id => '11',
269 319 :issue_id => '5',
270 320 :spent_on => '2008-03-14',
271 321 :hours => '7.3'}
272 322 end
273 323
274 324 assert_response :success
275 325 assert assigns(:time_entry).errors[:issue_id].present?
276 326 end
277 327
278 328 def test_create_without_project_should_deny_without_permission
279 329 @request.session[:user_id] = 2
280 330 Project.find(3).disable_module!(:time_tracking)
281 331
282 332 assert_no_difference 'TimeEntry.count' do
283 333 post :create, :time_entry => {:project_id => '3',
284 334 :activity_id => '11',
285 335 :issue_id => '',
286 336 :spent_on => '2008-03-14',
287 337 :hours => '7.3'}
288 338 end
289 339
290 340 assert_response 403
291 341 end
292 342
293 343 def test_create_without_project_with_failure
294 344 @request.session[:user_id] = 2
295 345 assert_no_difference 'TimeEntry.count' do
296 346 post :create, :time_entry => {:project_id => '1',
297 347 :activity_id => '11',
298 348 :issue_id => '',
299 349 :spent_on => '2008-03-14',
300 350 :hours => ''}
301 351 end
302 352
303 353 assert_response :success
304 354 assert_select 'select[name=?]', 'time_entry[project_id]' do
305 355 assert_select 'option[value="1"][selected=selected]'
306 356 end
307 357 end
308 358
309 359 def test_update
310 360 entry = TimeEntry.find(1)
311 361 assert_equal 1, entry.issue_id
312 362 assert_equal 2, entry.user_id
313 363
314 364 @request.session[:user_id] = 1
315 365 put :update, :id => 1,
316 366 :time_entry => {:issue_id => '2',
317 367 :hours => '8'}
318 368 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
319 369 entry.reload
320 370
321 371 assert_equal 8, entry.hours
322 372 assert_equal 2, entry.issue_id
323 373 assert_equal 2, entry.user_id
324 374 end
325 375
326 376 def test_update_should_allow_to_change_issue_to_another_project
327 377 entry = TimeEntry.generate!(:issue_id => 1)
328 378
329 379 @request.session[:user_id] = 1
330 380 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
331 381 assert_response 302
332 382 entry.reload
333 383
334 384 assert_equal 5, entry.issue_id
335 385 assert_equal 3, entry.project_id
336 386 end
337 387
338 388 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
339 389 entry = TimeEntry.generate!(:issue_id => 1)
340 390 Project.find(3).disable_module!(:time_tracking)
341 391
342 392 @request.session[:user_id] = 1
343 393 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
344 394 assert_response 200
345 395 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
346 396 end
347 397
348 398 def test_get_bulk_edit
349 399 @request.session[:user_id] = 2
350 400 get :bulk_edit, :ids => [1, 2]
351 401 assert_response :success
352 402 assert_template 'bulk_edit'
353 403
354 404 assert_select 'ul#bulk-selection' do
355 405 assert_select 'li', 2
356 406 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
357 407 end
358 408
359 409 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
360 410 # System wide custom field
361 411 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
362 412
363 413 # Activities
364 414 assert_select 'select[name=?]', 'time_entry[activity_id]' do
365 415 assert_select 'option[value=""]', :text => '(No change)'
366 416 assert_select 'option[value="9"]', :text => 'Design'
367 417 end
368 418 end
369 419 end
370 420
371 421 def test_get_bulk_edit_on_different_projects
372 422 @request.session[:user_id] = 2
373 423 get :bulk_edit, :ids => [1, 2, 6]
374 424 assert_response :success
375 425 assert_template 'bulk_edit'
376 426 end
377 427
378 428 def test_bulk_update
379 429 @request.session[:user_id] = 2
380 430 # update time entry activity
381 431 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382 432
383 433 assert_response 302
384 434 # check that the issues were updated
385 435 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 436 end
387 437
388 438 def test_bulk_update_with_failure
389 439 @request.session[:user_id] = 2
390 440 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391 441
392 442 assert_response 302
393 443 assert_match /Failed to save 2 time entrie/, flash[:error]
394 444 end
395 445
396 446 def test_bulk_update_on_different_projects
397 447 @request.session[:user_id] = 2
398 448 # makes user a manager on the other project
399 449 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400 450
401 451 # update time entry activity
402 452 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403 453
404 454 assert_response 302
405 455 # check that the issues were updated
406 456 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 457 end
408 458
409 459 def test_bulk_update_on_different_projects_without_rights
410 460 @request.session[:user_id] = 3
411 461 user = User.find(3)
412 462 action = { :controller => "timelog", :action => "bulk_update" }
413 463 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 464 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 465 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 466 assert_response 403
417 467 end
418 468
419 469 def test_bulk_update_custom_field
420 470 @request.session[:user_id] = 2
421 471 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422 472
423 473 assert_response 302
424 474 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 475 end
426 476
427 477 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 478 @request.session[:user_id] = 2
429 479 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430 480
431 481 assert_response :redirect
432 482 assert_redirected_to '/time_entries'
433 483 end
434 484
435 485 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 486 @request.session[:user_id] = 2
437 487 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438 488
439 489 assert_response :redirect
440 490 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 491 end
442 492
443 493 def test_post_bulk_update_without_edit_permission_should_be_denied
444 494 @request.session[:user_id] = 2
445 495 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 496 post :bulk_update, :ids => [1,2]
447 497
448 498 assert_response 403
449 499 end
450 500
451 501 def test_destroy
452 502 @request.session[:user_id] = 2
453 503 delete :destroy, :id => 1
454 504 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 505 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 506 assert_nil TimeEntry.find_by_id(1)
457 507 end
458 508
459 509 def test_destroy_should_fail
460 510 # simulate that this fails (e.g. due to a plugin), see #5700
461 511 TimeEntry.any_instance.expects(:destroy).returns(false)
462 512
463 513 @request.session[:user_id] = 2
464 514 delete :destroy, :id => 1
465 515 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 516 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 517 assert_not_nil TimeEntry.find_by_id(1)
468 518 end
469 519
470 520 def test_index_all_projects
471 521 get :index
472 522 assert_response :success
473 523 assert_template 'index'
474 524 assert_not_nil assigns(:total_hours)
475 525 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 526 assert_select 'form#query_form[action=?]', '/time_entries'
477 527 end
478 528
479 529 def test_index_all_projects_should_show_log_time_link
480 530 @request.session[:user_id] = 2
481 531 get :index
482 532 assert_response :success
483 533 assert_template 'index'
484 534 assert_select 'a[href=?]', '/time_entries/new', :text => /Log time/
485 535 end
486 536
487 537 def test_index_my_spent_time
488 538 @request.session[:user_id] = 2
489 539 get :index, :user_id => 'me'
490 540 assert_response :success
491 541 assert_template 'index'
492 542 assert assigns(:entries).all? {|entry| entry.user_id == 2}
493 543 end
494 544
495 545 def test_index_at_project_level
496 546 get :index, :project_id => 'ecookbook'
497 547 assert_response :success
498 548 assert_template 'index'
499 549 assert_not_nil assigns(:entries)
500 550 assert_equal 4, assigns(:entries).size
501 551 # project and subproject
502 552 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
503 553 assert_not_nil assigns(:total_hours)
504 554 assert_equal "162.90", "%.2f" % assigns(:total_hours)
505 555 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
506 556 end
507 557
508 558 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
509 559 entry = TimeEntry.generate!(:project => Project.find(3))
510 560
511 561 with_settings :display_subprojects_issues => '0' do
512 562 get :index, :project_id => 'ecookbook'
513 563 assert_response :success
514 564 assert_template 'index'
515 565 assert_not_include entry, assigns(:entries)
516 566 end
517 567 end
518 568
519 569 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
520 570 entry = TimeEntry.generate!(:project => Project.find(3))
521 571
522 572 with_settings :display_subprojects_issues => '0' do
523 573 get :index, :project_id => 'ecookbook', :subproject_id => 3
524 574 assert_response :success
525 575 assert_template 'index'
526 576 assert_include entry, assigns(:entries)
527 577 end
528 578 end
529 579
530 580 def test_index_at_project_level_with_date_range
531 581 get :index, :project_id => 'ecookbook',
532 582 :f => ['spent_on'],
533 583 :op => {'spent_on' => '><'},
534 584 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
535 585 assert_response :success
536 586 assert_template 'index'
537 587 assert_not_nil assigns(:entries)
538 588 assert_equal 3, assigns(:entries).size
539 589 assert_not_nil assigns(:total_hours)
540 590 assert_equal "12.90", "%.2f" % assigns(:total_hours)
541 591 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
542 592 end
543 593
544 594 def test_index_at_project_level_with_date_range_using_from_and_to_params
545 595 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
546 596 assert_response :success
547 597 assert_template 'index'
548 598 assert_not_nil assigns(:entries)
549 599 assert_equal 3, assigns(:entries).size
550 600 assert_not_nil assigns(:total_hours)
551 601 assert_equal "12.90", "%.2f" % assigns(:total_hours)
552 602 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
553 603 end
554 604
555 605 def test_index_at_project_level_with_period
556 606 get :index, :project_id => 'ecookbook',
557 607 :f => ['spent_on'],
558 608 :op => {'spent_on' => '>t-'},
559 609 :v => {'spent_on' => ['7']}
560 610 assert_response :success
561 611 assert_template 'index'
562 612 assert_not_nil assigns(:entries)
563 613 assert_not_nil assigns(:total_hours)
564 614 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
565 615 end
566 616
567 617 def test_index_at_issue_level
568 618 get :index, :issue_id => 1
569 619 assert_response :success
570 620 assert_template 'index'
571 621 assert_not_nil assigns(:entries)
572 622 assert_equal 2, assigns(:entries).size
573 623 assert_not_nil assigns(:total_hours)
574 624 assert_equal 154.25, assigns(:total_hours)
575 625 # display all time
576 626 assert_nil assigns(:from)
577 627 assert_nil assigns(:to)
578 628 assert_select 'form#query_form[action=?]', '/issues/1/time_entries'
579 629 end
580 630
581 631 def test_index_should_sort_by_spent_on_and_created_on
582 632 t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
583 633 t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
584 634 t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
585 635
586 636 get :index, :project_id => 1,
587 637 :f => ['spent_on'],
588 638 :op => {'spent_on' => '><'},
589 639 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
590 640 assert_response :success
591 641 assert_equal [t2, t1, t3], assigns(:entries)
592 642
593 643 get :index, :project_id => 1,
594 644 :f => ['spent_on'],
595 645 :op => {'spent_on' => '><'},
596 646 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
597 647 :sort => 'spent_on'
598 648 assert_response :success
599 649 assert_equal [t3, t1, t2], assigns(:entries)
600 650 end
601 651
602 652 def test_index_with_filter_on_issue_custom_field
603 653 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
604 654 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
605 655
606 656 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
607 657 assert_response :success
608 658 assert_equal [entry], assigns(:entries)
609 659 end
610 660
611 661 def test_index_with_issue_custom_field_column
612 662 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
613 663 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
614 664
615 665 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
616 666 assert_response :success
617 667 assert_include :'issue.cf_2', assigns(:query).column_names
618 668 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
619 669 end
620 670
621 671 def test_index_with_time_entry_custom_field_column
622 672 field = TimeEntryCustomField.generate!(:field_format => 'string')
623 673 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
624 674 field_name = "cf_#{field.id}"
625 675
626 676 get :index, :c => ["hours", field_name]
627 677 assert_response :success
628 678 assert_include field_name.to_sym, assigns(:query).column_names
629 679 assert_select "td.#{field_name}", :text => 'CF Value'
630 680 end
631 681
632 682 def test_index_with_time_entry_custom_field_sorting
633 683 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
634 684 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
635 685 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
636 686 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
637 687 field_name = "cf_#{field.id}"
638 688
639 689 get :index, :c => ["hours", field_name], :sort => field_name
640 690 assert_response :success
641 691 assert_include field_name.to_sym, assigns(:query).column_names
642 692 assert_select "th a.sort", :text => 'String Field'
643 693
644 694 # Make sure that values are properly sorted
645 695 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
646 696 assert_equal 3, values.size
647 697 assert_equal values.sort, values
648 698 end
649 699
650 700 def test_index_atom_feed
651 701 get :index, :project_id => 1, :format => 'atom'
652 702 assert_response :success
653 703 assert_equal 'application/atom+xml', @response.content_type
654 704 assert_not_nil assigns(:items)
655 705 assert assigns(:items).first.is_a?(TimeEntry)
656 706 end
657 707
658 708 def test_index_at_project_level_should_include_csv_export_dialog
659 709 get :index, :project_id => 'ecookbook',
660 710 :f => ['spent_on'],
661 711 :op => {'spent_on' => '>='},
662 712 :v => {'spent_on' => ['2007-04-01']},
663 713 :c => ['spent_on', 'user']
664 714 assert_response :success
665 715
666 716 assert_select '#csv-export-options' do
667 717 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
668 718 # filter
669 719 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
670 720 assert_select 'input[name=?][value=?]', 'op[spent_on]', '>='
671 721 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
672 722 # columns
673 723 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
674 724 assert_select 'input[name=?][value=?]', 'c[]', 'user'
675 725 assert_select 'input[name=?]', 'c[]', 2
676 726 end
677 727 end
678 728 end
679 729
680 730 def test_index_cross_project_should_include_csv_export_dialog
681 731 get :index
682 732 assert_response :success
683 733
684 734 assert_select '#csv-export-options' do
685 735 assert_select 'form[action=?][method=get]', '/time_entries.csv'
686 736 end
687 737 end
688 738
689 739 def test_index_at_issue_level_should_include_csv_export_dialog
690 740 get :index, :issue_id => 3
691 741 assert_response :success
692 742
693 743 assert_select '#csv-export-options' do
694 744 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
695 745 end
696 746 end
697 747
698 748 def test_index_csv_all_projects
699 749 with_settings :date_format => '%m/%d/%Y' do
700 750 get :index, :format => 'csv'
701 751 assert_response :success
702 752 assert_equal 'text/csv; header=present', response.content_type
703 753 end
704 754 end
705 755
706 756 def test_index_csv
707 757 with_settings :date_format => '%m/%d/%Y' do
708 758 get :index, :project_id => 1, :format => 'csv'
709 759 assert_response :success
710 760 assert_equal 'text/csv; header=present', response.content_type
711 761 end
712 762 end
713 763
714 764 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
715 765 issue = Issue.find(1)
716 766 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
717 767
718 768 get :index, :format => 'csv'
719 769 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
720 770 assert_not_nil line
721 771 assert_include "#{issue.tracker} #1: #{issue.subject}", line
722 772 end
723 773 end
General Comments 0
You need to be logged in to leave comments. Login now