Rewrite the Gantt chart. #6276

This version of the Gantt chart supports nested charts. So Projects,
Versions, and Issues will be nested underneath their parents correctly.

Additional features:

* Move all Gantt code to Redmine::Helpers::Gantt class instead of having it in
  the Gantt class, controller, and view
* Recursive and nest sub-projects
* Recursive and nest versions
* Recursive and nest issues
* Draw a line showing when a Project is active and it's progress
* Draw a line showing when a Version is active and it's progress
* Show a version's % complete
* Change the color of Projects, Versions, and Issues if they are late or
  behind schedule
* Added Project#start_date and #due_date
* Added Project#completed_percent
* Use a mini-gravatar on the Gantt chart
* Added tests for the Gantt rendering

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@4072 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Eric Davis
2010-09-10 03:09:02 +00:00
parent 8d52608dba
commit bdb3937e0f
29 changed files with 1879 additions and 403 deletions
+8 -8
View File
@@ -5,20 +5,20 @@ class GanttsControllerTest < ActionController::TestCase
context "#gantt" do
should "work" do
i2 = Issue.find(2)
i2.update_attribute(:due_date, 1.month.from_now)
get :show, :project_id => 1
assert_response :success
assert_template 'show.html.erb'
assert_not_nil assigns(:gantt)
events = assigns(:gantt).events
assert_not_nil events
# Issue with start and due dates
i = Issue.find(1)
assert_not_nil i.due_date
assert events.include?(Issue.find(1))
# Issue with without due date but targeted to a version with date
assert_select "div a.issue", /##{i.id}/
# Issue with on a targeted version should not be in the events but loaded in the html
i = Issue.find(2)
assert_nil i.due_date
assert events.include?(i)
assert_select "div a.issue", /##{i.id}/
end
should "work cross project" do
@@ -26,8 +26,8 @@ class GanttsControllerTest < ActionController::TestCase
assert_response :success
assert_template 'show.html.erb'
assert_not_nil assigns(:gantt)
events = assigns(:gantt).events
assert_not_nil events
assert_not_nil assigns(:gantt).query
assert_nil assigns(:gantt).project
end
should "export to pdf" do
+2 -1
View File
@@ -25,8 +25,9 @@ module ObjectDaddyHelpers
def Issue.generate_for_project!(project, attributes={})
issue = Issue.spawn(attributes) do |issue|
issue.project = project
issue.tracker = project.trackers.first unless project.trackers.empty?
yield issue if block_given?
end
issue.tracker = project.trackers.first unless project.trackers.empty?
issue.save!
issue
end
+1 -1
View File
@@ -601,7 +601,7 @@ EXPECTED
# turn off avatars
Setting.gravatar_enabled = '0'
assert_nil avatar(User.find_by_mail('jsmith@somenet.foo'))
assert_equal '', avatar(User.find_by_mail('jsmith@somenet.foo'))
end
def test_link_to_user
+22
View File
@@ -510,6 +510,28 @@ class IssueTest < ActiveSupport::TestCase
assert !Issue.new(:due_date => nil).overdue?
assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
end
context "#behind_schedule?" do
should "be false if the issue has no start_date" do
assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
end
should "be false if the issue has no end_date" do
assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
end
should "be false if the issue has more done than it's calendar time" do
assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
end
should "be true if the issue hasn't been started at all" do
assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
end
should "be true if the issue has used more calendar time than it's done ratio" do
assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
end
end
def test_assignable_users
assert_kind_of User, Issue.find(1).assignable_users.first
+703
View File
@@ -0,0 +1,703 @@
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../../../test_helper'
class Redmine::Helpers::GanttTest < ActiveSupport::TestCase
# Utility methods and classes so assert_select can be used.
class GanttViewTest < ActionView::Base
include ActionView::Helpers::UrlHelper
include ActionView::Helpers::TextHelper
include ActionController::UrlWriter
include ApplicationHelper
include ProjectsHelper
include IssuesHelper
def self.default_url_options
{:only_path => true }
end
end
include ActionController::Assertions::SelectorAssertions
def setup
@response = ActionController::TestResponse.new
# Fixtures
ProjectCustomField.delete_all
Project.destroy_all
User.current = User.find(1)
end
def build_view
@view = GanttViewTest.new
end
def html_document
HTML::Document.new(@response.body)
end
# Creates a Gantt chart for a 4 week span
def create_gantt(project=Project.generate!)
@project = project
@gantt = Redmine::Helpers::Gantt.new
@gantt.project = @project
@gantt.query = Query.generate_default!(:project => @project)
@gantt.view = build_view
@gantt.instance_variable_set('@date_from', 2.weeks.ago.to_date)
@gantt.instance_variable_set('@date_to', 2.weeks.from_now.to_date)
end
context "#number_of_rows" do
context "with one project" do
should "return the number of rows just for that project"
end
context "with no project" do
should "return the total number of rows for all the projects, resursively"
end
end
context "#number_of_rows_on_project" do
setup do
create_gantt
end
should "clear the @query.project so cross-project issues and versions can be counted" do
assert @gantt.query.project
@gantt.number_of_rows_on_project(@project)
assert_nil @gantt.query.project
end
should "count 1 for the project itself" do
assert_equal 1, @gantt.number_of_rows_on_project(@project)
end
should "count the number of issues without a version" do
@project.issues << Issue.generate_for_project!(@project, :fixed_version => nil)
assert_equal 2, @gantt.number_of_rows_on_project(@project)
end
should "count the number of versions" do
@project.versions << Version.generate!
@project.versions << Version.generate!
assert_equal 3, @gantt.number_of_rows_on_project(@project)
end
should "count the number of issues on versions, including cross-project" do
version = Version.generate!
@project.versions << version
@project.issues << Issue.generate_for_project!(@project, :fixed_version => version)
assert_equal 3, @gantt.number_of_rows_on_project(@project)
end
should "recursive and count the number of rows on each subproject" do
@project.versions << Version.generate! # +1
@subproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1
@subproject.set_parent!(@project)
@subproject.issues << Issue.generate_for_project!(@subproject) # +1
@subproject.issues << Issue.generate_for_project!(@subproject) # +1
@subsubproject = Project.generate!(:enabled_module_names => ['issue_tracking']) # +1
@subsubproject.set_parent!(@subproject)
@subsubproject.issues << Issue.generate_for_project!(@subsubproject) # +1
assert_equal 7, @gantt.number_of_rows_on_project(@project) # +1 for self
end
end
# TODO: more of an integration test
context "#subjects" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => 1.week.from_now.to_date, :sharing => 'none')
@project.versions << @version
@issue = Issue.generate!(:fixed_version => @version,
:subject => "gantt#line_for_project",
:tracker => @tracker,
:project => @project,
:done_ratio => 30,
:start_date => Date.yesterday,
:due_date => 1.week.from_now.to_date)
@project.issues << @issue
@response.body = @gantt.subjects
end
context "project" do
should "be rendered" do
assert_select "div.project-name a", /#{@project.name}/
end
should "have an indent of 4" do
assert_select "div.project-name[style*=left:4px]"
end
end
context "version" do
should "be rendered" do
assert_select "div.version-name a", /#{@version.name}/
end
should "be indented 24 (one level)" do
assert_select "div.version-name[style*=left:24px]"
end
end
context "issue" do
should "be rendered" do
assert_select "div.issue-subject", /#{@issue.subject}/
end
should "be indented 44 (two levels)" do
assert_select "div.issue-subject[style*=left:44px]"
end
end
end
context "#lines" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => 1.week.from_now.to_date)
@project.versions << @version
@issue = Issue.generate!(:fixed_version => @version,
:subject => "gantt#line_for_project",
:tracker => @tracker,
:project => @project,
:done_ratio => 30,
:start_date => Date.yesterday,
:due_date => 1.week.from_now.to_date)
@project.issues << @issue
@response.body = @gantt.lines
end
context "project" do
should "be rendered" do
assert_select "div.project_todo"
assert_select "div.project-line.starting"
assert_select "div.project-line.ending"
assert_select "div.label.project-name", /#{@project.name}/
end
end
context "version" do
should "be rendered" do
assert_select "div.milestone_todo"
assert_select "div.milestone.starting"
assert_select "div.milestone.ending"
assert_select "div.label.version-name", /#{@version.name}/
end
end
context "issue" do
should "be rendered" do
assert_select "div.task_todo"
assert_select "div.label.issue-name", /#{@issue.done_ratio}/
assert_select "div.tooltip", /#{@issue.subject}/
end
end
end
context "#render_project" do
should "be tested"
end
context "#render_issues" do
should "be tested"
end
context "#render_version" do
should "be tested"
end
context "#subject_for_project" do
setup do
create_gantt
end
context ":html format" do
should "add an absolute positioned div" do
@response.body = @gantt.subject_for_project(@project, {:format => :html})
assert_select "div[style*=absolute]"
end
should "use the indent option to move the div to the right" do
@response.body = @gantt.subject_for_project(@project, {:format => :html, :indent => 40})
assert_select "div[style*=left:40]"
end
should "include the project name" do
@response.body = @gantt.subject_for_project(@project, {:format => :html})
assert_select 'div', :text => /#{@project.name}/
end
should "include a link to the project" do
@response.body = @gantt.subject_for_project(@project, {:format => :html})
assert_select 'a[href=?]', "/projects/#{@project.identifier}", :text => /#{@project.name}/
end
should "style overdue projects" do
@project.enabled_module_names = [:issue_tracking]
@project.versions << Version.generate!(:effective_date => Date.yesterday)
assert @project.overdue?, "Need an overdue project for this test"
@response.body = @gantt.subject_for_project(@project, {:format => :html})
assert_select 'div span.project-overdue'
end
end
should "test the PNG format"
should "test the PDF format"
end
context "#line_for_project" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => Date.yesterday)
@project.versions << @version
@project.issues << Issue.generate!(:fixed_version => @version,
:subject => "gantt#line_for_project",
:tracker => @tracker,
:project => @project,
:done_ratio => 30,
:start_date => Date.yesterday,
:due_date => 1.week.from_now.to_date)
end
context ":html format" do
context "todo line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_todo[style*=left:52px]"
end
should "be the total width of the project" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_todo[style*=width:31px]"
end
end
context "late line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_late[style*=left:52px]"
end
should "be the total delayed width of the project" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_late[style*=width:6px]"
end
end
context "done line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_done[style*=left:52px]"
end
should "Be the total done width of the project" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project_done[style*=left:52px]"
end
end
context "starting marker" do
should "not appear if the starting point is off the gantt chart" do
# Shift the date range of the chart
@gantt.instance_variable_set('@date_from', Date.today)
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-line.starting", false
end
should "appear at the starting point" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-line.starting[style*=left:52px]"
end
end
context "ending marker" do
should "not appear if the starting point is off the gantt chart" do
# Shift the date range of the chart
@gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date)
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-line.ending", false
end
should "appear at the end of the date range" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-line.ending[style*=left:84px]"
end
end
context "status content" do
should "appear at the far left, even if it's far in the past" do
@gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date)
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-name", /#{@project.name}/
end
should "show the project name" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-name", /#{@project.name}/
end
should "show the percent complete" do
@response.body = @gantt.line_for_project(@project, {:format => :html, :zoom => 4})
assert_select "div.project-name", /0%/
end
end
end
should "test the PNG format"
should "test the PDF format"
end
context "#subject_for_version" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => Date.yesterday)
@project.versions << @version
@project.issues << Issue.generate!(:fixed_version => @version,
:subject => "gantt#subject_for_version",
:tracker => @tracker,
:project => @project,
:start_date => Date.today)
end
context ":html format" do
should "add an absolute positioned div" do
@response.body = @gantt.subject_for_version(@version, {:format => :html})
assert_select "div[style*=absolute]"
end
should "use the indent option to move the div to the right" do
@response.body = @gantt.subject_for_version(@version, {:format => :html, :indent => 40})
assert_select "div[style*=left:40]"
end
should "include the version name" do
@response.body = @gantt.subject_for_version(@version, {:format => :html})
assert_select 'div', :text => /#{@version.name}/
end
should "include a link to the version" do
@response.body = @gantt.subject_for_version(@version, {:format => :html})
assert_select 'a[href=?]', Regexp.escape("/versions/show/#{@version.to_param}"), :text => /#{@version.name}/
end
should "style late versions" do
assert @version.overdue?, "Need an overdue version for this test"
@response.body = @gantt.subject_for_version(@version, {:format => :html})
assert_select 'div span.version-behind-schedule'
end
should "style behind schedule versions" do
assert @version.behind_schedule?, "Need a behind schedule version for this test"
@response.body = @gantt.subject_for_version(@version, {:format => :html})
assert_select 'div span.version-behind-schedule'
end
end
should "test the PNG format"
should "test the PDF format"
end
context "#line_for_version" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => 1.week.from_now.to_date)
@project.versions << @version
@project.issues << Issue.generate!(:fixed_version => @version,
:subject => "gantt#line_for_project",
:tracker => @tracker,
:project => @project,
:done_ratio => 30,
:start_date => Date.yesterday,
:due_date => 1.week.from_now.to_date)
end
context ":html format" do
context "todo line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_todo[style*=left:52px]"
end
should "be the total width of the version" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_todo[style*=width:31px]"
end
end
context "late line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_late[style*=left:52px]"
end
should "be the total delayed width of the version" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_late[style*=width:6px]"
end
end
context "done line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_done[style*=left:52px]"
end
should "Be the total done width of the version" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone_done[style*=left:52px]"
end
end
context "starting marker" do
should "not appear if the starting point is off the gantt chart" do
# Shift the date range of the chart
@gantt.instance_variable_set('@date_from', Date.today)
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone.starting", false
end
should "appear at the starting point" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone.starting[style*=left:52px]"
end
end
context "ending marker" do
should "not appear if the starting point is off the gantt chart" do
# Shift the date range of the chart
@gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date)
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone.ending", false
end
should "appear at the end of the date range" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.milestone.ending[style*=left:84px]"
end
end
context "status content" do
should "appear at the far left, even if it's far in the past" do
@gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date)
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.version-name", /#{@version.name}/
end
should "show the version name" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.version-name", /#{@version.name}/
end
should "show the percent complete" do
@response.body = @gantt.line_for_version(@version, {:format => :html, :zoom => 4})
assert_select "div.version-name", /30%/
end
end
end
should "test the PNG format"
should "test the PDF format"
end
context "#subject_for_issue" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@issue = Issue.generate!(:subject => "gantt#subject_for_issue",
:tracker => @tracker,
:project => @project,
:start_date => 3.days.ago.to_date,
:due_date => Date.yesterday)
@project.issues << @issue
end
context ":html format" do
should "add an absolute positioned div" do
@response.body = @gantt.subject_for_issue(@issue, {:format => :html})
assert_select "div[style*=absolute]"
end
should "use the indent option to move the div to the right" do
@response.body = @gantt.subject_for_issue(@issue, {:format => :html, :indent => 40})
assert_select "div[style*=left:40]"
end
should "include the issue subject" do
@response.body = @gantt.subject_for_issue(@issue, {:format => :html})
assert_select 'div', :text => /#{@issue.subject}/
end
should "include a link to the issue" do
@response.body = @gantt.subject_for_issue(@issue, {:format => :html})
assert_select 'a[href=?]', Regexp.escape("/issues/#{@issue.to_param}"), :text => /#{@tracker.name} ##{@issue.id}/
end
should "style overdue issues" do
assert @issue.overdue?, "Need an overdue issue for this test"
@response.body = @gantt.subject_for_issue(@issue, {:format => :html})
assert_select 'div span.issue-overdue'
end
end
should "test the PNG format"
should "test the PDF format"
end
context "#line_for_issue" do
setup do
create_gantt
@project.enabled_module_names = [:issue_tracking]
@tracker = Tracker.generate!
@project.trackers << @tracker
@version = Version.generate!(:effective_date => 1.week.from_now.to_date)
@project.versions << @version
@issue = Issue.generate!(:fixed_version => @version,
:subject => "gantt#line_for_project",
:tracker => @tracker,
:project => @project,
:done_ratio => 30,
:start_date => Date.yesterday,
:due_date => 1.week.from_now.to_date)
@project.issues << @issue
end
context ":html format" do
context "todo line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_todo[style*=left:52px]"
end
should "be the total width of the issue" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_todo[style*=width:34px]"
end
end
context "late line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_late[style*=left:52px]"
end
should "be the total delayed width of the issue" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_late[style*=width:6px]"
end
end
context "done line" do
should "start from the starting point on the left" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_done[style*=left:52px]"
end
should "Be the total done width of the issue" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.task_done[style*=left:52px]"
end
end
context "status content" do
should "appear at the far left, even if it's far in the past" do
@gantt.instance_variable_set('@date_to', 2.weeks.ago.to_date)
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.issue-name"
end
should "show the issue status" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.issue-name", /#{@issue.status.name}/
end
should "show the percent complete" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.issue-name", /30%/
end
end
end
should "have an issue tooltip" do
@response.body = @gantt.line_for_issue(@issue, {:format => :html, :zoom => 4})
assert_select "div.tooltip", /#{@issue.subject}/
end
should "test the PNG format"
should "test the PDF format"
end
context "#to_image" do
should "be tested"
end
context "#to_pdf" do
should "be tested"
end
end
+118
View File
@@ -842,4 +842,122 @@ class ProjectTest < ActiveSupport::TestCase
end
context "#start_date" do
setup do
ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
@project = Project.generate!(:identifier => 'test0')
@project.trackers << Tracker.generate!
end
should "be nil if there are no issues on the project" do
assert_nil @project.start_date
end
should "be nil if issue tracking is disabled" do
Issue.generate_for_project!(@project, :start_date => Date.today)
@project.enabled_modules.find_all_by_name('issue_tracking').each {|m| m.destroy}
@project.reload
assert_nil @project.start_date
end
should "be the earliest start date of it's issues" do
early = 7.days.ago.to_date
Issue.generate_for_project!(@project, :start_date => Date.today)
Issue.generate_for_project!(@project, :start_date => early)
assert_equal early, @project.start_date
end
end
context "#due_date" do
setup do
ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
@project = Project.generate!(:identifier => 'test0')
@project.trackers << Tracker.generate!
end
should "be nil if there are no issues on the project" do
assert_nil @project.due_date
end
should "be nil if issue tracking is disabled" do
Issue.generate_for_project!(@project, :due_date => Date.today)
@project.enabled_modules.find_all_by_name('issue_tracking').each {|m| m.destroy}
@project.reload
assert_nil @project.due_date
end
should "be the latest due date of it's issues" do
future = 7.days.from_now.to_date
Issue.generate_for_project!(@project, :due_date => future)
Issue.generate_for_project!(@project, :due_date => Date.today)
assert_equal future, @project.due_date
end
should "be the latest due date of it's versions" do
future = 7.days.from_now.to_date
@project.versions << Version.generate!(:effective_date => future)
@project.versions << Version.generate!(:effective_date => Date.today)
assert_equal future, @project.due_date
end
should "pick the latest date from it's issues and versions" do
future = 7.days.from_now.to_date
far_future = 14.days.from_now.to_date
Issue.generate_for_project!(@project, :due_date => far_future)
@project.versions << Version.generate!(:effective_date => future)
assert_equal far_future, @project.due_date
end
end
context "Project#completed_percent" do
setup do
ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
@project = Project.generate!(:identifier => 'test0')
@project.trackers << Tracker.generate!
end
context "no versions" do
should "be 100" do
assert_equal 100, @project.completed_percent
end
end
context "with versions" do
should "return 0 if the versions have no issues" do
Version.generate!(:project => @project)
Version.generate!(:project => @project)
assert_equal 0, @project.completed_percent
end
should "return 100 if the version has only closed issues" do
v1 = Version.generate!(:project => @project)
Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v1)
v2 = Version.generate!(:project => @project)
Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('Closed'), :fixed_version => v2)
assert_equal 100, @project.completed_percent
end
should "return the averaged completed percent of the versions (not weighted)" do
v1 = Version.generate!(:project => @project)
Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v1)
v2 = Version.generate!(:project => @project)
Issue.generate_for_project!(@project, :status => IssueStatus.find_by_name('New'), :estimated_hours => 10, :done_ratio => 50, :fixed_version => v2)
assert_equal 50, @project.completed_percent
end
end
end
end
+51 -1
View File
@@ -104,7 +104,57 @@ class VersionTest < ActiveSupport::TestCase
assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_pourcent
assert_progress_equal 25.0/100.0*100, v.closed_pourcent
end
context "#behind_schedule?" do
setup do
ProjectCustomField.destroy_all # Custom values are a mess to isolate in tests
@project = Project.generate!(:identifier => 'test0')
@project.trackers << Tracker.generate!
@version = Version.generate!(:project => @project, :effective_date => nil)
end
should "be false if there are no issues assigned" do
@version.update_attribute(:effective_date, Date.yesterday)
assert_equal false, @version.behind_schedule?
end
should "be false if there is no effective_date" do
assert_equal false, @version.behind_schedule?
end
should "be false if all of the issues are ahead of schedule" do
@version.update_attribute(:effective_date, 7.days.from_now.to_date)
@version.fixed_issues = [
Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60), # 14 day span, 60% done, 50% time left
Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60) # 14 day span, 60% done, 50% time left
]
assert_equal 60, @version.completed_pourcent
assert_equal false, @version.behind_schedule?
end
should "be true if any of the issues are behind schedule" do
@version.update_attribute(:effective_date, 7.days.from_now.to_date)
@version.fixed_issues = [
Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 60), # 14 day span, 60% done, 50% time left
Issue.generate_for_project!(@project, :start_date => 7.days.ago, :done_ratio => 20) # 14 day span, 20% done, 50% time left
]
assert_equal 40, @version.completed_pourcent
assert_equal true, @version.behind_schedule?
end
should "be false if all of the issues are complete" do
@version.update_attribute(:effective_date, 7.days.from_now.to_date)
@version.fixed_issues = [
Issue.generate_for_project!(@project, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)), # 7 day span
Issue.generate_for_project!(@project, :start_date => 14.days.ago, :done_ratio => 100, :status => IssueStatus.find(5)) # 7 day span
]
assert_equal 100, @version.completed_pourcent
assert_equal false, @version.behind_schedule?
end
end
context "#estimated_hours" do
setup do
@version = Version.create!(:project_id => 1, :name => '#estimated_hours')