##// END OF EJS Templates
Issue subject is not updated when you select another issue on time entry form (#24041)....
Jean-Philippe Lang -
r15569:2544fe6e15d4
parent child
Show More
@@ -0,0 +1,1
1 $('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>');
@@ -1,49 +1,47
1 1 <%= error_messages_for 'time_entry' %>
2 2 <%= back_url_hidden_field_tag %>
3 3
4 4 <div class="box tabular">
5 5 <% if @time_entry.new_record? %>
6 6 <% if params[:project_id] %>
7 7 <%= hidden_field_tag 'project_id', params[:project_id] %>
8 8 <% elsif params[:issue_id] %>
9 9 <%= hidden_field_tag 'issue_id', params[:issue_id] %>
10 10 <% else %>
11 11 <p><%= f.select :project_id, project_tree_options_for_select(Project.allowed_to(:log_time).to_a, :selected => @time_entry.project, :include_blank => true), :required => true %></p>
12 12 <% end %>
13 13 <% end %>
14 14 <p>
15 15 <%= f.text_field :issue_id, :size => 6 %>
16 <% if @time_entry.issue.try(:visible?) %>
17 <span id="time_entry_issue"><%= "#{@time_entry.issue.tracker.name} ##{@time_entry.issue.id}: #{@time_entry.issue.subject}" %></span>
18 <% end %>
16 <span id="time_entry_issue">
17 <%= link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>
18 </span>
19 19 </p>
20 20 <p><%= f.date_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
21 21 <p><%= f.text_field :hours, :size => 6, :required => true %></p>
22 22 <p><%= f.text_field :comments, :size => 100, :maxlength => 1024 %></p>
23 23 <p><%= f.select :activity_id, activity_collection_for_select_options(@time_entry), :required => true %></p>
24 24 <% @time_entry.custom_field_values.each do |value| %>
25 25 <p><%= custom_field_tag_with_label :time_entry, value %></p>
26 26 <% end %>
27 27 <%= call_hook(:view_timelog_edit_form_bottom, { :time_entry => @time_entry, :form => f }) %>
28 28 </div>
29 29
30 30 <%= javascript_tag do %>
31 <% if @time_entry.new_record? %>
32 31 $(document).ready(function(){
33 32 $('#time_entry_project_id, #time_entry_issue_id').change(function(){
34 33 $.ajax({
35 url: '<%= escape_javascript new_time_entry_path(:format => 'js') %>',
34 url: '<%= escape_javascript(@time_entry.new_record? ? new_time_entry_path(:format => 'js') : edit_time_entry_path(:format => 'js')) %>',
36 35 type: 'post',
37 data: $('#new_time_entry').serialize()
36 data: $(this).closest('form').serialize()
38 37 });
39 38 });
40 39 });
41 <% end %>
42 40
43 41 observeAutocompleteField('time_entry_issue_id', '<%= escape_javascript auto_complete_issues_path(:project_id => @project, :scope => (@project ? nil : 'all'))%>', {
44 42 select: function(event, ui) {
45 $('#time_entry_issue').text(ui.item.label);
46 $('#time_entry_issue_id').blur();
43 $('#time_entry_issue').text('');
44 $('#time_entry_issue_id').val(ui.item.value).change();
47 45 }
48 46 });
49 47 <% end %>
@@ -1,1 +1,2
1 1 $('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), @time_entry.activity_id) %>');
2 $('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>');
@@ -1,384 +1,388
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 Rails.application.routes.draw do
19 19 root :to => 'welcome#index', :as => 'home'
20 20
21 21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
22 22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
23 23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
24 24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
25 25 match 'account/activate', :to => 'account#activate', :via => :get
26 26 get 'account/activation_email', :to => 'account#activation_email', :as => 'activation_email'
27 27
28 28 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put, :patch]
29 29 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put, :patch]
30 30 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put, :patch]
31 31 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put, :patch]
32 32
33 33 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
34 34 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
35 35
36 36 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
37 37 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
38 38 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
39 39 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
40 40
41 41 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
42 42 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
43 43 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
44 44 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
45 45
46 46 # Misc issue routes. TODO: move into resources
47 47 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
48 48 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
49 49 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
50 50 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
51 51
52 52 resources :journals, :only => [:edit, :update] do
53 53 member do
54 54 get 'diff'
55 55 end
56 56 end
57 57
58 58 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
59 59 get '/issues/gantt', :to => 'gantts#show'
60 60
61 61 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
62 62 get '/issues/calendar', :to => 'calendars#show'
63 63
64 64 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
65 65 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
66 66
67 67 get '/issues/imports/new', :to => 'imports#new', :as => 'new_issues_import'
68 68 post '/imports', :to => 'imports#create', :as => 'imports'
69 69 get '/imports/:id', :to => 'imports#show', :as => 'import'
70 70 match '/imports/:id/settings', :to => 'imports#settings', :via => [:get, :post], :as => 'import_settings'
71 71 match '/imports/:id/mapping', :to => 'imports#mapping', :via => [:get, :post], :as => 'import_mapping'
72 72 match '/imports/:id/run', :to => 'imports#run', :via => [:get, :post], :as => 'import_run'
73 73
74 74 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
75 75 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
76 76 match 'my/page', :controller => 'my', :action => 'page', :via => :get
77 77 post 'my/page', :to => 'my#update_page'
78 78 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
79 79 get 'my/api_key', :to => 'my#show_api_key', :as => 'my_api_key'
80 80 post 'my/api_key', :to => 'my#reset_api_key'
81 81 post 'my/rss_key', :to => 'my#reset_rss_key', :as => 'my_rss_key'
82 82 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
83 83 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
84 84 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
85 85 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
86 86 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
87 87
88 88 resources :users do
89 89 resources :memberships, :controller => 'principal_memberships'
90 90 resources :email_addresses, :only => [:index, :create, :update, :destroy]
91 91 end
92 92
93 93 post 'watchers/watch', :to => 'watchers#watch', :as => 'watch'
94 94 delete 'watchers/watch', :to => 'watchers#unwatch'
95 95 get 'watchers/new', :to => 'watchers#new', :as => 'new_watchers'
96 96 post 'watchers', :to => 'watchers#create'
97 97 post 'watchers/append', :to => 'watchers#append'
98 98 delete 'watchers', :to => 'watchers#destroy'
99 99 get 'watchers/autocomplete_for_user', :to => 'watchers#autocomplete_for_user'
100 100 # Specific routes for issue watchers API
101 101 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
102 102 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
103 103
104 104 resources :projects do
105 105 member do
106 106 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
107 107 post 'modules'
108 108 post 'archive'
109 109 post 'unarchive'
110 110 post 'close'
111 111 post 'reopen'
112 112 match 'copy', :via => [:get, :post]
113 113 end
114 114
115 115 shallow do
116 116 resources :memberships, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
117 117 collection do
118 118 get 'autocomplete'
119 119 end
120 120 end
121 121 end
122 122
123 123 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
124 124
125 125 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
126 126 resources :issues, :only => [:index, :new, :create]
127 127 # Used when updating the form of a new issue
128 128 post 'issues/new', :to => 'issues#new'
129 129
130 130 resources :files, :only => [:index, :new, :create]
131 131
132 132 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
133 133 collection do
134 134 put 'close_completed'
135 135 end
136 136 end
137 137 get 'versions.:format', :to => 'versions#index'
138 138 get 'roadmap', :to => 'versions#index', :format => false
139 139 get 'versions', :to => 'versions#index'
140 140
141 141 resources :news, :except => [:show, :edit, :update, :destroy]
142 142 resources :time_entries, :controller => 'timelog', :except => [:show, :edit, :update, :destroy] do
143 143 get 'report', :on => :collection
144 144 end
145 145 resources :queries, :only => [:new, :create]
146 146 shallow do
147 147 resources :issue_categories
148 148 end
149 149 resources :documents, :except => [:show, :edit, :update, :destroy]
150 150 resources :boards
151 151 shallow do
152 152 resources :repositories, :except => [:index, :show] do
153 153 member do
154 154 match 'committers', :via => [:get, :post]
155 155 end
156 156 end
157 157 end
158 158
159 159 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
160 160 resources :wiki, :except => [:index, :create], :as => 'wiki_page' do
161 161 member do
162 162 get 'rename'
163 163 post 'rename'
164 164 get 'history'
165 165 get 'diff'
166 166 match 'preview', :via => [:post, :put, :patch]
167 167 post 'protect'
168 168 post 'add_attachment'
169 169 end
170 170 collection do
171 171 get 'export'
172 172 get 'date_index'
173 173 post 'new'
174 174 end
175 175 end
176 176 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
177 177 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
178 178 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
179 179 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
180 180 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
181 181 end
182 182
183 183 resources :issues do
184 184 member do
185 185 # Used when updating the form of an existing issue
186 186 patch 'edit', :to => 'issues#edit'
187 187 end
188 188 collection do
189 189 match 'bulk_edit', :via => [:get, :post]
190 190 post 'bulk_update'
191 191 end
192 192 resources :time_entries, :controller => 'timelog', :only => [:new, :create]
193 193 shallow do
194 194 resources :relations, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
195 195 end
196 196 end
197 197 # Used when updating the form of a new issue outside a project
198 198 post '/issues/new', :to => 'issues#new'
199 199 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
200 200
201 201 resources :queries, :except => [:show]
202 202
203 203 resources :news, :only => [:index, :show, :edit, :update, :destroy]
204 204 match '/news/:id/comments', :to => 'comments#create', :via => :post
205 205 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
206 206
207 207 resources :versions, :only => [:show, :edit, :update, :destroy] do
208 208 post 'status_by', :on => :member
209 209 end
210 210
211 211 resources :documents, :only => [:show, :edit, :update, :destroy] do
212 212 post 'add_attachment', :on => :member
213 213 end
214 214
215 215 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
216 216
217 217 resources :time_entries, :controller => 'timelog', :except => :destroy do
218 member do
219 # Used when updating the edit form of an existing time entry
220 patch 'edit', :to => 'timelog#edit'
221 end
218 222 collection do
219 223 get 'report'
220 224 get 'bulk_edit'
221 225 post 'bulk_update'
222 226 end
223 227 end
224 228 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
225 229 # TODO: delete /time_entries for bulk deletion
226 230 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
227 231 # Used to update the new time entry form
228 232 post '/time_entries/new', :to => 'timelog#new'
229 233
230 234 get 'projects/:id/activity', :to => 'activities#index', :as => :project_activity
231 235 get 'activity', :to => 'activities#index'
232 236
233 237 # repositories routes
234 238 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
235 239 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
236 240
237 241 get 'projects/:id/repository/:repository_id/changes(/*path)',
238 242 :to => 'repositories#changes',
239 243 :format => false
240 244
241 245 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
242 246 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
243 247 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
244 248 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
245 249 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
246 250 %w(browse show entry raw annotate diff).each do |action|
247 251 get "projects/:id/repository/:repository_id/revisions/:rev/#{action}(/*path)",
248 252 :controller => 'repositories',
249 253 :action => action,
250 254 :format => false,
251 255 :constraints => {:rev => /[a-z0-9\.\-_]+/}
252 256 end
253 257
254 258 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
255 259 get 'projects/:id/repository/graph', :to => 'repositories#graph'
256 260
257 261 get 'projects/:id/repository/changes(/*path)',
258 262 :to => 'repositories#changes',
259 263 :format => false
260 264
261 265 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
262 266 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
263 267 get 'projects/:id/repository/revision', :to => 'repositories#revision'
264 268 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
265 269 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
266 270 %w(browse show entry raw annotate diff).each do |action|
267 271 get "projects/:id/repository/revisions/:rev/#{action}(/*path)",
268 272 :controller => 'repositories',
269 273 :action => action,
270 274 :format => false,
271 275 :constraints => {:rev => /[a-z0-9\.\-_]+/}
272 276 end
273 277 %w(browse entry raw changes annotate diff).each do |action|
274 278 get "projects/:id/repository/:repository_id/#{action}(/*path)",
275 279 :controller => 'repositories',
276 280 :action => action,
277 281 :format => false
278 282 end
279 283 %w(browse entry raw changes annotate diff).each do |action|
280 284 get "projects/:id/repository/#{action}(/*path)",
281 285 :controller => 'repositories',
282 286 :action => action,
283 287 :format => false
284 288 end
285 289
286 290 get 'projects/:id/repository/:repository_id/show/*path', :to => 'repositories#show', :format => false
287 291 get 'projects/:id/repository/show/*path', :to => 'repositories#show', :format => false
288 292
289 293 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
290 294 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
291 295
292 296 # additional routes for having the file name at the end of url
293 297 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
294 298 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
295 299 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
296 300 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
297 301 resources :attachments, :only => [:show, :update, :destroy]
298 302 get 'attachments/:object_type/:object_id/edit', :to => 'attachments#edit_all', :as => :object_attachments_edit
299 303 patch 'attachments/:object_type/:object_id', :to => 'attachments#update_all', :as => :object_attachments
300 304
301 305 resources :groups do
302 306 resources :memberships, :controller => 'principal_memberships'
303 307 member do
304 308 get 'autocomplete_for_user'
305 309 end
306 310 end
307 311
308 312 get 'groups/:id/users/new', :to => 'groups#new_users', :id => /\d+/, :as => 'new_group_users'
309 313 post 'groups/:id/users', :to => 'groups#add_users', :id => /\d+/, :as => 'group_users'
310 314 delete 'groups/:id/users/:user_id', :to => 'groups#remove_user', :id => /\d+/, :as => 'group_user'
311 315
312 316 resources :trackers, :except => :show do
313 317 collection do
314 318 match 'fields', :via => [:get, :post]
315 319 end
316 320 end
317 321 resources :issue_statuses, :except => :show do
318 322 collection do
319 323 post 'update_issue_done_ratio'
320 324 end
321 325 end
322 326 resources :custom_fields, :except => :show do
323 327 resources :enumerations, :controller => 'custom_field_enumerations', :except => [:show, :new, :edit]
324 328 put 'enumerations', :to => 'custom_field_enumerations#update_each'
325 329 end
326 330 resources :roles do
327 331 collection do
328 332 match 'permissions', :via => [:get, :post]
329 333 end
330 334 end
331 335 resources :enumerations, :except => :show
332 336 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
333 337
334 338 get 'projects/:id/search', :controller => 'search', :action => 'index'
335 339 get 'search', :controller => 'search', :action => 'index'
336 340
337 341
338 342 get 'mail_handler', :to => 'mail_handler#new'
339 343 post 'mail_handler', :to => 'mail_handler#index'
340 344
341 345 get 'admin', :to => 'admin#index'
342 346 get 'admin/projects', :to => 'admin#projects'
343 347 get 'admin/plugins', :to => 'admin#plugins'
344 348 get 'admin/info', :to => 'admin#info'
345 349 post 'admin/test_email', :to => 'admin#test_email', :as => 'test_email'
346 350 post 'admin/default_configuration', :to => 'admin#default_configuration'
347 351
348 352 resources :auth_sources do
349 353 member do
350 354 get 'test_connection', :as => 'try_connection'
351 355 end
352 356 collection do
353 357 get 'autocomplete_for_new_user'
354 358 end
355 359 end
356 360
357 361 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
358 362 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
359 363 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
360 364 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
361 365 match 'settings', :controller => 'settings', :action => 'index', :via => :get
362 366 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
363 367 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
364 368
365 369 match 'sys/projects', :to => 'sys#projects', :via => :get
366 370 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
367 371 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => [:get, :post]
368 372
369 373 match 'uploads', :to => 'attachments#upload', :via => :post
370 374
371 375 get 'robots.txt', :to => 'welcome#robots'
372 376
373 377 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
374 378 file = File.join(plugin_dir, "config/routes.rb")
375 379 if File.exists?(file)
376 380 begin
377 381 instance_eval File.read(file)
378 382 rescue Exception => e
379 383 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
380 384 exit 1
381 385 end
382 386 end
383 387 end
384 388 end
@@ -1,1018 +1,1020
1 1 # -*- coding: utf-8 -*-
2 2 # Redmine - project management software
3 3 # Copyright (C) 2006-2016 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 < Redmine::ControllerTest
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
36 36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
37 37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
38 assert_select 'span[id=?]', 'time_entry_issue'
38 39 assert_select 'select[name=?]', 'time_entry[project_id]' do
39 40 # blank option for project
40 41 assert_select 'option[value=""]'
41 42 end
42 43 end
43 44
44 45 def test_new_with_project_id
45 46 @request.session[:user_id] = 3
46 47 get :new, :params => {:project_id => 1}
47 48 assert_response :success
48 49
49 50 assert_select 'input[name=?][type=hidden]', 'project_id'
50 51 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
51 52 assert_select 'select[name=?]', 'time_entry[project_id]', 0
52 53 end
53 54
54 55 def test_new_with_issue_id
55 56 @request.session[:user_id] = 3
56 57 get :new, :params => {:issue_id => 2}
57 58 assert_response :success
58 59
59 60 assert_select 'input[name=?][type=hidden]', 'project_id', 0
60 61 assert_select 'input[name=?][type=hidden]', 'issue_id'
62 assert_select 'a[href=?]', '/issues/2', :text => /Feature request #2/
61 63 assert_select 'select[name=?]', 'time_entry[project_id]', 0
62 64 end
63 65
64 66 def test_new_without_project_should_prefill_the_form
65 67 @request.session[:user_id] = 3
66 68 get :new, :params => {:time_entry => {:project_id => '1'}}
67 69 assert_response :success
68 70
69 71 assert_select 'select[name=?]', 'time_entry[project_id]' do
70 72 assert_select 'option[value="1"][selected=selected]'
71 73 end
72 74 end
73 75
74 76 def test_new_without_project_should_deny_without_permission
75 77 Role.all.each {|role| role.remove_permission! :log_time}
76 78 @request.session[:user_id] = 3
77 79
78 80 get :new
79 81 assert_response 403
80 82 end
81 83
82 84 def test_new_should_select_default_activity
83 85 @request.session[:user_id] = 3
84 86 get :new, :params => {:project_id => 1}
85 87 assert_response :success
86 88 assert_select 'select[name=?]', 'time_entry[activity_id]' do
87 89 assert_select 'option[selected=selected]', :text => 'Development'
88 90 end
89 91 end
90 92
91 93 def test_new_should_only_show_active_time_entry_activities
92 94 @request.session[:user_id] = 3
93 95 get :new, :params => {:project_id => 1}
94 96 assert_response :success
95 97 assert_select 'option', :text => 'Inactive Activity', :count => 0
96 98 end
97 99
98 100 def test_post_new_as_js_should_update_activity_options
99 101 @request.session[:user_id] = 3
100 102 post :new, :params => {:time_entry => {:project_id => 1}, :format => 'js'}
101 103 assert_response :success
102 104 assert_include '#time_entry_activity_id', response.body
103 105 end
104 106
105 107 def test_get_edit_existing_time
106 108 @request.session[:user_id] = 2
107 109 get :edit, :params => {:id => 2, :project_id => nil}
108 110 assert_response :success
109 111
110 112 assert_select 'form[action=?]', '/time_entries/2'
111 113 end
112 114
113 115 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
114 116 te = TimeEntry.find(1)
115 117 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
116 118 te.save!(:validate => false)
117 119
118 120 @request.session[:user_id] = 1
119 121 get :edit, :params => {:project_id => 1, :id => 1}
120 122 assert_response :success
121 123
122 124 # Blank option since nothing is pre-selected
123 125 assert_select 'option', :text => '--- Please select ---'
124 126 end
125 127
126 128 def test_post_create
127 129 @request.session[:user_id] = 3
128 130 assert_difference 'TimeEntry.count' do
129 131 post :create, :params => {
130 132 :project_id => 1,
131 133 :time_entry => {:comments => 'Some work on TimelogControllerTest',
132 134 # Not the default activity
133 135 :activity_id => '11',
134 136 :spent_on => '2008-03-14',
135 137 :issue_id => '1',
136 138 :hours => '7.3'
137 139 }
138 140 }
139 141 assert_redirected_to '/projects/ecookbook/time_entries'
140 142 end
141 143
142 144 t = TimeEntry.order('id DESC').first
143 145 assert_not_nil t
144 146 assert_equal 'Some work on TimelogControllerTest', t.comments
145 147 assert_equal 1, t.project_id
146 148 assert_equal 1, t.issue_id
147 149 assert_equal 11, t.activity_id
148 150 assert_equal 7.3, t.hours
149 151 assert_equal 3, t.user_id
150 152 end
151 153
152 154 def test_post_create_with_blank_issue
153 155 @request.session[:user_id] = 3
154 156 assert_difference 'TimeEntry.count' do
155 157 post :create, :params => {
156 158 :project_id => 1,
157 159 :time_entry => {
158 160 :comments => 'Some work on TimelogControllerTest',
159 161 # Not the default activity
160 162 :activity_id => '11',
161 163 :issue_id => '',
162 164 :spent_on => '2008-03-14',
163 165 :hours => '7.3'
164 166 }
165 167 }
166 168 assert_redirected_to '/projects/ecookbook/time_entries'
167 169 end
168 170
169 171 t = TimeEntry.order('id DESC').first
170 172 assert_not_nil t
171 173 assert_equal 'Some work on TimelogControllerTest', t.comments
172 174 assert_equal 1, t.project_id
173 175 assert_nil t.issue_id
174 176 assert_equal 11, t.activity_id
175 177 assert_equal 7.3, t.hours
176 178 assert_equal 3, t.user_id
177 179 end
178 180
179 181 def test_create_on_project_with_time_tracking_disabled_should_fail
180 182 Project.find(1).disable_module! :time_tracking
181 183
182 184 @request.session[:user_id] = 2
183 185 assert_no_difference 'TimeEntry.count' do
184 186 post :create, :params => {
185 187 :time_entry => {
186 188 :project_id => '1', :issue_id => '',
187 189 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
188 190 }
189 191 }
190 192 end
191 193 end
192 194
193 195 def test_create_on_project_without_permission_should_fail
194 196 Role.find(1).remove_permission! :log_time
195 197
196 198 @request.session[:user_id] = 2
197 199 assert_no_difference 'TimeEntry.count' do
198 200 post :create, :params => {
199 201 :time_entry => {
200 202 :project_id => '1', :issue_id => '',
201 203 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
202 204 }
203 205 }
204 206 end
205 207 end
206 208
207 209 def test_create_on_issue_in_project_with_time_tracking_disabled_should_fail
208 210 Project.find(1).disable_module! :time_tracking
209 211
210 212 @request.session[:user_id] = 2
211 213 assert_no_difference 'TimeEntry.count' do
212 214 post :create, :params => {
213 215 :time_entry => {
214 216 :project_id => '', :issue_id => '1',
215 217 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
216 218 }
217 219 }
218 220 assert_select_error /Issue is invalid/
219 221 end
220 222 end
221 223
222 224 def test_create_on_issue_in_project_without_permission_should_fail
223 225 Role.find(1).remove_permission! :log_time
224 226
225 227 @request.session[:user_id] = 2
226 228 assert_no_difference 'TimeEntry.count' do
227 229 post :create, :params => {
228 230 :time_entry => {
229 231 :project_id => '', :issue_id => '1',
230 232 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
231 233 }
232 234 }
233 235 assert_select_error /Issue is invalid/
234 236 end
235 237 end
236 238
237 239 def test_create_on_issue_that_is_not_visible_should_not_disclose_subject
238 240 issue = Issue.generate!(:subject => "issue_that_is_not_visible", :is_private => true)
239 241 assert !issue.visible?(User.find(3))
240 242
241 243 @request.session[:user_id] = 3
242 244 assert_no_difference 'TimeEntry.count' do
243 245 post :create, :params => {
244 246 :time_entry => {
245 247 :project_id => '', :issue_id => issue.id.to_s,
246 248 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
247 249 }
248 250 }
249 251 end
250 252 assert_select_error /Issue is invalid/
251 253 assert_select "input[name=?][value=?]", "time_entry[issue_id]", issue.id.to_s
252 assert_select "#time_entry_issue", 0
254 assert_select "#time_entry_issue a", 0
253 255 assert !response.body.include?('issue_that_is_not_visible')
254 256 end
255 257
256 258 def test_create_and_continue_at_project_level
257 259 @request.session[:user_id] = 2
258 260 assert_difference 'TimeEntry.count' do
259 261 post :create, :params => {
260 262 :time_entry => {
261 263 :project_id => '1',
262 264 :activity_id => '11',
263 265 :issue_id => '',
264 266 :spent_on => '2008-03-14',
265 267 :hours => '7.3'
266 268 },
267 269 :continue => '1'
268 270 }
269 271 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
270 272 end
271 273 end
272 274
273 275 def test_create_and_continue_at_issue_level
274 276 @request.session[:user_id] = 2
275 277 assert_difference 'TimeEntry.count' do
276 278 post :create, :params => {
277 279 :time_entry => {
278 280 :project_id => '',
279 281 :activity_id => '11',
280 282 :issue_id => '1',
281 283 :spent_on => '2008-03-14',
282 284 :hours => '7.3'
283 285 },
284 286 :continue => '1'
285 287 }
286 288 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
287 289 end
288 290 end
289 291
290 292 def test_create_and_continue_with_project_id
291 293 @request.session[:user_id] = 2
292 294 assert_difference 'TimeEntry.count' do
293 295 post :create, :params => {
294 296 :project_id => 1,
295 297 :time_entry => {
296 298 :activity_id => '11',
297 299 :issue_id => '',
298 300 :spent_on => '2008-03-14',
299 301 :hours => '7.3'
300 302 },
301 303 :continue => '1'
302 304 }
303 305 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
304 306 end
305 307 end
306 308
307 309 def test_create_and_continue_with_issue_id
308 310 @request.session[:user_id] = 2
309 311 assert_difference 'TimeEntry.count' do
310 312 post :create, :params => {
311 313 :issue_id => 1,
312 314 :time_entry => {
313 315 :activity_id => '11',
314 316 :issue_id => '1',
315 317 :spent_on => '2008-03-14',
316 318 :hours => '7.3'
317 319 },
318 320 :continue => '1'
319 321 }
320 322 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='
321 323 end
322 324 end
323 325
324 326 def test_create_without_log_time_permission_should_be_denied
325 327 @request.session[:user_id] = 2
326 328 Role.find_by_name('Manager').remove_permission! :log_time
327 329 post :create, :params => {
328 330 :project_id => 1,
329 331 :time_entry => {
330 332 :activity_id => '11',
331 333 :issue_id => '',
332 334 :spent_on => '2008-03-14',
333 335 :hours => '7.3'
334 336 }
335 337 }
336 338 assert_response 403
337 339 end
338 340
339 341 def test_create_without_project_and_issue_should_fail
340 342 @request.session[:user_id] = 2
341 343 post :create, :params => {:time_entry => {:issue_id => ''}}
342 344
343 345 assert_response :success
344 346 assert_select_error /Project cannot be blank/
345 347 end
346 348
347 349 def test_create_with_failure
348 350 @request.session[:user_id] = 2
349 351 post :create, :params => {
350 352 :project_id => 1,
351 353 :time_entry => {
352 354 :activity_id => '',
353 355 :issue_id => '',
354 356 :spent_on => '2008-03-14',
355 357 :hours => '7.3'
356 358 }
357 359 }
358 360 assert_response :success
359 361 end
360 362
361 363 def test_create_without_project
362 364 @request.session[:user_id] = 2
363 365 assert_difference 'TimeEntry.count' do
364 366 post :create, :params => {
365 367 :time_entry => {
366 368 :project_id => '1',
367 369 :activity_id => '11',
368 370 :issue_id => '',
369 371 :spent_on => '2008-03-14',
370 372 :hours => '7.3'
371 373 }
372 374 }
373 375 end
374 376
375 377 assert_redirected_to '/projects/ecookbook/time_entries'
376 378 time_entry = TimeEntry.order('id DESC').first
377 379 assert_equal 1, time_entry.project_id
378 380 end
379 381
380 382 def test_create_without_project_should_fail_with_issue_not_inside_project
381 383 @request.session[:user_id] = 2
382 384 assert_no_difference 'TimeEntry.count' do
383 385 post :create, :params => {
384 386 :time_entry => {
385 387 :project_id => '1',
386 388 :activity_id => '11',
387 389 :issue_id => '5',
388 390 :spent_on => '2008-03-14',
389 391 :hours => '7.3'
390 392 }
391 393 }
392 394 end
393 395
394 396 assert_response :success
395 397 assert_select_error /Issue is invalid/
396 398 end
397 399
398 400 def test_create_without_project_should_deny_without_permission
399 401 @request.session[:user_id] = 2
400 402 Project.find(3).disable_module!(:time_tracking)
401 403
402 404 assert_no_difference 'TimeEntry.count' do
403 405 post :create, :params => {
404 406 :time_entry => {
405 407 :project_id => '3',
406 408 :activity_id => '11',
407 409 :issue_id => '',
408 410 :spent_on => '2008-03-14',
409 411 :hours => '7.3'
410 412 }
411 413 }
412 414 end
413 415
414 416 assert_response 403
415 417 end
416 418
417 419 def test_create_without_project_with_failure
418 420 @request.session[:user_id] = 2
419 421 assert_no_difference 'TimeEntry.count' do
420 422 post :create, :params => {
421 423 :time_entry => {
422 424 :project_id => '1',
423 425 :activity_id => '11',
424 426 :issue_id => '',
425 427 :spent_on => '2008-03-14',
426 428 :hours => ''
427 429 }
428 430 }
429 431 end
430 432
431 433 assert_response :success
432 434 assert_select 'select[name=?]', 'time_entry[project_id]' do
433 435 assert_select 'option[value="1"][selected=selected]'
434 436 end
435 437 end
436 438
437 439 def test_update
438 440 entry = TimeEntry.find(1)
439 441 assert_equal 1, entry.issue_id
440 442 assert_equal 2, entry.user_id
441 443
442 444 @request.session[:user_id] = 1
443 445 put :update, :params => {
444 446 :id => 1,
445 447 :time_entry => {
446 448 :issue_id => '2',
447 449 :hours => '8'
448 450 }
449 451 }
450 452 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
451 453 entry.reload
452 454
453 455 assert_equal 8, entry.hours
454 456 assert_equal 2, entry.issue_id
455 457 assert_equal 2, entry.user_id
456 458 end
457 459
458 460 def test_update_should_allow_to_change_issue_to_another_project
459 461 entry = TimeEntry.generate!(:issue_id => 1)
460 462
461 463 @request.session[:user_id] = 1
462 464 put :update, :params => {
463 465 :id => entry.id,
464 466 :time_entry => {
465 467 :issue_id => '5'
466 468 }
467 469 }
468 470 assert_response 302
469 471 entry.reload
470 472
471 473 assert_equal 5, entry.issue_id
472 474 assert_equal 3, entry.project_id
473 475 end
474 476
475 477 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
476 478 entry = TimeEntry.generate!(:issue_id => 1)
477 479 Project.find(3).disable_module!(:time_tracking)
478 480
479 481 @request.session[:user_id] = 1
480 482 put :update, :params => {
481 483 :id => entry.id,
482 484 :time_entry => {
483 485 :issue_id => '5'
484 486 }
485 487 }
486 488 assert_response :success
487 489 assert_select_error /Issue is invalid/
488 490 end
489 491
490 492 def test_get_bulk_edit
491 493 @request.session[:user_id] = 2
492 494
493 495 get :bulk_edit, :params => {:ids => [1, 2]}
494 496 assert_response :success
495 497
496 498 assert_select 'ul#bulk-selection' do
497 499 assert_select 'li', 2
498 500 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
499 501 end
500 502
501 503 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
502 504 # System wide custom field
503 505 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
504
506
505 507 # Activities
506 508 assert_select 'select[name=?]', 'time_entry[activity_id]' do
507 509 assert_select 'option[value=""]', :text => '(No change)'
508 510 assert_select 'option[value="9"]', :text => 'Design'
509 511 end
510 512 end
511 513 end
512 514
513 515 def test_get_bulk_edit_on_different_projects
514 516 @request.session[:user_id] = 2
515 517
516 518 get :bulk_edit, :params => {:ids => [1, 2, 6]}
517 519 assert_response :success
518 520 end
519 521
520 522 def test_bulk_edit_with_edit_own_time_entries_permission
521 523 @request.session[:user_id] = 2
522 524 Role.find_by_name('Manager').remove_permission! :edit_time_entries
523 525 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
524 526 ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
525 527
526 528 get :bulk_edit, :params => {:ids => ids}
527 529 assert_response :success
528 530 end
529 531
530 532 def test_bulk_update
531 533 @request.session[:user_id] = 2
532 534 # update time entry activity
533 535 post :bulk_update, :params => {:ids => [1, 2], :time_entry => { :activity_id => 9}}
534 536
535 537 assert_response 302
536 538 # check that the issues were updated
537 539 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
538 540 end
539 541
540 542 def test_bulk_update_with_failure
541 543 @request.session[:user_id] = 2
542 544 post :bulk_update, :params => {:ids => [1, 2], :time_entry => { :hours => 'A'}}
543 545
544 546 assert_response 302
545 547 assert_match /Failed to save 2 time entrie/, flash[:error]
546 548 end
547 549
548 550 def test_bulk_update_on_different_projects
549 551 @request.session[:user_id] = 2
550 552 # makes user a manager on the other project
551 553 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
552
554
553 555 # update time entry activity
554 556 post :bulk_update, :params => {:ids => [1, 2, 4], :time_entry => { :activity_id => 9 }}
555 557
556 558 assert_response 302
557 559 # check that the issues were updated
558 560 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
559 561 end
560 562
561 563 def test_bulk_update_on_different_projects_without_rights
562 564 @request.session[:user_id] = 3
563 565 user = User.find(3)
564 566 action = { :controller => "timelog", :action => "bulk_update" }
565 567 assert user.allowed_to?(action, TimeEntry.find(1).project)
566 568 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
567 569
568 570 post :bulk_update, :params => {:ids => [1, 5], :time_entry => { :activity_id => 9 }}
569 571 assert_response 403
570 572 end
571 573
572 574 def test_bulk_update_with_edit_own_time_entries_permission
573 575 @request.session[:user_id] = 2
574 576 Role.find_by_name('Manager').remove_permission! :edit_time_entries
575 577 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
576 578 ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
577 579
578 580 post :bulk_update, :params => {:ids => ids, :time_entry => { :activity_id => 9 }}
579 581 assert_response 302
580 582 end
581 583
582 584 def test_bulk_update_with_edit_own_time_entries_permissions_should_be_denied_for_time_entries_of_other_user
583 585 @request.session[:user_id] = 2
584 586 Role.find_by_name('Manager').remove_permission! :edit_time_entries
585 587 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
586 588
587 589 post :bulk_update, :params => {:ids => [1, 2], :time_entry => { :activity_id => 9 }}
588 590 assert_response 403
589 591 end
590 592
591 593 def test_bulk_update_custom_field
592 594 @request.session[:user_id] = 2
593 595 post :bulk_update, :params => {:ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }}
594 596
595 597 assert_response 302
596 598 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
597 599 end
598 600
599 601 def test_bulk_update_clear_custom_field
600 602 field = TimeEntryCustomField.generate!(:field_format => 'string')
601 603 @request.session[:user_id] = 2
602 604 post :bulk_update, :params => {:ids => [1, 2], :time_entry => { :custom_field_values => {field.id.to_s => '__none__'} }}
603 605
604 606 assert_response 302
605 607 assert_equal ["", ""], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(field).value}
606 608 end
607 609
608 610 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
609 611 @request.session[:user_id] = 2
610 612 post :bulk_update, :params => {:ids => [1,2], :back_url => '/time_entries'}
611 613
612 614 assert_response :redirect
613 615 assert_redirected_to '/time_entries'
614 616 end
615 617
616 618 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
617 619 @request.session[:user_id] = 2
618 620 post :bulk_update, :params => {:ids => [1,2], :back_url => 'http://google.com'}
619 621
620 622 assert_response :redirect
621 623 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
622 624 end
623 625
624 626 def test_post_bulk_update_without_edit_permission_should_be_denied
625 627 @request.session[:user_id] = 2
626 628 Role.find_by_name('Manager').remove_permission! :edit_time_entries
627 629
628 630 post :bulk_update, :params => {:ids => [1,2]}
629 631 assert_response 403
630 632 end
631 633
632 634 def test_destroy
633 635 @request.session[:user_id] = 2
634 636
635 637 delete :destroy, :params => {:id => 1}
636 638 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
637 639 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
638 640 assert_nil TimeEntry.find_by_id(1)
639 641 end
640 642
641 643 def test_destroy_should_fail
642 644 # simulate that this fails (e.g. due to a plugin), see #5700
643 645 TimeEntry.any_instance.expects(:destroy).returns(false)
644 646 @request.session[:user_id] = 2
645 647
646 648 delete :destroy, :params => {:id => 1}
647 649 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
648 650 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
649 651 assert_not_nil TimeEntry.find_by_id(1)
650 652 end
651 653
652 654 def test_index_all_projects
653 655 get :index
654 656 assert_response :success
655 657
656 658 assert_select '.total-for-hours', :text => 'Hours: 162.90'
657 659 assert_select 'form#query_form[action=?]', '/time_entries'
658 660 end
659 661
660 662 def test_index_all_projects_should_show_log_time_link
661 663 @request.session[:user_id] = 2
662 664 get :index
663 665 assert_response :success
664 666
665 667 assert_select 'a[href=?]', '/time_entries/new', :text => /Log time/
666 668 end
667 669
668 670 def test_index_my_spent_time
669 671 @request.session[:user_id] = 2
670 672 get :index, :params => {:user_id => 'me', :c => ['user']}
671 673 assert_response :success
672 674
673 675 users = css_select('table.time-entries tbody td.user').map(&:text).uniq
674 676 assert_equal ["John Smith"], users
675 677 end
676 678
677 679 def test_index_at_project_level
678 680 get :index, :params => {:project_id => 'ecookbook', :c => ['project']}
679 681 assert_response :success
680
682
681 683 assert_select 'tr.time-entry', 4
682
684
683 685 # project and subproject
684 686 projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
685 687 assert_equal ["eCookbook", "eCookbook Subproject 1"], projects
686 688
687 689 assert_select '.total-for-hours', :text => 'Hours: 162.90'
688 690 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
689 691 end
690 692
691 693 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
692 694 entry = TimeEntry.generate!(:project => Project.find(3))
693 695
694 696 with_settings :display_subprojects_issues => '0' do
695 697 get :index, :params => {:project_id => 'ecookbook', :c => ['project']}
696 698 assert_response :success
697 699
698 700 projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
699 701 assert_equal ["eCookbook"], projects
700 702 end
701 703 end
702 704
703 705 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
704 706 entry = TimeEntry.generate!(:project => Project.find(3))
705 707
706 708 with_settings :display_subprojects_issues => '0' do
707 709 get :index, :params => {:project_id => 'ecookbook', :c => ['project'], :subproject_id => 3}
708 710 assert_response :success
709 711
710 712 projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
711 713 assert_equal ["eCookbook", "eCookbook Subproject 1"], projects
712 714 end
713 715 end
714 716
715 717 def test_index_at_project_level_with_issue_id_short_filter
716 718 issue = Issue.generate!(:project_id => 1)
717 719 TimeEntry.generate!(:issue => issue, :hours => 4)
718 720 TimeEntry.generate!(:issue => issue, :hours => 3)
719 721 @request.session[:user_id] = 2
720 722
721 723 get :index, :params => {:project_id => 'ecookbook', :issue_id => issue.id.to_s, :set_filter => 1}
722 724 assert_select '.total-for-hours', :text => 'Hours: 7.00'
723 725 end
724 726
725 727 def test_index_at_project_level_with_issue_fixed_version_id_short_filter
726 728 version = Version.generate!(:project_id => 1)
727 729 issue = Issue.generate!(:project_id => 1, :fixed_version => version)
728 730 TimeEntry.generate!(:issue => issue, :hours => 2)
729 731 TimeEntry.generate!(:issue => issue, :hours => 3)
730 732 @request.session[:user_id] = 2
731 733
732 734 get :index, :params => {:project_id => 'ecookbook', :"issue.fixed_version_id" => version.id.to_s, :set_filter => 1}
733 735 assert_select '.total-for-hours', :text => 'Hours: 5.00'
734 736 end
735 737
736 738 def test_index_at_project_level_with_date_range
737 739 get :index, :params => {
738 740 :project_id => 'ecookbook',
739 741 :f => ['spent_on'],
740 742 :op => {'spent_on' => '><'},
741 743 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
742 744 }
743 745 assert_response :success
744 746
745 747 assert_select 'tr.time-entry', 3
746 748 assert_select '.total-for-hours', :text => 'Hours: 12.90'
747 749 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
748 750 end
749 751
750 752 def test_index_at_project_level_with_date_range_using_from_and_to_params
751 753 get :index, :params => {
752 754 :project_id => 'ecookbook',
753 755 :from => '2007-03-20',
754 756 :to => '2007-04-30'
755 757 }
756 758 assert_response :success
757 759
758 760 assert_select 'tr.time-entry', 3
759 761 assert_select '.total-for-hours', :text => 'Hours: 12.90'
760 762 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
761 763 end
762 764
763 765 def test_index_at_project_level_with_period
764 766 get :index, :params => {
765 767 :project_id => 'ecookbook',
766 768 :f => ['spent_on'],
767 769 :op => {'spent_on' => '>t-'},
768 770 :v => {'spent_on' => ['7']}
769 771 }
770 772 assert_response :success
771 773
772 774 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
773 775 end
774 776
775 777 def test_index_should_sort_by_spent_on_and_created_on
776 778 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)
777 779 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)
778 780 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)
779 781
780 782 get :index, :params => {
781 783 :project_id => 1,
782 784 :f => ['spent_on'],
783 785 :op => {'spent_on' => '><'},
784 786 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
785 787 }
786 788 assert_response :success
787 789 assert_equal [t2, t1, t3].map(&:id).map(&:to_s), css_select('input[name="ids[]"]').map {|e| e.attr('value')}
788 790
789 791 get :index, :params => {
790 792 :project_id => 1,
791 793 :f => ['spent_on'],
792 794 :op => {'spent_on' => '><'},
793 795 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
794 796 :sort => 'spent_on'
795 797 }
796 798 assert_response :success
797 799 assert_equal [t3, t1, t2].map(&:id).map(&:to_s), css_select('input[name="ids[]"]').map {|e| e.attr('value')}
798 800 end
799 801
800 802 def test_index_with_issue_status_filter
801 803 Issue.where(:status_id => 4).update_all(:status_id => 2)
802 804 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
803 805 entry = TimeEntry.generate!(:issue => issue, :hours => 4.5)
804 806
805 807 get :index, :params => {
806 808 :f => ['issue.status_id'],
807 809 :op => {'issue.status_id' => '='},
808 810 :v => {'issue.status_id' => ['4']}
809 811 }
810 812 assert_response :success
811 813 assert_equal [entry].map(&:id).map(&:to_s), css_select('input[name="ids[]"]').map {|e| e.attr('value')}
812 814 end
813 815
814 816 def test_index_with_issue_status_column
815 817 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
816 818 entry = TimeEntry.generate!(:issue => issue)
817 819
818 820 get :index, :params => {
819 821 :c => %w(project spent_on issue comments hours issue.status)
820 822 }
821 823 assert_response :success
822 824 assert_select 'td.issue-status', :text => issue.status.name
823 825 end
824 826
825 827 def test_index_with_issue_status_sort
826 828 TimeEntry.delete_all
827 829 TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 1))
828 830 TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 5))
829 831 TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 3))
830 832 TimeEntry.generate!(:project_id => 1)
831 833
832 834 get :index, :params => {
833 835 :c => ["hours", 'issue.status'],
834 836 :sort => 'issue.status'
835 837 }
836 838 assert_response :success
837 839
838 840 # Make sure that values are properly sorted
839 841 values = css_select("td.issue-status").map(&:text).reject(&:blank?)
840 842 assert_equal IssueStatus.where(:id => [1, 5, 3]).sorted.pluck(:name), values
841 843 end
842 844
843 845 def test_index_with_issue_tracker_filter
844 846 Issue.where(:tracker_id => 2).update_all(:tracker_id => 1)
845 847 issue = Issue.generate!(:project_id => 1, :tracker_id => 2)
846 848 entry = TimeEntry.generate!(:issue => issue, :hours => 4.5)
847 849
848 850 get :index, :params => {
849 851 :f => ['issue.tracker_id'],
850 852 :op => {'issue.tracker_id' => '='},
851 853 :v => {'issue.tracker_id' => ['2']}
852 854 }
853 855 assert_response :success
854 856 assert_equal [entry].map(&:id).map(&:to_s), css_select('input[name="ids[]"]').map {|e| e.attr('value')}
855 857 end
856 858
857 859 def test_index_with_issue_tracker_column
858 860 issue = Issue.generate!(:project_id => 1, :tracker_id => 2)
859 861 entry = TimeEntry.generate!(:issue => issue)
860 862
861 863 get :index, :params => {
862 864 :c => %w(project spent_on issue comments hours issue.tracker)
863 865 }
864 866 assert_response :success
865 867 assert_select 'td.issue-tracker', :text => issue.tracker.name
866 868 end
867 869
868 870 def test_index_with_issue_tracker_sort
869 871 TimeEntry.delete_all
870 872 TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 1))
871 873 TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 3))
872 874 TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 2))
873 875 TimeEntry.generate!(:project_id => 1)
874 876
875 877 get :index, :params => {
876 878 :c => ["hours", 'issue.tracker'],
877 879 :sort => 'issue.tracker'
878 880 }
879 881 assert_response :success
880 882
881 883 # Make sure that values are properly sorted
882 884 values = css_select("td.issue-tracker").map(&:text).reject(&:blank?)
883 885 assert_equal Tracker.where(:id => [1, 2, 3]).sorted.pluck(:name), values
884 886 end
885 887
886 888 def test_index_with_filter_on_issue_custom_field
887 889 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
888 890 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
889 891
890 892 get :index, :params => {
891 893 :f => ['issue.cf_2'],
892 894 :op => {'issue.cf_2' => '='},
893 895 :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
894 896 }
895 897 assert_response :success
896 898 assert_equal [entry].map(&:id).map(&:to_s), css_select('input[name="ids[]"]').map {|e| e.attr('value')}
897 899 end
898 900
899 901 def test_index_with_issue_custom_field_column
900 902 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
901 903 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
902 904
903 905 get :index, :params => {
904 906 :c => %w(project spent_on issue comments hours issue.cf_2)
905 907 }
906 908 assert_response :success
907 909 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
908 910 end
909 911
910 912 def test_index_with_time_entry_custom_field_column
911 913 field = TimeEntryCustomField.generate!(:field_format => 'string')
912 914 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
913 915 field_name = "cf_#{field.id}"
914 916
915 917 get :index, :params => {
916 918 :c => ["hours", field_name]
917 919 }
918 920 assert_response :success
919 921 assert_select "td.#{field_name}", :text => 'CF Value'
920 922 end
921 923
922 924 def test_index_with_time_entry_custom_field_sorting
923 925 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
924 926 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
925 927 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
926 928 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
927 929 field_name = "cf_#{field.id}"
928 930
929 931 get :index, :params => {
930 932 :c => ["hours", field_name],
931 933 :sort => field_name
932 934 }
933 935 assert_response :success
934 936 assert_select "th a.sort", :text => 'String Field'
935 937
936 938 # Make sure that values are properly sorted
937 939 values = css_select("td.#{field_name}").map(&:text).reject(&:blank?)
938 940 assert_equal values.sort, values
939 941 assert_equal 3, values.size
940 942 end
941 943
942 944 def test_index_with_query
943 945 query = TimeEntryQuery.new(:project_id => 1, :name => 'Time Entry Query', :visibility => 2)
944 946 query.save!
945 947 @request.session[:user_id] = 2
946 948
947 949 get :index, :params => {:project_id => 'ecookbook', :query_id => query.id}
948 950 assert_response :success
949 951 assert_select 'h2', :text => query.name
950 952 assert_select '#sidebar a.selected', :text => query.name
951 953 end
952 954
953 955 def test_index_atom_feed
954 956 get :index, :params => {:project_id => 1, :format => 'atom'}
955 957 assert_response :success
956 958 assert_equal 'application/atom+xml', @response.content_type
957 959 assert_select 'entry > title', :text => /7\.65 hours/
958 960 end
959 961
960 962 def test_index_at_project_level_should_include_csv_export_dialog
961 963 get :index, :params => {
962 :project_id => 'ecookbook',
964 :project_id => 'ecookbook',
963 965 :f => ['spent_on'],
964 966 :op => {'spent_on' => '>='},
965 967 :v => {'spent_on' => ['2007-04-01']},
966 968 :c => ['spent_on', 'user']
967 969 }
968 970 assert_response :success
969 971
970 972 assert_select '#csv-export-options' do
971 973 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
972 974 # filter
973 975 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
974 976 assert_select 'input[name=?][value=?]', 'op[spent_on]', '>='
975 977 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
976 978 # columns
977 979 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
978 980 assert_select 'input[name=?][value=?]', 'c[]', 'user'
979 981 assert_select 'input[name=?]', 'c[]', 2
980 982 end
981 983 end
982 984 end
983 985
984 986 def test_index_cross_project_should_include_csv_export_dialog
985 987 get :index
986 988 assert_response :success
987 989
988 990 assert_select '#csv-export-options' do
989 991 assert_select 'form[action=?][method=get]', '/time_entries.csv'
990 992 end
991 993 end
992 994
993 995 def test_index_csv_all_projects
994 996 with_settings :date_format => '%m/%d/%Y' do
995 997 get :index, :params => {:format => 'csv'}
996 998 assert_response :success
997 999 assert_equal 'text/csv; header=present', response.content_type
998 1000 end
999 1001 end
1000 1002
1001 1003 def test_index_csv
1002 1004 with_settings :date_format => '%m/%d/%Y' do
1003 1005 get :index, :params => {:project_id => 1, :format => 'csv'}
1004 1006 assert_response :success
1005 1007 assert_equal 'text/csv; header=present', response.content_type
1006 1008 end
1007 1009 end
1008 1010
1009 1011 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
1010 1012 issue = Issue.find(1)
1011 1013 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
1012 1014
1013 1015 get :index, :params => {:format => 'csv'}
1014 1016 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
1015 1017 assert_not_nil line
1016 1018 assert_include "#{issue.tracker} #1: #{issue.subject}", line
1017 1019 end
1018 1020 end
@@ -1,60 +1,61
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 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 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 20 class RoutingTimelogsTest < Redmine::RoutingTest
21 21 def test_timelogs_global
22 22 should_route 'GET /time_entries' => 'timelog#index'
23 23 should_route 'GET /time_entries.csv' => 'timelog#index', :format => 'csv'
24 24 should_route 'GET /time_entries.atom' => 'timelog#index', :format => 'atom'
25 25 should_route 'GET /time_entries/new' => 'timelog#new'
26 26 should_route 'POST /time_entries/new' => 'timelog#new'
27 27 should_route 'POST /time_entries' => 'timelog#create'
28 28
29 29 should_route 'GET /time_entries/22/edit' => 'timelog#edit', :id => '22'
30 should_route 'PUT /time_entries/22' => 'timelog#update', :id => '22'
30 should_route 'PATCH /time_entries/22/edit' => 'timelog#edit', :id => '22'
31 should_route 'PATCH /time_entries/22' => 'timelog#update', :id => '22'
31 32 should_route 'DELETE /time_entries/22' => 'timelog#destroy', :id => '22'
32 33 end
33 34
34 35 def test_timelogs_scoped_under_project
35 36 should_route 'GET /projects/foo/time_entries' => 'timelog#index', :project_id => 'foo'
36 37 should_route 'GET /projects/foo/time_entries.csv' => 'timelog#index', :project_id => 'foo', :format => 'csv'
37 38 should_route 'GET /projects/foo/time_entries.atom' => 'timelog#index', :project_id => 'foo', :format => 'atom'
38 39 should_route 'GET /projects/foo/time_entries/new' => 'timelog#new', :project_id => 'foo'
39 40 should_route 'POST /projects/foo/time_entries' => 'timelog#create', :project_id => 'foo'
40 41 end
41 42
42 43 def test_timelogs_scoped_under_issues
43 44 should_route 'GET /issues/234/time_entries/new' => 'timelog#new', :issue_id => '234'
44 45 should_route 'POST /issues/234/time_entries' => 'timelog#create', :issue_id => '234'
45 46 end
46 47
47 48 def test_timelogs_report
48 49 should_route 'GET /time_entries/report' => 'timelog#report'
49 50 should_route 'GET /time_entries/report.csv' => 'timelog#report', :format => 'csv'
50 51
51 52 should_route 'GET /projects/foo/time_entries/report' => 'timelog#report', :project_id => 'foo'
52 53 should_route 'GET /projects/foo/time_entries/report.csv' => 'timelog#report', :project_id => 'foo', :format => 'csv'
53 54 end
54 55
55 56 def test_timelogs_bulk_edit
56 57 should_route 'GET /time_entries/bulk_edit' => 'timelog#bulk_edit'
57 58 should_route 'POST /time_entries/bulk_update' => 'timelog#bulk_update'
58 59 should_route 'DELETE /time_entries/destroy' => 'timelog#destroy'
59 60 end
60 61 end
General Comments 0
You need to be logged in to leave comments. Login now