##// END OF EJS Templates
Merged r13562 (#18269)....
Jean-Philippe Lang -
r13248:2d612f32b7c0
parent child
Show More
@@ -1,202 +1,206
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2014 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 module QueriesHelper
21 21 include ApplicationHelper
22 22
23 23 def filters_options_for_select(query)
24 24 options_for_select(filters_options(query))
25 25 end
26 26
27 27 def filters_options(query)
28 28 options = [[]]
29 29 options += query.available_filters.map do |field, field_options|
30 30 [field_options[:name], field]
31 31 end
32 32 end
33 33
34 34 def query_filters_hidden_tags(query)
35 35 tags = ''.html_safe
36 36 query.filters.each do |field, options|
37 37 tags << hidden_field_tag("f[]", field, :id => nil)
38 38 tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil)
39 39 options[:values].each do |value|
40 40 tags << hidden_field_tag("v[#{field}][]", value, :id => nil)
41 41 end
42 42 end
43 43 tags
44 44 end
45 45
46 46 def query_columns_hidden_tags(query)
47 47 tags = ''.html_safe
48 48 query.columns.each do |column|
49 49 tags << hidden_field_tag("c[]", column.name, :id => nil)
50 50 end
51 51 tags
52 52 end
53 53
54 54 def query_hidden_tags(query)
55 55 query_filters_hidden_tags(query) + query_columns_hidden_tags(query)
56 56 end
57 57
58 58 def available_block_columns_tags(query)
59 59 tags = ''.html_safe
60 60 query.available_block_columns.each do |column|
61 61 tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline')
62 62 end
63 63 tags
64 64 end
65 65
66 66 def query_available_inline_columns_options(query)
67 67 (query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
68 68 end
69 69
70 70 def query_selected_inline_columns_options(query)
71 71 (query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
72 72 end
73 73
74 74 def render_query_columns_selection(query, options={})
75 75 tag_name = (options[:name] || 'c') + '[]'
76 76 render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
77 77 end
78 78
79 79 def column_header(column)
80 80 column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
81 81 :default_order => column.default_order) :
82 82 content_tag('th', h(column.caption))
83 83 end
84 84
85 85 def column_content(column, issue)
86 86 value = column.value_object(issue)
87 87 if value.is_a?(Array)
88 88 value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
89 89 else
90 90 column_value(column, issue, value)
91 91 end
92 92 end
93 93
94 94 def column_value(column, issue, value)
95 95 case column.name
96 96 when :id
97 97 link_to value, issue_path(issue)
98 98 when :subject
99 99 link_to value, issue_path(issue)
100 100 when :parent
101 101 value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
102 102 when :description
103 103 issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
104 104 when :done_ratio
105 105 progress_bar(value, :width => '80px')
106 106 when :relations
107 107 other = value.other_issue(issue)
108 108 content_tag('span',
109 109 (l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
110 110 :class => value.css_classes_for(issue))
111 111 else
112 112 format_object(value)
113 113 end
114 114 end
115 115
116 116 def csv_content(column, issue)
117 117 value = column.value_object(issue)
118 118 if value.is_a?(Array)
119 119 value.collect {|v| csv_value(column, issue, v)}.compact.join(', ')
120 120 else
121 121 csv_value(column, issue, value)
122 122 end
123 123 end
124 124
125 def csv_value(column, issue, value)
125 def csv_value(column, object, value)
126 126 format_object(value, false) do |value|
127 127 case value.class.name
128 128 when 'Float'
129 129 sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator))
130 130 when 'IssueRelation'
131 other = value.other_issue(issue)
132 l(value.label_for(issue)) + " ##{other.id}"
131 other = value.other_issue(object)
132 l(value.label_for(object)) + " ##{other.id}"
133 133 when 'Issue'
134 value.id
134 if object.is_a?(TimeEntry)
135 "#{value.tracker} ##{value.id}: #{value.subject}"
136 else
137 value.id
138 end
135 139 else
136 140 value
137 141 end
138 142 end
139 143 end
140 144
141 145 def query_to_csv(items, query, options={})
142 146 encoding = l(:general_csv_encoding)
143 147 columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns)
144 148 query.available_block_columns.each do |column|
145 149 if options[column.name].present?
146 150 columns << column
147 151 end
148 152 end
149 153
150 154 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
151 155 # csv header fields
152 156 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
153 157 # csv lines
154 158 items.each do |item|
155 159 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, item), encoding) }
156 160 end
157 161 end
158 162 export
159 163 end
160 164
161 165 # Retrieve query from session or build a new query
162 166 def retrieve_query
163 167 if !params[:query_id].blank?
164 168 cond = "project_id IS NULL"
165 169 cond << " OR project_id = #{@project.id}" if @project
166 170 @query = IssueQuery.where(cond).find(params[:query_id])
167 171 raise ::Unauthorized unless @query.visible?
168 172 @query.project = @project
169 173 session[:query] = {:id => @query.id, :project_id => @query.project_id}
170 174 sort_clear
171 175 elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
172 176 # Give it a name, required to be valid
173 177 @query = IssueQuery.new(:name => "_")
174 178 @query.project = @project
175 179 @query.build_from_params(params)
176 180 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
177 181 else
178 182 # retrieve from session
179 183 @query = nil
180 184 @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
181 185 @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
182 186 @query.project = @project
183 187 end
184 188 end
185 189
186 190 def retrieve_query_from_session
187 191 if session[:query]
188 192 if session[:query][:id]
189 193 @query = IssueQuery.find_by_id(session[:query][:id])
190 194 return unless @query
191 195 else
192 196 @query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
193 197 end
194 198 if session[:query].has_key?(:project_id)
195 199 @query.project_id = session[:query][:project_id]
196 200 else
197 201 @query.project = @project
198 202 end
199 203 @query
200 204 end
201 205 end
202 206 end
@@ -1,717 +1,727
1 1 # -*- coding: utf-8 -*-
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2014 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_no_tag 'option', :content => 'Inactive Activity'
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 # Default activity selected
104 104 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
105 105 end
106 106
107 107 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
108 108 te = TimeEntry.find(1)
109 109 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
110 110 te.save!
111 111
112 112 @request.session[:user_id] = 1
113 113 get :edit, :project_id => 1, :id => 1
114 114 assert_response :success
115 115 assert_template 'edit'
116 116 # Blank option since nothing is pre-selected
117 117 assert_tag :tag => 'option', :content => '--- Please select ---'
118 118 end
119 119
120 120 def test_post_create
121 121 @request.session[:user_id] = 3
122 122 assert_difference 'TimeEntry.count' do
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 :spent_on => '2008-03-14',
128 128 :issue_id => '1',
129 129 :hours => '7.3'}
130 130 assert_redirected_to '/projects/ecookbook/time_entries'
131 131 end
132 132
133 133 t = TimeEntry.order('id DESC').first
134 134 assert_not_nil t
135 135 assert_equal 'Some work on TimelogControllerTest', t.comments
136 136 assert_equal 1, t.project_id
137 137 assert_equal 1, t.issue_id
138 138 assert_equal 11, t.activity_id
139 139 assert_equal 7.3, t.hours
140 140 assert_equal 3, t.user_id
141 141 end
142 142
143 143 def test_post_create_with_blank_issue
144 144 @request.session[:user_id] = 3
145 145 assert_difference 'TimeEntry.count' do
146 146 post :create, :project_id => 1,
147 147 :time_entry => {:comments => 'Some work on TimelogControllerTest',
148 148 # Not the default activity
149 149 :activity_id => '11',
150 150 :issue_id => '',
151 151 :spent_on => '2008-03-14',
152 152 :hours => '7.3'}
153 153 assert_redirected_to '/projects/ecookbook/time_entries'
154 154 end
155 155
156 156 t = TimeEntry.order('id DESC').first
157 157 assert_not_nil t
158 158 assert_equal 'Some work on TimelogControllerTest', t.comments
159 159 assert_equal 1, t.project_id
160 160 assert_nil t.issue_id
161 161 assert_equal 11, t.activity_id
162 162 assert_equal 7.3, t.hours
163 163 assert_equal 3, t.user_id
164 164 end
165 165
166 166 def test_create_and_continue_at_project_level
167 167 @request.session[:user_id] = 2
168 168 assert_difference 'TimeEntry.count' do
169 169 post :create, :time_entry => {:project_id => '1',
170 170 :activity_id => '11',
171 171 :issue_id => '',
172 172 :spent_on => '2008-03-14',
173 173 :hours => '7.3'},
174 174 :continue => '1'
175 175 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
176 176 end
177 177 end
178 178
179 179 def test_create_and_continue_at_issue_level
180 180 @request.session[:user_id] = 2
181 181 assert_difference 'TimeEntry.count' do
182 182 post :create, :time_entry => {:project_id => '',
183 183 :activity_id => '11',
184 184 :issue_id => '1',
185 185 :spent_on => '2008-03-14',
186 186 :hours => '7.3'},
187 187 :continue => '1'
188 188 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
189 189 end
190 190 end
191 191
192 192 def test_create_and_continue_with_project_id
193 193 @request.session[:user_id] = 2
194 194 assert_difference 'TimeEntry.count' do
195 195 post :create, :project_id => 1,
196 196 :time_entry => {:activity_id => '11',
197 197 :issue_id => '',
198 198 :spent_on => '2008-03-14',
199 199 :hours => '7.3'},
200 200 :continue => '1'
201 201 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
202 202 end
203 203 end
204 204
205 205 def test_create_and_continue_with_issue_id
206 206 @request.session[:user_id] = 2
207 207 assert_difference 'TimeEntry.count' do
208 208 post :create, :issue_id => 1,
209 209 :time_entry => {:activity_id => '11',
210 210 :issue_id => '1',
211 211 :spent_on => '2008-03-14',
212 212 :hours => '7.3'},
213 213 :continue => '1'
214 214 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='
215 215 end
216 216 end
217 217
218 218 def test_create_without_log_time_permission_should_be_denied
219 219 @request.session[:user_id] = 2
220 220 Role.find_by_name('Manager').remove_permission! :log_time
221 221 post :create, :project_id => 1,
222 222 :time_entry => {:activity_id => '11',
223 223 :issue_id => '',
224 224 :spent_on => '2008-03-14',
225 225 :hours => '7.3'}
226 226
227 227 assert_response 403
228 228 end
229 229
230 230 def test_create_without_project_and_issue_should_fail
231 231 @request.session[:user_id] = 2
232 232 post :create, :time_entry => {:issue_id => ''}
233 233
234 234 assert_response :success
235 235 assert_template 'new'
236 236 end
237 237
238 238 def test_create_with_failure
239 239 @request.session[:user_id] = 2
240 240 post :create, :project_id => 1,
241 241 :time_entry => {:activity_id => '',
242 242 :issue_id => '',
243 243 :spent_on => '2008-03-14',
244 244 :hours => '7.3'}
245 245
246 246 assert_response :success
247 247 assert_template 'new'
248 248 end
249 249
250 250 def test_create_without_project
251 251 @request.session[:user_id] = 2
252 252 assert_difference 'TimeEntry.count' do
253 253 post :create, :time_entry => {:project_id => '1',
254 254 :activity_id => '11',
255 255 :issue_id => '',
256 256 :spent_on => '2008-03-14',
257 257 :hours => '7.3'}
258 258 end
259 259
260 260 assert_redirected_to '/projects/ecookbook/time_entries'
261 261 time_entry = TimeEntry.order('id DESC').first
262 262 assert_equal 1, time_entry.project_id
263 263 end
264 264
265 265 def test_create_without_project_should_fail_with_issue_not_inside_project
266 266 @request.session[:user_id] = 2
267 267 assert_no_difference 'TimeEntry.count' do
268 268 post :create, :time_entry => {:project_id => '1',
269 269 :activity_id => '11',
270 270 :issue_id => '5',
271 271 :spent_on => '2008-03-14',
272 272 :hours => '7.3'}
273 273 end
274 274
275 275 assert_response :success
276 276 assert assigns(:time_entry).errors[:issue_id].present?
277 277 end
278 278
279 279 def test_create_without_project_should_deny_without_permission
280 280 @request.session[:user_id] = 2
281 281 Project.find(3).disable_module!(:time_tracking)
282 282
283 283 assert_no_difference 'TimeEntry.count' do
284 284 post :create, :time_entry => {:project_id => '3',
285 285 :activity_id => '11',
286 286 :issue_id => '',
287 287 :spent_on => '2008-03-14',
288 288 :hours => '7.3'}
289 289 end
290 290
291 291 assert_response 403
292 292 end
293 293
294 294 def test_create_without_project_with_failure
295 295 @request.session[:user_id] = 2
296 296 assert_no_difference 'TimeEntry.count' do
297 297 post :create, :time_entry => {:project_id => '1',
298 298 :activity_id => '11',
299 299 :issue_id => '',
300 300 :spent_on => '2008-03-14',
301 301 :hours => ''}
302 302 end
303 303
304 304 assert_response :success
305 305 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
306 306 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
307 307 end
308 308
309 309 def test_update
310 310 entry = TimeEntry.find(1)
311 311 assert_equal 1, entry.issue_id
312 312 assert_equal 2, entry.user_id
313 313
314 314 @request.session[:user_id] = 1
315 315 put :update, :id => 1,
316 316 :time_entry => {:issue_id => '2',
317 317 :hours => '8'}
318 318 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
319 319 entry.reload
320 320
321 321 assert_equal 8, entry.hours
322 322 assert_equal 2, entry.issue_id
323 323 assert_equal 2, entry.user_id
324 324 end
325 325
326 326 def test_update_should_allow_to_change_issue_to_another_project
327 327 entry = TimeEntry.generate!(:issue_id => 1)
328 328
329 329 @request.session[:user_id] = 1
330 330 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
331 331 assert_response 302
332 332 entry.reload
333 333
334 334 assert_equal 5, entry.issue_id
335 335 assert_equal 3, entry.project_id
336 336 end
337 337
338 338 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
339 339 entry = TimeEntry.generate!(:issue_id => 1)
340 340 Project.find(3).disable_module!(:time_tracking)
341 341
342 342 @request.session[:user_id] = 1
343 343 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
344 344 assert_response 200
345 345 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
346 346 end
347 347
348 348 def test_get_bulk_edit
349 349 @request.session[:user_id] = 2
350 350 get :bulk_edit, :ids => [1, 2]
351 351 assert_response :success
352 352 assert_template 'bulk_edit'
353 353
354 354 assert_select 'ul#bulk-selection' do
355 355 assert_select 'li', 2
356 356 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
357 357 end
358 358
359 359 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
360 360 # System wide custom field
361 361 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
362 362
363 363 # Activities
364 364 assert_select 'select[name=?]', 'time_entry[activity_id]' do
365 365 assert_select 'option[value=]', :text => '(No change)'
366 366 assert_select 'option[value=9]', :text => 'Design'
367 367 end
368 368 end
369 369 end
370 370
371 371 def test_get_bulk_edit_on_different_projects
372 372 @request.session[:user_id] = 2
373 373 get :bulk_edit, :ids => [1, 2, 6]
374 374 assert_response :success
375 375 assert_template 'bulk_edit'
376 376 end
377 377
378 378 def test_bulk_update
379 379 @request.session[:user_id] = 2
380 380 # update time entry activity
381 381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382 382
383 383 assert_response 302
384 384 # check that the issues were updated
385 385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 386 end
387 387
388 388 def test_bulk_update_with_failure
389 389 @request.session[:user_id] = 2
390 390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391 391
392 392 assert_response 302
393 393 assert_match /Failed to save 2 time entrie/, flash[:error]
394 394 end
395 395
396 396 def test_bulk_update_on_different_projects
397 397 @request.session[:user_id] = 2
398 398 # makes user a manager on the other project
399 399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400 400
401 401 # update time entry activity
402 402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403 403
404 404 assert_response 302
405 405 # check that the issues were updated
406 406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 407 end
408 408
409 409 def test_bulk_update_on_different_projects_without_rights
410 410 @request.session[:user_id] = 3
411 411 user = User.find(3)
412 412 action = { :controller => "timelog", :action => "bulk_update" }
413 413 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 416 assert_response 403
417 417 end
418 418
419 419 def test_bulk_update_custom_field
420 420 @request.session[:user_id] = 2
421 421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422 422
423 423 assert_response 302
424 424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 425 end
426 426
427 427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 428 @request.session[:user_id] = 2
429 429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430 430
431 431 assert_response :redirect
432 432 assert_redirected_to '/time_entries'
433 433 end
434 434
435 435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 436 @request.session[:user_id] = 2
437 437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438 438
439 439 assert_response :redirect
440 440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 441 end
442 442
443 443 def test_post_bulk_update_without_edit_permission_should_be_denied
444 444 @request.session[:user_id] = 2
445 445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 446 post :bulk_update, :ids => [1,2]
447 447
448 448 assert_response 403
449 449 end
450 450
451 451 def test_destroy
452 452 @request.session[:user_id] = 2
453 453 delete :destroy, :id => 1
454 454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 456 assert_nil TimeEntry.find_by_id(1)
457 457 end
458 458
459 459 def test_destroy_should_fail
460 460 # simulate that this fails (e.g. due to a plugin), see #5700
461 461 TimeEntry.any_instance.expects(:destroy).returns(false)
462 462
463 463 @request.session[:user_id] = 2
464 464 delete :destroy, :id => 1
465 465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 467 assert_not_nil TimeEntry.find_by_id(1)
468 468 end
469 469
470 470 def test_index_all_projects
471 471 get :index
472 472 assert_response :success
473 473 assert_template 'index'
474 474 assert_not_nil assigns(:total_hours)
475 475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 476 assert_tag :form,
477 477 :attributes => {:action => "/time_entries", :id => 'query_form'}
478 478 end
479 479
480 480 def test_index_all_projects_should_show_log_time_link
481 481 @request.session[:user_id] = 2
482 482 get :index
483 483 assert_response :success
484 484 assert_template 'index'
485 485 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
486 486 end
487 487
488 488 def test_index_my_spent_time
489 489 @request.session[:user_id] = 2
490 490 get :index, :user_id => 'me'
491 491 assert_response :success
492 492 assert_template 'index'
493 493 assert assigns(:entries).all? {|entry| entry.user_id == 2}
494 494 end
495 495
496 496 def test_index_at_project_level
497 497 get :index, :project_id => 'ecookbook'
498 498 assert_response :success
499 499 assert_template 'index'
500 500 assert_not_nil assigns(:entries)
501 501 assert_equal 4, assigns(:entries).size
502 502 # project and subproject
503 503 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
504 504 assert_not_nil assigns(:total_hours)
505 505 assert_equal "162.90", "%.2f" % assigns(:total_hours)
506 506 assert_tag :form,
507 507 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
508 508 end
509 509
510 510 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
511 511 entry = TimeEntry.generate!(:project => Project.find(3))
512 512
513 513 with_settings :display_subprojects_issues => '0' do
514 514 get :index, :project_id => 'ecookbook'
515 515 assert_response :success
516 516 assert_template 'index'
517 517 assert_not_include entry, assigns(:entries)
518 518 end
519 519 end
520 520
521 521 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
522 522 entry = TimeEntry.generate!(:project => Project.find(3))
523 523
524 524 with_settings :display_subprojects_issues => '0' do
525 525 get :index, :project_id => 'ecookbook', :subproject_id => 3
526 526 assert_response :success
527 527 assert_template 'index'
528 528 assert_include entry, assigns(:entries)
529 529 end
530 530 end
531 531
532 532 def test_index_at_project_level_with_date_range
533 533 get :index, :project_id => 'ecookbook',
534 534 :f => ['spent_on'],
535 535 :op => {'spent_on' => '><'},
536 536 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
537 537 assert_response :success
538 538 assert_template 'index'
539 539 assert_not_nil assigns(:entries)
540 540 assert_equal 3, assigns(:entries).size
541 541 assert_not_nil assigns(:total_hours)
542 542 assert_equal "12.90", "%.2f" % assigns(:total_hours)
543 543 assert_tag :form,
544 544 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
545 545 end
546 546
547 547 def test_index_at_project_level_with_date_range_using_from_and_to_params
548 548 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
549 549 assert_response :success
550 550 assert_template 'index'
551 551 assert_not_nil assigns(:entries)
552 552 assert_equal 3, assigns(:entries).size
553 553 assert_not_nil assigns(:total_hours)
554 554 assert_equal "12.90", "%.2f" % assigns(:total_hours)
555 555 assert_tag :form,
556 556 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
557 557 end
558 558
559 559 def test_index_at_project_level_with_period
560 560 get :index, :project_id => 'ecookbook',
561 561 :f => ['spent_on'],
562 562 :op => {'spent_on' => '>t-'},
563 563 :v => {'spent_on' => ['7']}
564 564 assert_response :success
565 565 assert_template 'index'
566 566 assert_not_nil assigns(:entries)
567 567 assert_not_nil assigns(:total_hours)
568 568 assert_tag :form,
569 569 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
570 570 end
571 571
572 572 def test_index_at_issue_level
573 573 get :index, :issue_id => 1
574 574 assert_response :success
575 575 assert_template 'index'
576 576 assert_not_nil assigns(:entries)
577 577 assert_equal 2, assigns(:entries).size
578 578 assert_not_nil assigns(:total_hours)
579 579 assert_equal 154.25, assigns(:total_hours)
580 580 # display all time
581 581 assert_nil assigns(:from)
582 582 assert_nil assigns(:to)
583 583 assert_tag :form,
584 584 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
585 585 end
586 586
587 587 def test_index_should_sort_by_spent_on_and_created_on
588 588 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)
589 589 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)
590 590 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)
591 591
592 592 get :index, :project_id => 1,
593 593 :f => ['spent_on'],
594 594 :op => {'spent_on' => '><'},
595 595 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
596 596 assert_response :success
597 597 assert_equal [t2, t1, t3], assigns(:entries)
598 598
599 599 get :index, :project_id => 1,
600 600 :f => ['spent_on'],
601 601 :op => {'spent_on' => '><'},
602 602 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
603 603 :sort => 'spent_on'
604 604 assert_response :success
605 605 assert_equal [t3, t1, t2], assigns(:entries)
606 606 end
607 607
608 608 def test_index_with_filter_on_issue_custom_field
609 609 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
610 610 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
611 611
612 612 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
613 613 assert_response :success
614 614 assert_equal [entry], assigns(:entries)
615 615 end
616 616
617 617 def test_index_with_issue_custom_field_column
618 618 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
619 619 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
620 620
621 621 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
622 622 assert_response :success
623 623 assert_include :'issue.cf_2', assigns(:query).column_names
624 624 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
625 625 end
626 626
627 627 def test_index_with_time_entry_custom_field_column
628 628 field = TimeEntryCustomField.generate!(:field_format => 'string')
629 629 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
630 630 field_name = "cf_#{field.id}"
631 631
632 632 get :index, :c => ["hours", field_name]
633 633 assert_response :success
634 634 assert_include field_name.to_sym, assigns(:query).column_names
635 635 assert_select "td.#{field_name}", :text => 'CF Value'
636 636 end
637 637
638 638 def test_index_with_time_entry_custom_field_sorting
639 639 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
640 640 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
641 641 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
642 642 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
643 643 field_name = "cf_#{field.id}"
644 644
645 645 get :index, :c => ["hours", field_name], :sort => field_name
646 646 assert_response :success
647 647 assert_include field_name.to_sym, assigns(:query).column_names
648 648 assert_select "th a.sort", :text => 'String Field'
649 649
650 650 # Make sure that values are properly sorted
651 651 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
652 652 assert_equal 3, values.size
653 653 assert_equal values.sort, values
654 654 end
655 655
656 656 def test_index_atom_feed
657 657 get :index, :project_id => 1, :format => 'atom'
658 658 assert_response :success
659 659 assert_equal 'application/atom+xml', @response.content_type
660 660 assert_not_nil assigns(:items)
661 661 assert assigns(:items).first.is_a?(TimeEntry)
662 662 end
663 663
664 664 def test_index_at_project_level_should_include_csv_export_dialog
665 665 get :index, :project_id => 'ecookbook',
666 666 :f => ['spent_on'],
667 667 :op => {'spent_on' => '>='},
668 668 :v => {'spent_on' => ['2007-04-01']},
669 669 :c => ['spent_on', 'user']
670 670 assert_response :success
671 671
672 672 assert_select '#csv-export-options' do
673 673 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
674 674 # filter
675 675 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
676 676 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
677 677 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
678 678 # columns
679 679 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
680 680 assert_select 'input[name=?][value=?]', 'c[]', 'user'
681 681 assert_select 'input[name=?]', 'c[]', 2
682 682 end
683 683 end
684 684 end
685 685
686 686 def test_index_cross_project_should_include_csv_export_dialog
687 687 get :index
688 688 assert_response :success
689 689
690 690 assert_select '#csv-export-options' do
691 691 assert_select 'form[action=?][method=get]', '/time_entries.csv'
692 692 end
693 693 end
694 694
695 695 def test_index_at_issue_level_should_include_csv_export_dialog
696 696 get :index, :issue_id => 3
697 697 assert_response :success
698 698
699 699 assert_select '#csv-export-options' do
700 700 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
701 701 end
702 702 end
703 703
704 704 def test_index_csv_all_projects
705 705 Setting.date_format = '%m/%d/%Y'
706 706 get :index, :format => 'csv'
707 707 assert_response :success
708 708 assert_equal 'text/csv; header=present', response.content_type
709 709 end
710 710
711 711 def test_index_csv
712 712 Setting.date_format = '%m/%d/%Y'
713 713 get :index, :project_id => 1, :format => 'csv'
714 714 assert_response :success
715 715 assert_equal 'text/csv; header=present', response.content_type
716 716 end
717
718 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
719 issue = Issue.find(1)
720 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
721
722 get :index, :format => 'csv'
723 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
724 assert_not_nil line
725 assert_include "#{issue.tracker} #1: #{issue.subject}", line
726 end
717 727 end
General Comments 0
You need to be logged in to leave comments. Login now