Merge pull request #486 from opf/feature/migrate_issues_controller_tests

[FEATURE] Migrate issues controller tests
pull/478/merge
Martin Czuchra 11 years ago
commit e12d484392
  1. 7
      app/assets/javascripts/context_menu.js
  2. 15
      app/controllers/application_controller.rb
  3. 55
      app/controllers/work_package_bulk_controller.rb
  4. 1
      app/controllers/work_packages/context_menus_controller.rb
  5. 12
      app/helpers/application_helper.rb
  6. 52
      app/views/work_package_bulk/edit.html.erb
  7. 2
      app/views/work_packages/context_menus/index.html.erb
  8. 2
      config/locales/de.yml
  9. 2
      config/locales/en.yml
  10. 10
      config/routes.rb
  11. 2
      doc/CHANGELOG.md
  12. 3
      lib/redmine.rb
  13. 412
      spec/controllers/work_package_bulk_controller_spec.rb
  14. 22
      spec/controllers/work_packages/context_menus_controller_spec.rb
  15. 3
      spec/factories/custom_value_factory.rb
  16. 4
      spec/models/work_package/work_package_copy_spec.rb
  17. 37
      spec/permissions/work_packages_bulk_spec.rb
  18. 42
      spec/routing/work_package_bulk_spec.rb
  19. 282
      test/functional/issues_controller_test.rb
  20. 5
      test/integration/routing_test.rb

