##// END OF EJS Templates
Fixed that project is ignored when entering an issue id on /time_entries/new form (#10020)....
Jean-Philippe Lang -
r8573:13f28858baf5
parent child
Show More
@@ -1,337 +1,339
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 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 TimelogController < ApplicationController
19 19 menu_item :issues
20 20
21 21 before_filter :find_project, :only => [:create]
22 22 before_filter :find_time_entry, :only => [:show, :edit, :update]
23 23 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
24 24 before_filter :authorize, :except => [:new, :index, :report]
25 25
26 26 before_filter :find_optional_project, :only => [:new, :index, :report]
27 27 before_filter :authorize_global, :only => [:new, :index, :report]
28 28
29 29 accept_rss_auth :index
30 30 accept_api_auth :index, :show, :create, :update, :destroy
31 31
32 32 helper :sort
33 33 include SortHelper
34 34 helper :issues
35 35 include TimelogHelper
36 36 helper :custom_fields
37 37 include CustomFieldsHelper
38 38
39 39 def index
40 40 sort_init 'spent_on', 'desc'
41 41 sort_update 'spent_on' => 'spent_on',
42 42 'user' => 'user_id',
43 43 'activity' => 'activity_id',
44 44 'project' => "#{Project.table_name}.name",
45 45 'issue' => 'issue_id',
46 46 'hours' => 'hours'
47 47
48 48 retrieve_date_range
49 49
50 50 scope = TimeEntry.visible.spent_between(@from, @to)
51 51 if @issue
52 52 scope = scope.on_issue(@issue)
53 53 elsif @project
54 54 scope = scope.on_project(@project, Setting.display_subprojects_issues?)
55 55 end
56 56
57 57 respond_to do |format|
58 58 format.html {
59 59 # Paginate results
60 60 @entry_count = scope.count
61 61 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
62 62 @entries = scope.all(
63 63 :include => [:project, :activity, :user, {:issue => :tracker}],
64 64 :order => sort_clause,
65 65 :limit => @entry_pages.items_per_page,
66 66 :offset => @entry_pages.current.offset
67 67 )
68 68 @total_hours = scope.sum(:hours).to_f
69 69
70 70 render :layout => !request.xhr?
71 71 }
72 72 format.api {
73 73 @entry_count = scope.count
74 74 @offset, @limit = api_offset_and_limit
75 75 @entries = scope.all(
76 76 :include => [:project, :activity, :user, {:issue => :tracker}],
77 77 :order => sort_clause,
78 78 :limit => @limit,
79 79 :offset => @offset
80 80 )
81 81 }
82 82 format.atom {
83 83 entries = scope.all(
84 84 :include => [:project, :activity, :user, {:issue => :tracker}],
85 85 :order => "#{TimeEntry.table_name}.created_on DESC",
86 86 :limit => Setting.feeds_limit.to_i
87 87 )
88 88 render_feed(entries, :title => l(:label_spent_time))
89 89 }
90 90 format.csv {
91 91 # Export all entries
92 92 @entries = scope.all(
93 93 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
94 94 :order => sort_clause
95 95 )
96 96 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
97 97 }
98 98 end
99 99 end
100 100
101 101 def report
102 102 retrieve_date_range
103 103 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
104 104
105 105 respond_to do |format|
106 106 format.html { render :layout => !request.xhr? }
107 107 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
108 108 end
109 109 end
110 110
111 111 def show
112 112 respond_to do |format|
113 113 # TODO: Implement html response
114 114 format.html { render :nothing => true, :status => 406 }
115 115 format.api
116 116 end
117 117 end
118 118
119 119 def new
120 120 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
121 121 @time_entry.attributes = params[:time_entry]
122 122 end
123 123
124 124 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
125 125 def create
126 126 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
127 127 @time_entry.attributes = params[:time_entry]
128 128
129 129 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
130 130
131 131 if @time_entry.save
132 132 respond_to do |format|
133 133 format.html {
134 134 flash[:notice] = l(:notice_successful_create)
135 135 if params[:continue]
136 136 if params[:project_id]
137 137 redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue, :back_url => params[:back_url]
138 138 else
139 139 redirect_to :action => 'new', :back_url => params[:back_url]
140 140 end
141 141 else
142 142 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
143 143 end
144 144 }
145 145 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
146 146 end
147 147 else
148 148 respond_to do |format|
149 149 format.html { render :action => 'new' }
150 150 format.api { render_validation_errors(@time_entry) }
151 151 end
152 152 end
153 153 end
154 154
155 155 def edit
156 156 @time_entry.attributes = params[:time_entry]
157 157 end
158 158
159 159 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
160 160 def update
161 161 @time_entry.attributes = params[:time_entry]
162 162
163 163 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
164 164
165 165 if @time_entry.save
166 166 respond_to do |format|
167 167 format.html {
168 168 flash[:notice] = l(:notice_successful_update)
169 169 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
170 170 }
171 171 format.api { head :ok }
172 172 end
173 173 else
174 174 respond_to do |format|
175 175 format.html { render :action => 'edit' }
176 176 format.api { render_validation_errors(@time_entry) }
177 177 end
178 178 end
179 179 end
180 180
181 181 def bulk_edit
182 182 @available_activities = TimeEntryActivity.shared.active
183 183 @custom_fields = TimeEntry.first.available_custom_fields
184 184 end
185 185
186 186 def bulk_update
187 187 attributes = parse_params_for_bulk_time_entry_attributes(params)
188 188
189 189 unsaved_time_entry_ids = []
190 190 @time_entries.each do |time_entry|
191 191 time_entry.reload
192 192 time_entry.attributes = attributes
193 193 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
194 194 unless time_entry.save
195 195 # Keep unsaved time_entry ids to display them in flash error
196 196 unsaved_time_entry_ids << time_entry.id
197 197 end
198 198 end
199 199 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
200 200 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
201 201 end
202 202
203 203 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
204 204 def destroy
205 205 @time_entries.each do |t|
206 206 begin
207 207 unless t.destroy && t.destroyed?
208 208 respond_to do |format|
209 209 format.html {
210 210 flash[:error] = l(:notice_unable_delete_time_entry)
211 211 redirect_to :back
212 212 }
213 213 format.api { render_validation_errors(t) }
214 214 end
215 215 return
216 216 end
217 217 rescue ::ActionController::RedirectBackError
218 218 redirect_to :action => 'index', :project_id => @projects.first
219 219 return
220 220 end
221 221 end
222 222
223 223 respond_to do |format|
224 224 format.html {
225 225 flash[:notice] = l(:notice_successful_delete)
226 226 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
227 227 }
228 228 format.api { head :ok }
229 229 end
230 230 end
231 231
232 232 private
233 233 def find_time_entry
234 234 @time_entry = TimeEntry.find(params[:id])
235 235 unless @time_entry.editable_by?(User.current)
236 236 render_403
237 237 return false
238 238 end
239 239 @project = @time_entry.project
240 240 rescue ActiveRecord::RecordNotFound
241 241 render_404
242 242 end
243 243
244 244 def find_time_entries
245 245 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
246 246 raise ActiveRecord::RecordNotFound if @time_entries.empty?
247 247 @projects = @time_entries.collect(&:project).compact.uniq
248 248 @project = @projects.first if @projects.size == 1
249 249 rescue ActiveRecord::RecordNotFound
250 250 render_404
251 251 end
252 252
253 253 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
254 254 if unsaved_time_entry_ids.empty?
255 255 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
256 256 else
257 257 flash[:error] = l(:notice_failed_to_save_time_entries,
258 258 :count => unsaved_time_entry_ids.size,
259 259 :total => time_entries.size,
260 260 :ids => '#' + unsaved_time_entry_ids.join(', #'))
261 261 end
262 262 end
263 263
264 264 def find_project
265 if (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
266 @project = Project.find(project_id)
267 end
265 268 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
266 269 @issue = Issue.find(issue_id)
267 @project = @issue.project
268 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
269 @project = Project.find(project_id)
270 else
270 @project ||= @issue.project
271 end
272 if @project.nil?
271 273 render_404
272 274 return false
273 275 end
274 276 rescue ActiveRecord::RecordNotFound
275 277 render_404
276 278 end
277 279
278 280 def find_optional_project
279 281 if !params[:issue_id].blank?
280 282 @issue = Issue.find(params[:issue_id])
281 283 @project = @issue.project
282 284 elsif !params[:project_id].blank?
283 285 @project = Project.find(params[:project_id])
284 286 end
285 287 end
286 288
287 289 # Retrieves the date range based on predefined ranges or specific from/to param dates
288 290 def retrieve_date_range
289 291 @free_period = false
290 292 @from, @to = nil, nil
291 293
292 294 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
293 295 case params[:period].to_s
294 296 when 'today'
295 297 @from = @to = Date.today
296 298 when 'yesterday'
297 299 @from = @to = Date.today - 1
298 300 when 'current_week'
299 301 @from = Date.today - (Date.today.cwday - 1)%7
300 302 @to = @from + 6
301 303 when 'last_week'
302 304 @from = Date.today - 7 - (Date.today.cwday - 1)%7
303 305 @to = @from + 6
304 306 when '7_days'
305 307 @from = Date.today - 7
306 308 @to = Date.today
307 309 when 'current_month'
308 310 @from = Date.civil(Date.today.year, Date.today.month, 1)
309 311 @to = (@from >> 1) - 1
310 312 when 'last_month'
311 313 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
312 314 @to = (@from >> 1) - 1
313 315 when '30_days'
314 316 @from = Date.today - 30
315 317 @to = Date.today
316 318 when 'current_year'
317 319 @from = Date.civil(Date.today.year, 1, 1)
318 320 @to = Date.civil(Date.today.year, 12, 31)
319 321 end
320 322 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
321 323 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
322 324 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
323 325 @free_period = true
324 326 else
325 327 # default
326 328 end
327 329
328 330 @from, @to = @to, @from if @from && @to && @from > @to
329 331 end
330 332
331 333 def parse_params_for_bulk_time_entry_attributes(params)
332 334 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
333 335 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
334 336 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
335 337 attributes
336 338 end
337 339 end
@@ -1,704 +1,718
1 1 # -*- coding: utf-8 -*-
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2011 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 require 'timelog_controller'
21 21
22 22 # Re-raise errors caught by the controller.
23 23 class TimelogController; def rescue_action(e) raise e end; end
24 24
25 25 class TimelogControllerTest < ActionController::TestCase
26 26 fixtures :projects, :enabled_modules, :roles, :members,
27 27 :member_roles, :issues, :time_entries, :users,
28 28 :trackers, :enumerations, :issue_statuses,
29 29 :custom_fields, :custom_values
30 30
31 31 include Redmine::I18n
32 32
33 33 def setup
34 34 @controller = TimelogController.new
35 35 @request = ActionController::TestRequest.new
36 36 @response = ActionController::TestResponse.new
37 37 end
38 38
39 39 def test_get_new
40 40 @request.session[:user_id] = 3
41 41 get :new, :project_id => 1
42 42 assert_response :success
43 43 assert_template 'new'
44 44 # Default activity selected
45 45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
46 46 :content => 'Development'
47 47 end
48 48
49 49 def test_get_new_should_only_show_active_time_entry_activities
50 50 @request.session[:user_id] = 3
51 51 get :new, :project_id => 1
52 52 assert_response :success
53 53 assert_template 'new'
54 54 assert_no_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
55 55 assert_no_tag 'option', :content => 'Inactive Activity'
56 56 end
57 57
58 58 def test_new_without_project
59 59 @request.session[:user_id] = 3
60 60 get :new
61 61 assert_response :success
62 62 assert_template 'new'
63 63 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'}
64 64 end
65 65
66 66 def test_new_without_project_should_deny_without_permission
67 67 Role.all.each {|role| role.remove_permission! :log_time}
68 68 @request.session[:user_id] = 3
69 69
70 70 get :new
71 71 assert_response 403
72 72 end
73 73
74 74 def test_get_edit_existing_time
75 75 @request.session[:user_id] = 2
76 76 get :edit, :id => 2, :project_id => nil
77 77 assert_response :success
78 78 assert_template 'edit'
79 79 # Default activity selected
80 80 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
81 81 end
82 82
83 83 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
84 84 te = TimeEntry.find(1)
85 85 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
86 86 te.save!
87 87
88 88 @request.session[:user_id] = 1
89 89 get :edit, :project_id => 1, :id => 1
90 90 assert_response :success
91 91 assert_template 'edit'
92 92 # Blank option since nothing is pre-selected
93 93 assert_tag :tag => 'option', :content => '--- Please select ---'
94 94 end
95 95
96 96 def test_post_create
97 97 # TODO: should POST to issues’ time log instead of project. change form
98 98 # and routing
99 99 @request.session[:user_id] = 3
100 100 post :create, :project_id => 1,
101 101 :time_entry => {:comments => 'Some work on TimelogControllerTest',
102 102 # Not the default activity
103 103 :activity_id => '11',
104 104 :spent_on => '2008-03-14',
105 105 :issue_id => '1',
106 106 :hours => '7.3'}
107 107 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
108 108
109 109 i = Issue.find(1)
110 110 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
111 111 assert_not_nil t
112 112 assert_equal 11, t.activity_id
113 113 assert_equal 7.3, t.hours
114 114 assert_equal 3, t.user_id
115 115 assert_equal i, t.issue
116 116 assert_equal i.project, t.project
117 117 end
118 118
119 119 def test_post_create_with_blank_issue
120 120 # TODO: should POST to issues’ time log instead of project. change form
121 121 # and routing
122 122 @request.session[:user_id] = 3
123 123 post :create, :project_id => 1,
124 124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
125 125 # Not the default activity
126 126 :activity_id => '11',
127 127 :issue_id => '',
128 128 :spent_on => '2008-03-14',
129 129 :hours => '7.3'}
130 130 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
131 131
132 132 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
133 133 assert_not_nil t
134 134 assert_equal 11, t.activity_id
135 135 assert_equal 7.3, t.hours
136 136 assert_equal 3, t.user_id
137 137 end
138 138
139 139 def test_create_and_continue
140 140 @request.session[:user_id] = 2
141 141 post :create, :project_id => 1,
142 142 :time_entry => {:activity_id => '11',
143 143 :issue_id => '',
144 144 :spent_on => '2008-03-14',
145 145 :hours => '7.3'},
146 146 :continue => '1'
147 147 assert_redirected_to '/projects/ecookbook/time_entries/new'
148 148 end
149 149
150 150 def test_create_and_continue_with_issue_id
151 151 @request.session[:user_id] = 2
152 152 post :create, :project_id => 1,
153 153 :time_entry => {:activity_id => '11',
154 154 :issue_id => '1',
155 155 :spent_on => '2008-03-14',
156 156 :hours => '7.3'},
157 157 :continue => '1'
158 158 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new'
159 159 end
160 160
161 161 def test_create_and_continue_without_project
162 162 @request.session[:user_id] = 2
163 163 post :create, :time_entry => {:project_id => '1',
164 164 :activity_id => '11',
165 165 :issue_id => '',
166 166 :spent_on => '2008-03-14',
167 167 :hours => '7.3'},
168 168 :continue => '1'
169 169
170 170 assert_redirected_to '/time_entries/new'
171 171 end
172 172
173 173 def test_create_without_log_time_permission_should_be_denied
174 174 @request.session[:user_id] = 2
175 175 Role.find_by_name('Manager').remove_permission! :log_time
176 176 post :create, :project_id => 1,
177 177 :time_entry => {:activity_id => '11',
178 178 :issue_id => '',
179 179 :spent_on => '2008-03-14',
180 180 :hours => '7.3'}
181 181
182 182 assert_response 403
183 183 end
184 184
185 185 def test_create_with_failure
186 186 @request.session[:user_id] = 2
187 187 post :create, :project_id => 1,
188 188 :time_entry => {:activity_id => '',
189 189 :issue_id => '',
190 190 :spent_on => '2008-03-14',
191 191 :hours => '7.3'}
192 192
193 193 assert_response :success
194 194 assert_template 'new'
195 195 end
196 196
197 197 def test_create_without_project
198 198 @request.session[:user_id] = 2
199 199 assert_difference 'TimeEntry.count' do
200 200 post :create, :time_entry => {:project_id => '1',
201 201 :activity_id => '11',
202 202 :issue_id => '',
203 203 :spent_on => '2008-03-14',
204 204 :hours => '7.3'}
205 205 end
206 206
207 207 assert_redirected_to '/projects/ecookbook/time_entries'
208 208 time_entry = TimeEntry.first(:order => 'id DESC')
209 209 assert_equal 1, time_entry.project_id
210 210 end
211 211
212 def test_create_without_project_should_fail_with_issue_not_inside_project
213 @request.session[:user_id] = 2
214 assert_no_difference 'TimeEntry.count' do
215 post :create, :time_entry => {:project_id => '1',
216 :activity_id => '11',
217 :issue_id => '5',
218 :spent_on => '2008-03-14',
219 :hours => '7.3'}
220 end
221
222 assert_response :success
223 assert assigns(:time_entry).errors[:issue_id].present?
224 end
225
212 226 def test_create_without_project_should_deny_without_permission
213 227 @request.session[:user_id] = 2
214 228 Project.find(3).disable_module!(:time_tracking)
215 229
216 230 assert_no_difference 'TimeEntry.count' do
217 231 post :create, :time_entry => {:project_id => '3',
218 232 :activity_id => '11',
219 233 :issue_id => '',
220 234 :spent_on => '2008-03-14',
221 235 :hours => '7.3'}
222 236 end
223 237
224 238 assert_response 403
225 239 end
226 240
227 241 def test_create_without_project_with_failure
228 242 @request.session[:user_id] = 2
229 243 assert_no_difference 'TimeEntry.count' do
230 244 post :create, :time_entry => {:project_id => '1',
231 245 :activity_id => '11',
232 246 :issue_id => '',
233 247 :spent_on => '2008-03-14',
234 248 :hours => ''}
235 249 end
236 250
237 251 assert_response :success
238 252 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
239 253 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
240 254 end
241 255
242 256 def test_update
243 257 entry = TimeEntry.find(1)
244 258 assert_equal 1, entry.issue_id
245 259 assert_equal 2, entry.user_id
246 260
247 261 @request.session[:user_id] = 1
248 262 put :update, :id => 1,
249 263 :time_entry => {:issue_id => '2',
250 264 :hours => '8'}
251 265 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
252 266 entry.reload
253 267
254 268 assert_equal 8, entry.hours
255 269 assert_equal 2, entry.issue_id
256 270 assert_equal 2, entry.user_id
257 271 end
258 272
259 273 def test_get_bulk_edit
260 274 @request.session[:user_id] = 2
261 275 get :bulk_edit, :ids => [1, 2]
262 276 assert_response :success
263 277 assert_template 'bulk_edit'
264 278
265 279 # System wide custom field
266 280 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
267 281 end
268 282
269 283 def test_get_bulk_edit_on_different_projects
270 284 @request.session[:user_id] = 2
271 285 get :bulk_edit, :ids => [1, 2, 6]
272 286 assert_response :success
273 287 assert_template 'bulk_edit'
274 288 end
275 289
276 290 def test_bulk_update
277 291 @request.session[:user_id] = 2
278 292 # update time entry activity
279 293 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
280 294
281 295 assert_response 302
282 296 # check that the issues were updated
283 297 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
284 298 end
285 299
286 300 def test_bulk_update_with_failure
287 301 @request.session[:user_id] = 2
288 302 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
289 303
290 304 assert_response 302
291 305 assert_match /Failed to save 2 time entrie/, flash[:error]
292 306 end
293 307
294 308 def test_bulk_update_on_different_projects
295 309 @request.session[:user_id] = 2
296 310 # makes user a manager on the other project
297 311 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
298 312
299 313 # update time entry activity
300 314 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
301 315
302 316 assert_response 302
303 317 # check that the issues were updated
304 318 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
305 319 end
306 320
307 321 def test_bulk_update_on_different_projects_without_rights
308 322 @request.session[:user_id] = 3
309 323 user = User.find(3)
310 324 action = { :controller => "timelog", :action => "bulk_update" }
311 325 assert user.allowed_to?(action, TimeEntry.find(1).project)
312 326 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
313 327 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
314 328 assert_response 403
315 329 end
316 330
317 331 def test_bulk_update_custom_field
318 332 @request.session[:user_id] = 2
319 333 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
320 334
321 335 assert_response 302
322 336 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
323 337 end
324 338
325 339 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
326 340 @request.session[:user_id] = 2
327 341 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
328 342
329 343 assert_response :redirect
330 344 assert_redirected_to '/time_entries'
331 345 end
332 346
333 347 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
334 348 @request.session[:user_id] = 2
335 349 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
336 350
337 351 assert_response :redirect
338 352 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
339 353 end
340 354
341 355 def test_post_bulk_update_without_edit_permission_should_be_denied
342 356 @request.session[:user_id] = 2
343 357 Role.find_by_name('Manager').remove_permission! :edit_time_entries
344 358 post :bulk_update, :ids => [1,2]
345 359
346 360 assert_response 403
347 361 end
348 362
349 363 def test_destroy
350 364 @request.session[:user_id] = 2
351 365 delete :destroy, :id => 1
352 366 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
353 367 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
354 368 assert_nil TimeEntry.find_by_id(1)
355 369 end
356 370
357 371 def test_destroy_should_fail
358 372 # simulate that this fails (e.g. due to a plugin), see #5700
359 373 TimeEntry.any_instance.expects(:destroy).returns(false)
360 374
361 375 @request.session[:user_id] = 2
362 376 delete :destroy, :id => 1
363 377 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
364 378 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
365 379 assert_not_nil TimeEntry.find_by_id(1)
366 380 end
367 381
368 382 def test_index_all_projects
369 383 get :index
370 384 assert_response :success
371 385 assert_template 'index'
372 386 assert_not_nil assigns(:total_hours)
373 387 assert_equal "162.90", "%.2f" % assigns(:total_hours)
374 388 assert_tag :form,
375 389 :attributes => {:action => "/time_entries", :id => 'query_form'}
376 390 end
377 391
378 392 def test_index_all_projects_should_show_log_time_link
379 393 @request.session[:user_id] = 2
380 394 get :index
381 395 assert_response :success
382 396 assert_template 'index'
383 397 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
384 398 end
385 399
386 400 def test_index_at_project_level
387 401 get :index, :project_id => 'ecookbook'
388 402 assert_response :success
389 403 assert_template 'index'
390 404 assert_not_nil assigns(:entries)
391 405 assert_equal 4, assigns(:entries).size
392 406 # project and subproject
393 407 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
394 408 assert_not_nil assigns(:total_hours)
395 409 assert_equal "162.90", "%.2f" % assigns(:total_hours)
396 410 # display all time by default
397 411 assert_nil assigns(:from)
398 412 assert_nil assigns(:to)
399 413 assert_tag :form,
400 414 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
401 415 end
402 416
403 417 def test_index_at_project_level_with_date_range
404 418 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
405 419 assert_response :success
406 420 assert_template 'index'
407 421 assert_not_nil assigns(:entries)
408 422 assert_equal 3, assigns(:entries).size
409 423 assert_not_nil assigns(:total_hours)
410 424 assert_equal "12.90", "%.2f" % assigns(:total_hours)
411 425 assert_equal '2007-03-20'.to_date, assigns(:from)
412 426 assert_equal '2007-04-30'.to_date, assigns(:to)
413 427 assert_tag :form,
414 428 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
415 429 end
416 430
417 431 def test_index_at_project_level_with_period
418 432 get :index, :project_id => 'ecookbook', :period => '7_days'
419 433 assert_response :success
420 434 assert_template 'index'
421 435 assert_not_nil assigns(:entries)
422 436 assert_not_nil assigns(:total_hours)
423 437 assert_equal Date.today - 7, assigns(:from)
424 438 assert_equal Date.today, assigns(:to)
425 439 assert_tag :form,
426 440 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
427 441 end
428 442
429 443 def test_index_one_day
430 444 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
431 445 assert_response :success
432 446 assert_template 'index'
433 447 assert_not_nil assigns(:total_hours)
434 448 assert_equal "4.25", "%.2f" % assigns(:total_hours)
435 449 assert_tag :form,
436 450 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
437 451 end
438 452
439 453 def test_index_today
440 454 Date.stubs(:today).returns('2011-12-15'.to_date)
441 455 get :index, :period => 'today'
442 456 assert_equal '2011-12-15'.to_date, assigns(:from)
443 457 assert_equal '2011-12-15'.to_date, assigns(:to)
444 458 end
445 459
446 460 def test_index_yesterday
447 461 Date.stubs(:today).returns('2011-12-15'.to_date)
448 462 get :index, :period => 'yesterday'
449 463 assert_equal '2011-12-14'.to_date, assigns(:from)
450 464 assert_equal '2011-12-14'.to_date, assigns(:to)
451 465 end
452 466
453 467 def test_index_current_week
454 468 Date.stubs(:today).returns('2011-12-15'.to_date)
455 469 get :index, :period => 'current_week'
456 470 assert_equal '2011-12-12'.to_date, assigns(:from)
457 471 assert_equal '2011-12-18'.to_date, assigns(:to)
458 472 end
459 473
460 474 def test_index_last_week
461 475 Date.stubs(:today).returns('2011-12-15'.to_date)
462 476 get :index, :period => 'current_week'
463 477 assert_equal '2011-12-05'.to_date, assigns(:from)
464 478 assert_equal '2011-12-11'.to_date, assigns(:to)
465 479 end
466 480
467 481 def test_index_last_week
468 482 Date.stubs(:today).returns('2011-12-15'.to_date)
469 483 get :index, :period => 'last_week'
470 484 assert_equal '2011-12-05'.to_date, assigns(:from)
471 485 assert_equal '2011-12-11'.to_date, assigns(:to)
472 486 end
473 487
474 488 def test_index_7_days
475 489 Date.stubs(:today).returns('2011-12-15'.to_date)
476 490 get :index, :period => '7_days'
477 491 assert_equal '2011-12-08'.to_date, assigns(:from)
478 492 assert_equal '2011-12-15'.to_date, assigns(:to)
479 493 end
480 494
481 495 def test_index_current_month
482 496 Date.stubs(:today).returns('2011-12-15'.to_date)
483 497 get :index, :period => 'current_month'
484 498 assert_equal '2011-12-01'.to_date, assigns(:from)
485 499 assert_equal '2011-12-31'.to_date, assigns(:to)
486 500 end
487 501
488 502 def test_index_last_month
489 503 Date.stubs(:today).returns('2011-12-15'.to_date)
490 504 get :index, :period => 'last_month'
491 505 assert_equal '2011-11-01'.to_date, assigns(:from)
492 506 assert_equal '2011-11-30'.to_date, assigns(:to)
493 507 end
494 508
495 509 def test_index_30_days
496 510 Date.stubs(:today).returns('2011-12-15'.to_date)
497 511 get :index, :period => '30_days'
498 512 assert_equal '2011-11-15'.to_date, assigns(:from)
499 513 assert_equal '2011-12-15'.to_date, assigns(:to)
500 514 end
501 515
502 516 def test_index_current_year
503 517 Date.stubs(:today).returns('2011-12-15'.to_date)
504 518 get :index, :period => 'current_year'
505 519 assert_equal '2011-01-01'.to_date, assigns(:from)
506 520 assert_equal '2011-12-31'.to_date, assigns(:to)
507 521 end
508 522
509 523 def test_index_at_issue_level
510 524 get :index, :issue_id => 1
511 525 assert_response :success
512 526 assert_template 'index'
513 527 assert_not_nil assigns(:entries)
514 528 assert_equal 2, assigns(:entries).size
515 529 assert_not_nil assigns(:total_hours)
516 530 assert_equal 154.25, assigns(:total_hours)
517 531 # display all time
518 532 assert_nil assigns(:from)
519 533 assert_nil assigns(:to)
520 534 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
521 535 # to use /issues/:issue_id/time_entries
522 536 assert_tag :form,
523 537 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
524 538 end
525 539
526 540 def test_index_atom_feed
527 541 get :index, :project_id => 1, :format => 'atom'
528 542 assert_response :success
529 543 assert_equal 'application/atom+xml', @response.content_type
530 544 assert_not_nil assigns(:items)
531 545 assert assigns(:items).first.is_a?(TimeEntry)
532 546 end
533 547
534 548 def test_index_all_projects_csv_export
535 549 Setting.date_format = '%m/%d/%Y'
536 550 get :index, :format => 'csv'
537 551 assert_response :success
538 552 assert_equal 'text/csv', @response.content_type
539 553 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
540 554 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
541 555 end
542 556
543 557 def test_index_csv_export
544 558 Setting.date_format = '%m/%d/%Y'
545 559 get :index, :project_id => 1, :format => 'csv'
546 560 assert_response :success
547 561 assert_equal 'text/csv', @response.content_type
548 562 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
549 563 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
550 564 end
551 565
552 566 def test_csv_big_5
553 567 user = User.find_by_id(3)
554 568 user.language = "zh-TW"
555 569 assert user.save
556 570 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
557 571 str_big5 = "\xa4@\xa4\xeb"
558 572 if str_utf8.respond_to?(:force_encoding)
559 573 str_utf8.force_encoding('UTF-8')
560 574 str_big5.force_encoding('Big5')
561 575 end
562 576 @request.session[:user_id] = 3
563 577 post :create, :project_id => 1,
564 578 :time_entry => {:comments => str_utf8,
565 579 # Not the default activity
566 580 :activity_id => '11',
567 581 :issue_id => '',
568 582 :spent_on => '2011-11-10',
569 583 :hours => '7.3'}
570 584 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
571 585
572 586 t = TimeEntry.find_by_comments(str_utf8)
573 587 assert_not_nil t
574 588 assert_equal 11, t.activity_id
575 589 assert_equal 7.3, t.hours
576 590 assert_equal 3, t.user_id
577 591
578 592 get :index, :project_id => 1, :format => 'csv',
579 593 :from => '2011-11-10', :to => '2011-11-10'
580 594 assert_response :success
581 595 assert_equal 'text/csv', @response.content_type
582 596 ar = @response.body.chomp.split("\n")
583 597 s1 = "\xa4\xe9\xb4\xc1"
584 598 if str_utf8.respond_to?(:force_encoding)
585 599 s1.force_encoding('Big5')
586 600 end
587 601 assert ar[0].include?(s1)
588 602 assert ar[1].include?(str_big5)
589 603 end
590 604
591 605 def test_csv_cannot_convert_should_be_replaced_big_5
592 606 user = User.find_by_id(3)
593 607 user.language = "zh-TW"
594 608 assert user.save
595 609 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
596 610 if str_utf8.respond_to?(:force_encoding)
597 611 str_utf8.force_encoding('UTF-8')
598 612 end
599 613 @request.session[:user_id] = 3
600 614 post :create, :project_id => 1,
601 615 :time_entry => {:comments => str_utf8,
602 616 # Not the default activity
603 617 :activity_id => '11',
604 618 :issue_id => '',
605 619 :spent_on => '2011-11-10',
606 620 :hours => '7.3'}
607 621 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
608 622
609 623 t = TimeEntry.find_by_comments(str_utf8)
610 624 assert_not_nil t
611 625 assert_equal 11, t.activity_id
612 626 assert_equal 7.3, t.hours
613 627 assert_equal 3, t.user_id
614 628
615 629 get :index, :project_id => 1, :format => 'csv',
616 630 :from => '2011-11-10', :to => '2011-11-10'
617 631 assert_response :success
618 632 assert_equal 'text/csv', @response.content_type
619 633 ar = @response.body.chomp.split("\n")
620 634 s1 = "\xa4\xe9\xb4\xc1"
621 635 if str_utf8.respond_to?(:force_encoding)
622 636 s1.force_encoding('Big5')
623 637 end
624 638 assert ar[0].include?(s1)
625 639 s2 = ar[1].split(",")[8]
626 640 if s2.respond_to?(:force_encoding)
627 641 s3 = "\xa5H?"
628 642 s3.force_encoding('Big5')
629 643 assert_equal s3, s2
630 644 elsif RUBY_PLATFORM == 'java'
631 645 assert_equal "??", s2
632 646 else
633 647 assert_equal "\xa5H???", s2
634 648 end
635 649 end
636 650
637 651 def test_csv_tw
638 652 with_settings :default_language => "zh-TW" do
639 653 str1 = "test_csv_tw"
640 654 user = User.find_by_id(3)
641 655 te1 = TimeEntry.create(:spent_on => '2011-11-10',
642 656 :hours => 999.9,
643 657 :project => Project.find(1),
644 658 :user => user,
645 659 :activity => TimeEntryActivity.find_by_name('Design'),
646 660 :comments => str1)
647 661 te2 = TimeEntry.find_by_comments(str1)
648 662 assert_not_nil te2
649 663 assert_equal 999.9, te2.hours
650 664 assert_equal 3, te2.user_id
651 665
652 666 get :index, :project_id => 1, :format => 'csv',
653 667 :from => '2011-11-10', :to => '2011-11-10'
654 668 assert_response :success
655 669 assert_equal 'text/csv', @response.content_type
656 670
657 671 ar = @response.body.chomp.split("\n")
658 672 s2 = ar[1].split(",")[7]
659 673 assert_equal '999.9', s2
660 674
661 675 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
662 676 if str_tw.respond_to?(:force_encoding)
663 677 str_tw.force_encoding('UTF-8')
664 678 end
665 679 assert_equal str_tw, l(:general_lang_name)
666 680 assert_equal ',', l(:general_csv_separator)
667 681 assert_equal '.', l(:general_csv_decimal_separator)
668 682 end
669 683 end
670 684
671 685 def test_csv_fr
672 686 with_settings :default_language => "fr" do
673 687 str1 = "test_csv_fr"
674 688 user = User.find_by_id(3)
675 689 te1 = TimeEntry.create(:spent_on => '2011-11-10',
676 690 :hours => 999.9,
677 691 :project => Project.find(1),
678 692 :user => user,
679 693 :activity => TimeEntryActivity.find_by_name('Design'),
680 694 :comments => str1)
681 695 te2 = TimeEntry.find_by_comments(str1)
682 696 assert_not_nil te2
683 697 assert_equal 999.9, te2.hours
684 698 assert_equal 3, te2.user_id
685 699
686 700 get :index, :project_id => 1, :format => 'csv',
687 701 :from => '2011-11-10', :to => '2011-11-10'
688 702 assert_response :success
689 703 assert_equal 'text/csv', @response.content_type
690 704
691 705 ar = @response.body.chomp.split("\n")
692 706 s2 = ar[1].split(";")[7]
693 707 assert_equal '999,9', s2
694 708
695 709 str_fr = "Fran\xc3\xa7ais"
696 710 if str_fr.respond_to?(:force_encoding)
697 711 str_fr.force_encoding('UTF-8')
698 712 end
699 713 assert_equal str_fr, l(:general_lang_name)
700 714 assert_equal ';', l(:general_csv_separator)
701 715 assert_equal ',', l(:general_csv_decimal_separator)
702 716 end
703 717 end
704 718 end
General Comments 0
You need to be logged in to leave comments. Login now