@ -140,11 +140,14 @@ ContextMenu.prototype = {
$('context-menu').style['top'] = (render_y + 'px');
Element.update('context-menu', '');
new Ajax.Updater({success:'context-menu'}, this.url,
// some IE-versions only know the srcElement
var target = e.target ? e.target : e.srcElement;
new Ajax.Updater({success:'context-menu'}, this.url,
{asynchronous:true,
method: 'get',
evalScripts:true,
parameters:jQuery(e.srcElement).closest("form").serialize(),
parameters: jQuery(target).closest("form").serialize(),
onComplete:function(request){
dims = $('context-menu').getDimensions();
menu_width = dims.width;

@ -571,21 +571,6 @@ class ApplicationController < ActionController::Base
flash[:warning] = l(:warning_attachments_not_saved, obj.unsaved_attachments.size) if obj.unsaved_attachments.present?
end
# Sets the `flash` notice or error based the number of issues that did not save
#
# @param [Array, Issue] issues all of the saved and unsaved Issues
# @param [Array, Integer] unsaved_issue_ids the issue ids that were not saved
def set_flash_from_bulk_issue_save(issues, unsaved_issue_ids)
if unsaved_issue_ids.empty?
flash[:notice] = l(:notice_successful_update) unless issues.empty?
else
flash[:error] = l(:notice_failed_to_save_work_packages,
:count => unsaved_issue_ids.size,
:total => issues.size,
:ids => '#' + unsaved_issue_ids.join(', #'))
end
end
# Rescues an invalid query statement. Just in case...
def query_statement_invalid(exception)
logger.error "Query::StatementInvalid: #{exception.message}" if logger

@ -27,9 +27,9 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
class IssuesController < ApplicationController
class WorkPackageBulkController < ApplicationController
before_filter :disable_api
before_filter :find_issues, :only => [:bulk_edit, :bulk_update]
before_filter :find_work_packages, only: [:edit, :update]
before_filter :authorize
include JournalsHelper
@ -39,42 +39,55 @@ class IssuesController < ApplicationController
include QueriesHelper
include IssuesHelper
# Bulk edit a set of issues
def bulk_edit
@issues.sort!
def edit
@work_packages.sort!
@available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
@custom_fields = @projects.map{|p|p.all_work_package_custom_fields}.inject{|memo,c|memo & c}
@assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
@types = @projects.map(&:types).inject{|memo,t| memo & t}
end
def bulk_update
@issues.sort!
attributes = parse_params_for_bulk_issue_attributes(params)
def update
@work_packages.sort!
attributes = parse_params_for_bulk_work_package_attributes(params)
unsaved_issue_ids = []
@issues.each do |issue|
issue.reload
issue.add_journal(User.current, params[:notes])
issue.safe_attributes = attributes
call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
unsaved_work_package_ids = []
@work_packages.each do |work_package|
work_package.reload
work_package.add_journal(User.current, params[:notes])
work_package.safe_attributes = attributes
call_hook(:controller_work_package_bulk_before_save, { params: params, work_package: work_package })
JournalObserver.instance.send_notification = params[:send_notification] == '0' ? false : true
unless issue.save
# Keep unsaved issue ids to display them in flash error
unsaved_issue_ids << issue.id
unless work_package.save
unsaved_work_package_ids << work_package.id
end
end
set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
redirect_back_or_default({:controller => '/work_packages', :action => 'index', :project_id => @project})
set_flash_from_bulk_save(@work_packages, unsaved_work_package_ids)
redirect_back_or_default({controller: '/work_packages', action: :index, project_id: @project})
end
private
def parse_params_for_bulk_issue_attributes(params)
attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
def parse_params_for_bulk_work_package_attributes(params)
attributes = (params[:work_package] || {}).reject {|k,v| v.blank?}
attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
attributes.delete :custom_field_values if not attributes.has_key?(:custom_field_values) or attributes[:custom_field_values].empty?
attributes
end
# Sets the `flash` notice or error based the number of work packages that did not save
#
# @param [Array, WorkPackage] work_packages all of the saved and unsaved WorkPackages
# @param [Array, Integer] unsaved_work_package_ids the WorkPackage ids that were not saved
def set_flash_from_bulk_save(work_packages, unsaved_work_package_ids)
if unsaved_work_package_ids.empty?
flash[:notice] = l(:notice_successful_update) unless work_packages.empty?
else
flash[:error] = l(:notice_failed_to_save_work_packages,
:count => unsaved_work_package_ids.size,
:total => work_packages.size,
:ids => '#' + unsaved_work_package_ids.join(', #'))
end
end
end

@ -44,6 +44,7 @@ class WorkPackages::ContextMenusController < ApplicationController
memo & s
end
end
@projects = @work_packages.collect(&:project).compact.uniq
@project = @projects.first if @projects.size == 1

@ -955,12 +955,12 @@ module ApplicationHelper
ret += content_tag :ul do
args[:collection].collect do |(s, name)|
content_tag :li do
context_menu_link (name || s), bulk_update_issues_path(:ids => args[:updated_object_ids],
:issue => { db_attribute => s },
:back_url => args[:back_url]),
:method => :put,
:selected => args[:selected].call(s),
:disabled => args[:disabled].call(s)
context_menu_link (name || s), work_package_bulk_update_path(:ids => args[:updated_object_ids],
:work_package => { db_attribute => s },
:back_url => args[:back_url]),
:method => :put,
:selected => args[:selected].call(s),
:disabled => args[:disabled].call(s)
end
end.join.html_safe
end

@ -27,41 +27,41 @@ See doc/COPYRIGHT.rdoc for more details.
++#%>
<h2><%= l(:label_bulk_edit_selected_issues) %></h2>
<h2><%= l(:label_bulk_edit_selected_work_packages) %></h2>
<ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.type} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n").html_safe %></ul>
<ul><%= @work_packages.collect {|i| content_tag('li', link_to(h("#{i.type} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n").html_safe %></ul>
<%= form_tag(:action => 'bulk_update') do %>
<%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join.html_safe %>
<%= form_tag(:action => 'update') do %>
<%= @work_packages.collect {|i| hidden_field_tag('ids[]', i.id)}.join.html_safe %>
<div class="box tabular">
<fieldset class="attributes">
<legend><%= l(:label_change_properties) %></legend>
<div class="splitcontentleft">
<p>
<label for="issue_type_id"><%= WorkPackage.human_attribute_name(:type) %></label>
<%= select_tag('issue[type_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@types, :id, :name)) %>
<label for="work_package_type_id"><%= WorkPackage.human_attribute_name(:type) %></label>
<%= select_tag('work_package[type_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>".html_safe + options_from_collection_for_select(@types, :id, :name)) %>
</p>
<% if @available_statuses.any? %>
<p>
<label for='status_id'><%= WorkPackage.human_attribute_name(:status) %></label>
<%= select_tag('issue[status_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
<%= select_tag('work_package[status_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>".html_safe + options_from_collection_for_select(@available_statuses, :id, :name)) %>
</p>
<% end %>
<p>
<label for='issue_priority_id'><%= WorkPackage.human_attribute_name(:priority) %></label>
<%= select_tag('issue[priority_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
<label for='work_package_priority_id'><%= WorkPackage.human_attribute_name(:priority) %></label>
<%= select_tag('work_package[priority_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>".html_safe + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
</p>
<p>
<label for='issue_assigned_to_id'><%= WorkPackage.human_attribute_name(:assigned_to) %></label>
<%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
<label for='work_package_assigned_to_id'><%= WorkPackage.human_attribute_name(:assigned_to) %></label>
<%= select_tag('work_package[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@assignables, :id, :name)) %>
</p>
<% if @project %>
<p>
<label for='category_id'><%= WorkPackage.human_attribute_name(:category) %></label>
<%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
<%= select_tag('work_package[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
options_from_collection_for_select(@project.categories, :id, :name)) %>
</p>
@ -69,8 +69,8 @@ See doc/COPYRIGHT.rdoc for more details.
<% #TODO: allow editing versions when multiple projects %>
<% if @project %>
<p>
<label for='issue_fixed_version_id'><%= WorkPackage.human_attribute_name(:fixed_version) %></label>
<%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
<label for='work_package_fixed_version_id'><%= WorkPackage.human_attribute_name(:fixed_version) %></label>
<%= select_tag('work_package[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
version_options_for_select(@project.shared_versions.open.sort)) %>
</p>
@ -78,35 +78,35 @@ See doc/COPYRIGHT.rdoc for more details.
<% @custom_fields.each do |custom_field| %>
<p>
<%= blank_custom_field_label_tag('issue', custom_field) %>
<%= custom_field_tag_for_bulk_edit('issue', custom_field) %>
<%= blank_custom_field_label_tag('work_package', custom_field) %>
<%= custom_field_tag_for_bulk_edit('work_package', custom_field) %>
</p>
<% end %>
<%= call_hook(:view_work_packages_bulk_edit_details_bottom, { :issues => @issues }) %>
<%= call_hook(:view_work_packages_bulk_edit_details_bottom, { work_packages: @work_packages }) %>
</div>
<div class="splitcontentright">
<% if @project && User.current.allowed_to?(:manage_subtasks, @project) %>
<p>
<label for='issue_parent_id'><%= WorkPackage.human_attribute_name(:parent_issue) %></label>
<%= text_field_tag 'issue[parent_id]', '', :size => 10 %>
<label for='work_package_parent_id'><%= WorkPackage.human_attribute_name(:parent_work_package) %></label>
<%= text_field_tag 'work_package[parent_id]', '', :size => 10 %>
</p>
<div id="parent_issue_candidates" class="autocomplete"></div>
<div id="parent_work_package_candidates" class="autocomplete"></div>
<%= javascript_tag "observeParentIssueField('#{work_packages_auto_complete_path(project_id: @project.id)}')" %>
<% end %>
<p>
<label for='issue_start_date'><%= WorkPackage.human_attribute_name(:start_date) %></label>
<%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
<label for='work_package_start_date'><%= WorkPackage.human_attribute_name(:start_date) %></label>
<%= text_field_tag 'work_package[start_date]', '', :size => 10 %><%= calendar_for('work_package_start_date') %>
</p>
<p>
<label for='issue_due_date'><%= WorkPackage.human_attribute_name(:due_date) %></label>
<%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
<label for='work_package_due_date'><%= WorkPackage.human_attribute_name(:due_date) %></label>
<%= text_field_tag 'work_package[due_date]', '', :size => 10 %><%= calendar_for('work_package_due_date') %>
</p>
<% if WorkPackage.use_field_for_done_ratio? %>
<p>
<label for='issue_done_ratio'><%= WorkPackage.human_attribute_name(:done_ratio) %></label>
<%= select_tag 'issue[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
<label for='work_package_done_ratio'><%= WorkPackage.human_attribute_name(:done_ratio) %></label>
<%= select_tag 'work_package[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
</p>
<% end %>
</div>

@ -38,7 +38,7 @@ See doc/COPYRIGHT.rdoc for more details.
</li>
<% else %>
<li class="edit">
<%= context_menu_link l(:button_edit), bulk_edit_issues_path(:ids => @work_packages.collect(&:id)),
<%= context_menu_link l(:button_edit), work_package_bulk_edit_path(:ids => @work_packages.collect(&:id)),
:class => 'icon-edit',
:disabled => !@can[:edit] %>
</li>

@ -611,7 +611,7 @@ de:
label_boolean: "Boolean"
label_branch: "Zweig"
label_browse: "Codebrowser"
label_bulk_edit_selected_issues: "Alle ausgewählten Tickets bearbeiten"
label_bulk_edit_selected_work_packages: "Alle ausgewählten Arbeitspakete bearbeiten"
label_calendar: "Kalender"
label_calendar_show: "Kalender anzeigen"
label_category: "Kategorie"

@ -593,7 +593,7 @@ en:
label_boolean: "Boolean"
label_branch: "Branch"
label_browse: "Browse"
label_bulk_edit_selected_issues: "Bulk edit selected issues"
label_bulk_edit_selected_work_packages: "Bulk edit selected work packages"
label_calendar: "Calendar"
label_calendar_show: "Show Calendar"
label_category: "Category"

@ -290,11 +290,6 @@ OpenProject::Application.routes.draw do
resources :time_entries, :controller => 'timelog'
resources :relations, :controller => 'relations', :only => [:create, :destroy]
collection do
get :bulk_edit, :format => false
put :bulk_update, :format => false
end
end
namespace :work_packages do
@ -303,6 +298,11 @@ OpenProject::Application.routes.draw do
resources :calendar, :controller => 'calendars', :only => [:index]
end
namespace :work_package_bulk do
get :edit, :format => false
put :update, :format => false
end
resources :work_packages, :only => [:show, :edit, :update, :index] do
get :new_type, :on => :member
put :preview, :on => :member

@ -30,8 +30,10 @@ See doc/COPYRIGHT.rdoc for more details.
# Changelog
* `#1281` I18n.js Not working correctly. Always returns English Translations
* `#1758` Migrate functional-tests for issues into specs for work package
* `#1771` Fixed bug: Refactor Types Project Settings into new Tab
* `#2158` Work Package General Setting
* `#2306` Migrate issues controller tests
* `#2307` Change icon of home button in header from OpenProjct icon to house icon
* `#2310` Add proper indices to work_package
* `#2319` Add a request-store to avoid redundant calls

@ -108,7 +108,8 @@ Redmine::AccessControl.map do |map|
:'issues/previews' => :create,
:work_packages => [:new, :new_type, :preview, :create] }
map.permission :move_work_packages, {:'work_packages/moves' => [:new, :create]}, :require => :loggedin
map.permission :edit_work_packages, { :issues => [:edit, :update, :bulk_edit, :bulk_update, :update_form],
map.permission :edit_work_packages, { :issues => [:edit, :update, :update_form],
:work_package_bulk => [:edit, :update],
:work_packages => [:edit, :update, :new_type, :preview, :quoted],
:journals => :preview,
:planning_elements => [:new, :create, :edit, :update],

@ -0,0 +1,412 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe WorkPackageBulkController do
let(:user) { FactoryGirl.create(:user) }
let(:custom_field_value) { '125' }
let(:custom_field_1) { FactoryGirl.create(:work_package_custom_field,
field_format: 'string',
is_for_all: true) }
let(:custom_field_2) { FactoryGirl.create(:work_package_custom_field) }
let(:status) { FactoryGirl.create(:status) }
let(:type) { FactoryGirl.create(:type_standard,
custom_fields: [custom_field_1, custom_field_2]) }
let(:project_1) { FactoryGirl.create(:project,
types: [type],
work_package_custom_fields: [custom_field_2]) }
let(:project_2) { FactoryGirl.create(:project,
types: [type]) }
let(:role) { FactoryGirl.create(:role,
permissions: [:edit_work_packages,
:view_work_packages,
:manage_subtasks]) }
let(:member_1) { FactoryGirl.create(:member,
project: project_1,
principal: user,
roles: [role]) }
let(:member_2) { FactoryGirl.create(:member,
project: project_2,
principal: user,
roles: [role]) }
let(:work_package_1) { FactoryGirl.create(:work_package,
author: user,
assigned_to: user,
type: type,
status: status,
custom_field_values: { custom_field_1.id => custom_field_value },
project: project_1) }
let(:work_package_2) { FactoryGirl.create(:work_package,
author: user,
assigned_to: user,
type: type,
status: status,
custom_field_values: { custom_field_1.id => custom_field_value },
project: project_1) }
let(:work_package_3) { FactoryGirl.create(:work_package,
author: user,
type: type,
status: status,
custom_field_values: { custom_field_1.id => custom_field_value },
project: project_2) }
before do
custom_field_1
member_1
User.stub(:current).and_return user
end
describe :edit do
shared_examples_for :response do
subject { response }
it { should be_success }
it { should render_template('edit') }
end
context "same project" do
before { get :edit, ids: [work_package_1.id, work_package_2.id] }
it_behaves_like :response
describe :view do
render_views
subject { response }
describe :parent do
it { assert_tag :input, attributes: { name: 'work_package[parent_id]' } }
end
context :custom_field do
describe :type do
it { assert_tag :input, attributes: { name: "work_package[custom_field_values][#{custom_field_1.id}]" } }
end
describe :project do
it { assert_tag :select, attributes: { name: "work_package[custom_field_values][#{custom_field_2.id}]" } }
end
end
end
end
context "different projects" do
before do
member_2
get :edit, ids: [work_package_1.id, work_package_2.id, work_package_3.id]
end
it_behaves_like :response
describe :view do
render_views
subject { response }
describe :parent do
it { assert_no_tag :input, attributes: { name: 'work_package[parent_id]' } }
end
context :custom_field do
describe :type do
it { assert_tag :input, attributes: { name: "work_package[custom_field_values][#{custom_field_1.id}]" } }
end
describe :project do
it { assert_no_tag :select, attributes: { name: "work_package[custom_field_values][#{custom_field_2.id}]" } }
end
end
end
end
end
describe :update do
let(:work_package_ids) { [work_package_1.id, work_package_2.id] }
let(:work_packages) { WorkPackage.find_all_by_id(work_package_ids) }
let(:priority) { FactoryGirl.create(:priority_immediate) }
let(:group_id) { '' }
describe :redirect do
context "in host" do
let(:url) { '/work_packages' }
before { put :update, ids: work_package_ids, back_url: url }
subject { response }
it { should be_redirect }
it { should redirect_to(url) }
end
context "of host" do
let(:url) { 'http://google.com' }
before { put :update, ids: work_package_ids, back_url: url }
subject { response }
it { should be_redirect }
it { should redirect_to(controller: 'work_packages',
action: :index,
project_id: project_1.identifier) }
end
end
shared_context :update_request do
before do
put :update,
ids: work_package_ids,
notes: 'Bulk editing',
work_package: { priority_id: priority.id,
assigned_to_id: group_id,
custom_field_values: { custom_field_1.id.to_s => '' },
send_notification: send_notification }
end
end
shared_examples_for :delivered do
subject { ActionMailer::Base.deliveries.size }
it { delivery_size }
end
context "with notification" do
let(:send_notification) { '1' }
let(:delivery_size) { 2 }
shared_examples_for "updated work package" do
describe :priority do
subject { WorkPackage.find_all_by_priority_id(priority.id).collect(&:id) }
it { should =~ work_package_ids }
end
describe :custom_fields do
let(:result) { [custom_field_value] }
subject { WorkPackage.find_all_by_id(work_package_ids)
.collect {|w| w.custom_value_for(custom_field_1.id).value }
.uniq }
it { should =~ result }
end
describe :journal do
describe :notes do
let(:result) { ['Bulk editing'] }
subject { WorkPackage.find_all_by_id(work_package_ids)
.collect {|w| w.last_journal.notes }
.uniq }
it { should =~ result }
end
describe :details do
let(:result) { [1] }
subject { WorkPackage.find_all_by_id(work_package_ids)
.collect {|w| w.last_journal.details.size }
.uniq }
it { should =~ result }
end
end
end
context "single project" do
include_context :update_request
it { response.response_code.should == 302 }
it_behaves_like :delivered
it_behaves_like "updated work package"
end
context "different projects" do
let(:work_package_ids) { [work_package_1.id, work_package_2.id, work_package_3.id] }
context "with permission" do
before { member_2 }
include_context :update_request
it { response.response_code.should == 302 }
it_behaves_like :delivered
it_behaves_like "updated work package"
end
context "w/o permission" do
include_context :update_request
it { response.response_code.should == 403 }
describe :journal do
subject { Journal.count }
it { should eq(work_package_ids.count) }
end
end
end
describe :properties do
describe :groups do
let(:group) { FactoryGirl.create(:group) }
let(:group_id) { group.id }
include_context :update_request
subject { work_packages.collect {|w| w.assigned_to_id }.uniq }
it { should =~ [group_id] }
end
describe :status do
let(:closed_status) { FactoryGirl.create(:closed_status) }
let(:workflow) { FactoryGirl.create(:workflow,
role: role,
type_id: type.id,
old_status: status,
new_status: closed_status) }
before do
workflow
put :update,
ids: work_package_ids,
work_package: { status_id: closed_status.id }
end
subject { work_packages.collect(&:status_id).uniq }
it { should =~ [closed_status.id] }
end
describe :parent do
let(:parent) { FactoryGirl.create(:work_package,
author: user,
project: project_1) }
before do
put :update,
ids: work_package_ids,
work_package: { parent_id: parent.id }
end
subject { work_packages.collect(&:parent_id).uniq }
it { should =~ [parent.id] }
end
describe :custom_fields do
let(:result) { '777' }
before do
put :update,
ids: work_package_ids,
work_package: { custom_field_values: { custom_field_1.id.to_s => result } }
end
subject { work_packages.collect {|w| w.custom_value_for(custom_field_1.id).value }
.uniq }
it { should =~ [result] }
end
describe :unassign do
before do
put :update,
ids: work_package_ids,
work_package: { assigned_to_id: 'none' }
end
subject { work_packages.collect(&:assigned_to_id).uniq }
it { should =~ [nil] }
end
describe :version do
let(:version) { FactoryGirl.create(:version,
status: 'open',
sharing: 'tree',
project: subproject) }
let(:subproject) { FactoryGirl.create(:project,
parent: project_1,
types: [type]) }
before do
put :update,
ids: work_package_ids,
work_package: { fixed_version_id: version.id.to_s }
end
subject { response }
it { should be_redirect }
describe :work_package do
describe :fixed_version do
subject { work_packages.collect(&:fixed_version_id).uniq }
it { should =~ [version.id] }
end
describe :project do
subject { work_packages.collect(&:project_id).uniq }
it { should_not =~ [subproject.id] }
end
end
end
end
end
context "w/o notification" do
let(:send_notification) { '0' }
describe :delivery do
include_context :update_request
it { response.response_code.should == 302 }
let(:delivery_size) { 0 }
it_behaves_like :delivered
end
end
end
end

@ -89,7 +89,7 @@ describe WorkPackages::ContextMenusController do
end
shared_examples_for :bulk_edit do
let(:edit_link) { "/issues/bulk_edit?#{ids_link}" }
let(:edit_link) { "/work_package_bulk/edit?#{ids_link}" }
it_behaves_like :edit_impl
end
@ -126,8 +126,8 @@ describe WorkPackages::ContextMenusController do
get :index, ids: ids
end
let(:status_link) { "/issues/bulk_update?#{ids_link}"\
"&amp;issue%5Bstatus_id%5D=#{status_2.id}" }
let(:status_link) { "/work_package_bulk/update?#{ids_link}"\
"&amp;work_package%5Bstatus_id%5D=#{status_2.id}" }
it do
assert_tag tag: 'a',
@ -139,8 +139,8 @@ describe WorkPackages::ContextMenusController do
shared_examples_for :priority do
let(:priority_immediate) { FactoryGirl.create(:priority_immediate) }
let(:priority_link) { "/issues/bulk_update?#{ids_link}"\
"&amp;issue%5Bpriority_id%5D=#{priority_immediate.id}" }
let(:priority_link) { "/work_package_bulk/update?#{ids_link}"\
"&amp;work_package%5Bpriority_id%5D=#{priority_immediate.id}" }
before do
priority_immediate
@ -161,10 +161,10 @@ describe WorkPackages::ContextMenusController do
project: project_1) }
let(:version_2) { FactoryGirl.create(:version,
project: project_1) }
let(:version_link_1) { "/issues/bulk_update?#{ids_link}"\
"&amp;issue%5Bfixed_version_id%5D=#{version_1.id}" }
let(:version_link_2) { "/issues/bulk_update?#{ids_link}"\
"&amp;issue%5Bfixed_version_id%5D=#{version_2.id}" }
let(:version_link_1) { "/work_package_bulk/update?#{ids_link}"\
"&amp;work_package%5Bfixed_version_id%5D=#{version_1.id}" }
let(:version_link_2) { "/work_package_bulk/update?#{ids_link}"\
"&amp;work_package%5Bfixed_version_id%5D=#{version_2.id}" }
before do
version_1
@ -182,8 +182,8 @@ describe WorkPackages::ContextMenusController do
end
shared_examples_for :assigned_to do
let(:assigned_to_link) { "/issues/bulk_update?#{ids_link}"\
"&amp;issue%5Bassigned_to_id%5D=#{user.id}" }
let(:assigned_to_link) { "/work_package_bulk/update?#{ids_link}"\
"&amp;work_package%5Bassigned_to_id%5D=#{user.id}" }
before { get :index, ids: ids }

@ -42,7 +42,8 @@ FactoryGirl.define do
end
factory :work_package_custom_value do
custom_field :factory => :issue_custom_field
custom_field :factory => :work_package_custom_field
customized_type "WorkPackageCustomField"
customized :factory => :work_package
end
end

@ -31,7 +31,7 @@ require 'spec_helper'
describe WorkPackage do
describe :copy do
let(:user) { FactoryGirl.create(:user) }
let (:custom_field) { FactoryGirl.create(:work_package_custom_field) }
let(:custom_field) { FactoryGirl.create(:work_package_custom_field) }
let(:source_type) { FactoryGirl.create(:type,
custom_fields: [custom_field]) }
let(:source_project) { FactoryGirl.create(:project,
@ -199,7 +199,7 @@ describe WorkPackage do
let(:type) { FactoryGirl.create(:type_standard) }
let(:project) { FactoryGirl.create(:project, types: [type]) }
let (:custom_field) { FactoryGirl.create(:work_package_custom_field,
let(:custom_field) { FactoryGirl.create(:work_package_custom_field,
name: 'Database',
field_format: 'list',
possible_values: ['MySQL', 'PostgreSQL', 'Oracle'],

@ -0,0 +1,37 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require_relative '../support/permission_specs'
describe WorkPackageBulkController, "edit_work_packages permission", type: :controller do
include PermissionSpecs
check_permission_required_for('work_package_bulk#edit', :edit_work_packages)
check_permission_required_for('work_package_bulk#update', :edit_work_packages)
end

@ -0,0 +1,42 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe WorkPackageBulkController do
it "should connect GET /work_package_bulk/edit to work_package_bulk/edit" do
get("/work_package_bulk/edit").should route_to(controller: 'work_package_bulk',
action: 'edit')
end
it "should connect PUT /work_package_bulk/update to work_package_bulk#update" do
put("/work_package_bulk/update").should route_to(controller: 'work_package_bulk',
action: 'update')
end
end

@ -1,282 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require File.expand_path('../../test_helper', __FILE__)
require 'issues_controller'
# Re-raise errors caught by the controller.
class IssuesController; def rescue_action(e) raise e end; end
class IssuesControllerTest < ActionController::TestCase
fixtures :all
def setup
super
@controller = IssuesController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
User.current = nil
end
def test_get_bulk_edit
@request.session[:user_id] = 2
get :bulk_edit, :ids => [1, 2]
assert_response :success
assert_template 'bulk_edit'
assert_tag :input, :attributes => {:name => 'issue[parent_id]'}
# Project specific custom field, date type
field = CustomField.find(9)
assert !field.is_for_all?
assert_equal 'date', field.field_format
assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
# System wide custom field
assert CustomField.find(1).is_for_all?
assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
end
def test_get_bulk_edit_on_different_projects
@request.session[:user_id] = 2
get :bulk_edit, :ids => [1, 2, 6]
assert_response :success
assert_template 'bulk_edit'
# Can not set issues from different projects as children of an issue
assert_no_tag :input, :attributes => {:name => 'issue[parent_id]'}
# Project specific custom field, date type
field = CustomField.find(9)
assert !field.is_for_all?
assert !field.project_ids.include?(WorkPackage.find(6).project_id)
assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
end
def test_bulk_update
issue = WorkPackage.find(1)
issue.recreate_initial_journal!
@request.session[:user_id] = 2
# update issues priority
put :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
:issue => { :priority_id => 7,
:assigned_to_id => '',
:custom_field_values => {'2' => ''} }
assert_response 302
# check that the issues were updated
assert_equal [7, 7], WorkPackage.find_all_by_id([1, 2]).collect {|i| i.priority.id}
issue.reload
journal = issue.journals.reorder('created_at DESC').first
assert_equal '125', issue.custom_value_for(2).value
assert_equal 'Bulk editing', journal.notes
assert_equal 1, journal.details.size
end
def test_bullk_update_should_not_send_a_notification_if_send_notification_is_off
@request.session[:user_id] = 2
ActionMailer::Base.deliveries.clear
put(:bulk_update,
{
:ids => [1, 2],
:issue => {
:priority_id => 7,
:assigned_to_id => '',
:custom_field_values => {'2' => ''}
},
:notes => 'Bulk editing',
:send_notification => '0'
})
assert_response 302
assert_equal 0, ActionMailer::Base.deliveries.size
end
def test_bulk_update_with_group_assignee
group = Group.find(11)
project = Project.find(1)
project.members << Member.new(:principal => group, :roles => [Role.first])
@request.session[:user_id] = 2
# update issues assignee
post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
:issue => {:priority_id => '',
:assigned_to_id => group.id,
:custom_field_values => {'2' => ''}}
assert_response 302
assert_equal [group, group], WorkPackage.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
end
def test_bulk_update_on_different_projects
issue = WorkPackage.find(1)
issue.recreate_initial_journal!
@request.session[:user_id] = 2
# update issues priority
put :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
:issue => {:priority_id => 7,
:assigned_to_id => '',
:custom_field_values => {'2' => ''}}
assert_response 302
# check that the issues were updated
assert_equal [7, 7, 7], WorkPackage.find([1,2,6]).map(&:priority_id)
issue.reload
journal = issue.journals.reorder('created_at DESC').first
assert_equal '125', issue.custom_value_for(2).value
assert_equal 'Bulk editing', journal.notes
assert_equal 1, journal.details.size
end
def test_bulk_update_on_different_projects_without_rights
Journal.delete_all
@request.session[:user_id] = 3
user = User.find(3)
action = { :controller => "issues", :action => "bulk_update" }
assert user.allowed_to?(action, WorkPackage.find(1).project)
assert ! user.allowed_to?(action, WorkPackage.find(6).project)
put :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
:issue => {:priority_id => 7,
:assigned_to_id => '',
:custom_field_values => {'2' => ''}}
assert_response 403
assert Journal.all.empty?
end
def test_bulk_update_should_send_a_notification
WorkPackage.find(1).recreate_initial_journal!
WorkPackage.find(2).recreate_initial_journal!
@request.session[:user_id] = 2
ActionMailer::Base.deliveries.clear
put(:bulk_update,
{
:ids => [1, 2],
:notes => 'Bulk editing',
:issue => {
:priority_id => 7,
:assigned_to_id => '',
:custom_field_values => {'2' => ''}
}
})
assert_response 302
assert_equal 5, ActionMailer::Base.deliveries.size
end
def test_bulk_update_status
@request.session[:user_id] = 2
# update issues priority
put :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
:issue => {:priority_id => '',
:assigned_to_id => '',
:status_id => '5'}
assert_response 302
issue = WorkPackage.find(1)
assert issue.closed?
end
def test_bulk_update_parent_id
@request.session[:user_id] = 2
put :bulk_update, :ids => [1, 3],
:notes => 'Bulk editing parent',
:issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_id => '2'}
assert_response 302
parent = WorkPackage.find(2)
assert_equal parent.id, WorkPackage.find(1).parent_id
assert_equal parent.id, WorkPackage.find(3).parent_id
assert_equal [1, 3], parent.children.collect(&:id).sort
end
def test_bulk_update_custom_field
issue = WorkPackage.find(1)
issue.recreate_initial_journal!
@request.session[:user_id] = 2
# update issues priority
put :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
:issue => {:priority_id => '',
:assigned_to_id => '',
:custom_field_values => {'2' => '777'}}
assert_response 302
issue.reload
journal = issue.journals.last
assert_equal '777', issue.custom_value_for(2).value
assert_equal 1, journal.details.size
assert_equal '125', journal.old_value_for(:custom_fields_2)
assert_equal '777', journal.new_value_for(:custom_fields_2)
end
def test_bulk_update_unassign
assert_not_nil WorkPackage.find(2).assigned_to
@request.session[:user_id] = 2
# unassign issues
put :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
assert_response 302
# check that the issues were updated
assert_nil WorkPackage.find(2).assigned_to
end
def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
@request.session[:user_id] = 2
put :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
assert_response :redirect
issues = WorkPackage.find([1,2])
issues.each do |issue|
assert_equal 4, issue.fixed_version_id
assert_not_equal issue.project_id, issue.fixed_version.project_id
end
end
def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
@request.session[:user_id] = 2
put :bulk_update, :ids => [1,2], :back_url => '/issues'
assert_response :redirect
assert_redirected_to '/issues'
end
def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
@request.session[:user_id] = 2
put :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
assert_response :redirect
assert_redirected_to :controller => 'work_packages', :action => 'index', :project_id => Project.find(1).identifier
end
end

@ -149,11 +149,6 @@ class RoutingTest < ActionDispatch::IntegrationTest
# Extra actions
should route(:get, "/issues/changes").to( :controller => 'journals',
:action => 'index')
should route(:get, "/issues/bulk_edit").to( :controller => 'issues',
:action => 'bulk_edit')
should route(:put, "/issues/bulk_update").to( :controller => 'issues',
:action => 'bulk_update')
end

Loading…
Cancel
Save