[PATCH] The sprint state is now shown in the sprint list. #226
by Darryl L. Pierce
Previously, the proposed state was not a key in the state text
dictionary.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/models/sprint.rb | 8 +++++++-
app/views/sprints/index.html.erb | 2 +-
doc/ChangeLog | 1 +
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/app/models/sprint.rb b/app/models/sprint.rb
index dd160e0..332ea64 100644
--- a/app/models/sprint.rb
+++ b/app/models/sprint.rb
@@ -73,12 +73,18 @@ class Sprint < ActiveRecord::Base
:dependent => :destroy
has_many :user_stories, :through => :backlog_items, :order => :priority
+ STATE_PROPOSED='proposed'
STATE_PLANNING='planning'
STATE_ACTIVE='active'
STATE_COMPLETED='completed'
STATE_CANCELLED='cancelled'
- STATES = {STATE_PLANNING => "Planning", STATE_ACTIVE => "Active", :STATE_COMPLETED => "Completed", STATE_CANCELLED => "Cancelled"}
+ STATES = {
+ STATE_PROPOSED => "Proposed",
+ STATE_PLANNING => "Planning",
+ STATE_ACTIVE => "Active",
+ STATE_COMPLETED => "Completed",
+ STATE_CANCELLED => "Cancelled"}
named_scope :default, { :order => "starts ASC" }
named_scope :for_product, lambda { |product_id|
diff --git a/app/views/sprints/index.html.erb b/app/views/sprints/index.html.erb
index 7f1cc79..ca2b752 100644
--- a/app/views/sprints/index.html.erb
+++ b/app/views/sprints/index.html.erb
@@ -21,7 +21,7 @@
<tr class="<%= "status-#{sprint.state}" %>">
<td><%= sprint.id %></td>
<td class="name">
- <%= link_to "#{sprint.title} (#{Sprint::STATES[sprint.state_text]})",
+ <%= link_to "#{sprint.title} (#{sprint.state_text})",
sprint_path(sprint) %>
<%= "Hours: #{show_hours_as_ear(sprint)}" %>
<%= RedCloth.new(get_first_sentence(sprint.goals)).to_html %>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index d4ade5a..2282b64 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -48,6 +48,7 @@ Change Log (0.3.0):
* #220 - When a user has project admin rights, then the link will say "Create" not "Request". (BUG)
* #221 - When requesting a product role, the URL is broken. (BUG)
* #222 - Fixed creating/editing products using the wrong URL. (BUG)
+ * #226 - The sprint status text is not shown on the sprint list page. (BUG)
* #228 - Fixed the URL in the product creation RSS entry. (BUG)
Change Log (0.2.0):
--
1.6.2.5
14 years, 8 months
[PATCH] Fixed the width of all edit pages. #197
by Darryl L. Pierce
Changed the div class from content to content-no-sidebar.
---
app/views/epics/_edit.html.erb | 2 +-
app/views/products/_edit.html.erb | 2 +-
app/views/projects/_edit.html.erb | 2 +-
app/views/roles/_edit.html.erb | 2 +-
app/views/sprints/_edit.html.erb | 2 +-
app/views/stories/_edit.html.erb | 2 +-
app/views/tasks/_edit.html.erb | 2 +-
app/views/users/_edit.html.erb | 2 +-
doc/ChangeLog | 1 +
9 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/app/views/epics/_edit.html.erb b/app/views/epics/_edit.html.erb
index 2b10c9c..ce6830d 100644
--- a/app/views/epics/_edit.html.erb
+++ b/app/views/epics/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @epic.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for :epic, @epic, :url => epics_path(@epic), :html => html do |form| %>
diff --git a/app/views/products/_edit.html.erb b/app/views/products/_edit.html.erb
index 4f47c6b..1eebc0b 100644
--- a/app/views/products/_edit.html.erb
+++ b/app/views/products/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @product.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:product, @product, :url => @product.new_record? ? products_path(:project => @project) : product_path(@product), :html => html) do |form| %>
diff --git a/app/views/projects/_edit.html.erb b/app/views/projects/_edit.html.erb
index 59a5913..f8c9e6d 100644
--- a/app/views/projects/_edit.html.erb
+++ b/app/views/projects/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% if @project.new_record? %>
<% html = {:method => :post} %>
<% url = projects_path %>
diff --git a/app/views/roles/_edit.html.erb b/app/views/roles/_edit.html.erb
index b74bc9f..8fa7ecd 100644
--- a/app/views/roles/_edit.html.erb
+++ b/app/views/roles/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @role.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:product_role, @role, :url => @role.new_record? ? product_roles_path(:product => @product) : product_roles_path(@product,@role), :html => html) do |form| %>
<table class="edit">
diff --git a/app/views/sprints/_edit.html.erb b/app/views/sprints/_edit.html.erb
index d210801..1b48199 100644
--- a/app/views/sprints/_edit.html.erb
+++ b/app/views/sprints/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @sprint.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:sprint, @sprint, :url => sprints_path(@sprint),
:html => html) do |form| %>
diff --git a/app/views/stories/_edit.html.erb b/app/views/stories/_edit.html.erb
index b22bc19..22b312b 100644
--- a/app/views/stories/_edit.html.erb
+++ b/app/views/stories/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @user_story.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:user_story, @user_story,
diff --git a/app/views/tasks/_edit.html.erb b/app/views/tasks/_edit.html.erb
index 1cb0dc9..98c3d8d 100644
--- a/app/views/tasks/_edit.html.erb
+++ b/app/views/tasks/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @task.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:task, @task, :url => tasks_path(@task), :html => html) do |form| %>
<%= hidden_field_tag :item, @backlog_item.id %>
diff --git a/app/views/users/_edit.html.erb b/app/views/users/_edit.html.erb
index c95e007..110e9bc 100644
--- a/app/views/users/_edit.html.erb
+++ b/app/views/users/_edit.html.erb
@@ -1,4 +1,4 @@
-<div id="content">
+<div id="content-no-sidebar">
<% html = @this_user.new_record? ? {:method => :post} : {:method => :put} %>
<% form_for(:user, @this_user, :url => users_path(@this_user), :html => html) do |form| %>
<table class="edit">
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 94c7fcb..d4ade5a 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -30,6 +30,7 @@ Change Log (0.3.0):
* #193 - After writing an epic, the user should be returned to the epics list. (BUG)
* #195 - Product details page does not show number of members. (BUG)
* #196 - Backlog items list page title mispelled. (BUG)
+ * #197 - All edit pages should be as wide as the client space. (BUG)
* #199 - Admins can approve or reject proposed projects.
* #200 - Users should not be able to close epics with open user stories. (BUG)
* #202 - Project admins are emailed when a new project is requested.
--
1.6.2.5
14 years, 8 months
[PATCH] Fixed the RSS entry for when a product is created. #228
by Darryl L. Pierce
The entry was being created before the product was saved, so the URL was
incorrect. Now it is generated after the save and works correctly.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/products_controller.rb | 19 +++++++++++++------
doc/ChangeLog | 1 +
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb
index 25d74f2..c8d8190 100644
--- a/app/controllers/products_controller.rb
+++ b/app/controllers/products_controller.rb
@@ -101,14 +101,14 @@ class ProductsController < ApplicationController
Product.transaction do
@product = Product.new(params[:product])
@product.project = @project
- @product.rss_entries << create_rss_entry("New product created: #{(a)product.name}",
- products_url(@product),
- "A new product was created:\r#{@product.description}}")
if @product.project.can_create_products?(@user)
if @product.save
flash[:message] = "Product created successfully."
format.html { redirect_to product_path(@product) }
+ add_rss_entry(create_rss_entry("New product created: #{(a)product.name}",
+ product_url(@product),
+ "A new product was created:\r#{@product.description}}"))
else
@title = "New Product"
@users = User.find(:all, :order => 'display_name ASC')
@@ -136,11 +136,11 @@ class ProductsController < ApplicationController
begin
Product.transaction do
@product.update_attributes(params[:product])
- @product.rss_entries << create_rss_entry("Details updated: #{(a)product.name}",
- product_url(@product),
- "The product's details have been updated...")
if @product.save
+ add_rss_entry(create_rss_entry("Details updated: #{(a)product.name}",
+ product_url(@product),
+ "The product's details have been updated..."))
flash[:message] = "#{(a)product.name} successfully updated."
format.html { redirect_to product_path(@product) }
else
@@ -200,6 +200,13 @@ class ProductsController < ApplicationController
end
end
+ def add_rss_entry rss_entry
+ Project.transaction do
+ @product.rss_entries << rss_entry
+ @product.save
+ end
+ end
+
def path_to_list
@project = Project.find_by_id(params[:project]) if params[:project]
if @project
diff --git a/doc/ChangeLog b/doc/ChangeLog
index ad1709e..94c7fcb 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -47,6 +47,7 @@ Change Log (0.3.0):
* #220 - When a user has project admin rights, then the link will say "Create" not "Request". (BUG)
* #221 - When requesting a product role, the URL is broken. (BUG)
* #222 - Fixed creating/editing products using the wrong URL. (BUG)
+ * #228 - Fixed the URL in the product creation RSS entry. (BUG)
Change Log (0.2.0):
* #97 - Users can request new projects.
--
1.6.2.5
14 years, 8 months
[PATCH] Fixed the posting url for requesting a product role. #221
by Darryl L. Pierce
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/roles/_edit.html.erb | 2 +-
doc/ChangeLog | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/app/views/roles/_edit.html.erb b/app/views/roles/_edit.html.erb
index d81db55..b74bc9f 100644
--- a/app/views/roles/_edit.html.erb
+++ b/app/views/roles/_edit.html.erb
@@ -1,6 +1,6 @@
<div id="content">
<% html = @role.new_record? ? {:method => :post} : {:method => :put} %>
- <% form_for(:product_role, @role, :url => product_roles_path(@product,@role), :html => html) do |form| %>
+ <% form_for(:product_role, @role, :url => @role.new_record? ? product_roles_path(:product => @product) : product_roles_path(@product,@role), :html => html) do |form| %>
<table class="edit">
<caption><%= "#{(a)role.new_record? ? 'Create' : 'Edit'} Product Role" %></caption>
<tbody>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 3e757ac..ad1709e 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -45,6 +45,7 @@ Change Log (0.3.0):
* #217 - All of a user's posts can be viewed.
* #218 - Sprints need a new stage called "proposed". (backed out)
* #220 - When a user has project admin rights, then the link will say "Create" not "Request". (BUG)
+ * #221 - When requesting a product role, the URL is broken. (BUG)
* #222 - Fixed creating/editing products using the wrong URL. (BUG)
Change Log (0.2.0):
--
1.6.2.5
14 years, 8 months
[PATCH 2/2] Fixed the ChangeLog for #222.
by Darryl L. Pierce
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
doc/ChangeLog | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 2fb2560..d2ac225 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -44,6 +44,7 @@ Change Log (0.3.0):
* #216 - The user's most recent postings are displayed on their details page.
* #217 - All of a user's posts can be viewed.
* #218 - Sprints need a new stage called "proposed". (backed out)
+ * #222 - Fixed creating/editing products using the wrong URL. (BUG)
Change Log (0.2.0):
* #97 - Users can request new projects.
--
1.6.2.5
14 years, 8 months
[PATCH 1/2] Fixed editing and posting products. #222
by Darryl L. Pierce
Changed the route used in the _edit.html.erb file. Also fixed a problem
in the routes.rb file related to product resources.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/products/_edit.html.erb | 2 +-
config/routes.rb | 7 +------
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/app/views/products/_edit.html.erb b/app/views/products/_edit.html.erb
index bcd4190..4f47c6b 100644
--- a/app/views/products/_edit.html.erb
+++ b/app/views/products/_edit.html.erb
@@ -1,6 +1,6 @@
<div id="content">
<% html = @product.new_record? ? {:method => :post} : {:method => :put} %>
- <% form_for(:product, @product, :url => products_path(@product), :html => html) do |form| %>
+ <% form_for(:product, @product, :url => @product.new_record? ? products_path(:project => @project) : product_path(@product), :html => html) do |form| %>
<% unless @product.new_record? %>
<% hidden_field_tag "id", @product.id %>
diff --git a/config/routes.rb b/config/routes.rb
index b8cbde5..3ba07ec 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -40,12 +40,7 @@ ActionController::Routing::Routes.draw do |map|
:comment => :post,
}
- map.resources :products, :member =>
- {
- :rss => :get
- }
-
- map.resources :products do |product|
+ map.resources :products, :member => {:rss => :get} do |product|
product.resources :roles
end
--
1.6.2.5
14 years, 8 months
[PATCH] Fixed the link to say "Create" for users with project admin rights. #220
by Darryl L. Pierce
The link will say "Create A New Project" for users with admin rights,
and "Request A New Project" for users without admin rights.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/views/projects/index.html.erb | 2 +-
doc/ChangeLog | 1 +
2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index a069927..2a317a5 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -44,7 +44,7 @@
<% render :layout => 'home/sidebar', :locals => {:title => 'Options'} do %>
<% if @user %>
- <%= link_to "Request A New Project", new_project_path, :class => "command" %>
+ <%= link_to "#{(a)user.privileges.admin_projects ? 'Create' : 'Request'} A New Project", new_project_path, :class => "command" %>
<% end %>
<% if @show_all_projects %>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index d2ac225..3e757ac 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -44,6 +44,7 @@ Change Log (0.3.0):
* #216 - The user's most recent postings are displayed on their details page.
* #217 - All of a user's posts can be viewed.
* #218 - Sprints need a new stage called "proposed". (backed out)
+ * #220 - When a user has project admin rights, then the link will say "Create" not "Request". (BUG)
* #222 - Fixed creating/editing products using the wrong URL. (BUG)
Change Log (0.2.0):
--
1.6.2.5
14 years, 8 months
[PATCH] A sprint cannot become planned unless its items are accepted. #165
by Darryl L. Pierce
Added a new method, BacklogItem::accepted?, which returns false if
there are any NAK votes, and true only if there are ACKs.
Added a new method, Sprint::accepted?, which reports false if any of its
backlog items report they were not accepted.
I also backed out the proposed state for sprints since it really seemed
to make no sense after working with that state. It actually made this
particular task harder to work on.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/sprints_controller.rb | 12 ++-
app/models/backlog_item.rb | 12 +++
app/models/sprint.rb | 24 +++---
app/models/user_mailer.rb | 2 +-
app/views/sprints/show.html.erb | 2 +-
doc/ChangeLog | 2 +-
test/fixtures/backlog_item_votes.yml | 21 +++++-
test/fixtures/backlog_items.yml | 10 +-
test/fixtures/notifications.yml | 1 +
test/fixtures/sprint_members.yml | 4 -
test/fixtures/sprints.yml | 9 --
test/functional/messages_controller_test.rb | 1 +
test/functional/sprints_controller_test.rb | 110 ++++++++++++++-------------
test/unit/backlog_item_test.rb | 30 +++++++
test/unit/sprint_test.rb | 27 ++++---
15 files changed, 161 insertions(+), 106 deletions(-)
diff --git a/app/controllers/sprints_controller.rb b/app/controllers/sprints_controller.rb
index 3aac6a2..ddcab57 100644
--- a/app/controllers/sprints_controller.rb
+++ b/app/controllers/sprints_controller.rb
@@ -20,10 +20,11 @@ class SprintsController < ApplicationController
before_filter :authenticated, :except => [:index, :show, :members]
before_filter :load_product, :only => [:new, :create]
before_filter :load_sprint, :except => [:index, :new, :create]
- before_filter :verify_proposed, :only => [:plan, :populate, :vote, :cast_votes]
+ before_filter :verify_planned, :only => [:plan, :populate, :vote, :cast_votes]
before_filter :verify_can_edit, :only => [:edit, :update]
before_filter :verify_can_change_state, :only => [:planning, :start, :complete, :cancel]
before_filter :verify_can_vote, :only => [:vote, :cast_votes]
+ before_filter :verify_sprint_can_start, :only => [:start]
before_filter :extract_votes_and_estimates, :only => [:vote]
before_filter :receive_votes_and_estimates, :only => [:cast_votes]
before_filter :prepare_for_edit, :only => [:new, :create, :edit, :update, :plan, :populate]
@@ -333,8 +334,8 @@ class SprintsController < ApplicationController
report_error("You are not allowed to vote on this sprint.", sprint_path(@sprint)) unless @sprint.is_team_member?(@user)
end
- def verify_proposed
- report_error("You can only vote on items while a sprint is in planning.", sprint_path(@sprint)) unless @sprint.proposed?
+ def verify_planned
+ report_error("You can only vote on items while a sprint is in planning.", sprint_path(@sprint)) unless @sprint.planning?
end
def verify_cast_votes
@@ -348,6 +349,11 @@ class SprintsController < ApplicationController
end
end
+ def verify_sprint_can_start
+ report_error("Only a planned sprint can be started.", sprint_path(@sprint)) unless @sprint.planning?
+ report_error("Cannot start a sprint with rejected or unaccepted backlog items.", sprint_path(@sprint)) unless @sprint.accepted?
+ end
+
def extract_votes_and_estimates
@votes = {}
@estimates = {}
diff --git a/app/models/backlog_item.rb b/app/models/backlog_item.rb
index b5de6cf..c6e500b 100644
--- a/app/models/backlog_item.rb
+++ b/app/models/backlog_item.rb
@@ -157,6 +157,18 @@ class BacklogItem < ActiveRecord::Base
end
end
+ # Returns whether the item has been accepted or not.
+ def accepted?
+ return false if votes.empty?
+
+ result = false
+ votes.each do |vote|
+ return false if vote.vote == BacklogItemVote::NAK
+ result |= (vote.vote == BacklogItemVote::ACK) unless vote.vote == BacklogItemVote::ABSTAIN
+ end
+ return result
+ end
+
# Returns whether the user can complete the given item.
def user_can_complete?(user)
can_complete? && owner?(user)
diff --git a/app/models/sprint.rb b/app/models/sprint.rb
index 9660830..dd160e0 100644
--- a/app/models/sprint.rb
+++ b/app/models/sprint.rb
@@ -73,13 +73,12 @@ class Sprint < ActiveRecord::Base
:dependent => :destroy
has_many :user_stories, :through => :backlog_items, :order => :priority
- STATE_PROPOSED='proposed'
STATE_PLANNING='planning'
STATE_ACTIVE='active'
STATE_COMPLETED='completed'
STATE_CANCELLED='cancelled'
- STATES = {STATE_PROPOSED => "Proposed", STATE_PLANNING => "Planning", STATE_ACTIVE => "Active", :STATE_COMPLETED => "Completed", STATE_CANCELLED => "Cancelled"}
+ STATES = {STATE_PLANNING => "Planning", STATE_ACTIVE => "Active", :STATE_COMPLETED => "Completed", STATE_CANCELLED => "Cancelled"}
named_scope :default, { :order => "starts ASC" }
named_scope :for_product, lambda { |product_id|
@@ -88,8 +87,7 @@ class Sprint < ActiveRecord::Base
named_scope :planned, {:conditions => ['state = ?', STATE_PLANNING]}
named_scope :active, {:conditions => ['state = ?', STATE_ACTIVE]}
- state_machine :initial => :proposed do
- state :proposed, :value => STATE_PROPOSED
+ state_machine :initial => :planning do
state :planning, :value => STATE_PLANNING
state :active, :value => STATE_ACTIVE
state :completed, :value => STATE_COMPLETED
@@ -103,16 +101,11 @@ class Sprint < ActiveRecord::Base
end
end
- event :unplan do
- transition :planning => :proposed
- end
-
event :plan do
transition :active => :planning
- transition :proposed => :planning
def can_fire?(sprint)
- sprint.proposed? || (sprint.active? && Sprint.for_product(sprint.product).planned.empty?)
+ sprint.active? && Sprint.for_product(sprint.product).planned.empty?
end
end
@@ -163,6 +156,13 @@ class Sprint < ActiveRecord::Base
backlog_items.inject(0) { |sum, item| sum + item.remaining_hours_for_date(starts + day) }
end
+ # Returns true of all backlog items have been accepted and none have been rejected.
+ def accepted?
+ result = true
+ backlog_items.each { |item| result &= item.accepted? }
+ return result
+ end
+
# Returns whether the specified user can modify this sprint.
def can_edit?(user)
(active? || planning?) && (is_product_owner(user) || is_team_lead?(user))
@@ -180,12 +180,12 @@ class Sprint < ActiveRecord::Base
# Returns whether the specified user is allowed to populate this sprint.
def can_populate?(user)
- proposed? && (is_product_owner(user) || is_team_lead?(user))
+ planning? && (is_product_owner(user) || is_team_lead?(user))
end
# Returns whether a burndown chart can be viewed for this sprint.
def can_view_burndown?
- !proposed? && !planning?
+ !planning?
end
# Rethers whether the sprint can have backlog items added, and whether the
diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb
index 4e1acef..3d35cf6 100644
--- a/app/models/user_mailer.rb
+++ b/app/models/user_mailer.rb
@@ -74,7 +74,7 @@ class UserMailer < ActionMailer::Base
end
# Sends an email to the sprint team lead when a developer has cast votes
- # on backlog items for a proposed sprint.
+ # on backlog items for a planned sprint.
def votes_cast(sprint, developer, votes)
recipients sprint.team_lead.email
cc developer.email
diff --git a/app/views/sprints/show.html.erb b/app/views/sprints/show.html.erb
index f5f572e..c1eed47 100644
--- a/app/views/sprints/show.html.erb
+++ b/app/views/sprints/show.html.erb
@@ -40,7 +40,7 @@
<% end %>
<% end %>
- <% if @sprint.is_team_member?(@user) && @sprint.proposed? %>
+ <% if @sprint.is_team_member?(@user) && @sprint.planning? %>
<%= link_to "Vote on sprint backlog",
vote_sprint_path(@sprint), :class => "command" %>
<% end %>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index adac356..1165be9 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -42,7 +42,7 @@ Change Log (0.3.0):
* #213 - Users can disable receiving emails when replies are posted.
* #216 - The user's most recent postings are displayed on their details page.
* #217 - All of a user's posts can be viewed.
- * #218 - Sprints need a new stage called "proposed".
+ * #218 - Sprints need a new stage called "proposed". (backed out)
Change Log (0.2.0):
* #97 - Users can request new projects.
diff --git a/test/fixtures/backlog_item_votes.yml b/test/fixtures/backlog_item_votes.yml
index 3630a1b..da08be1 100644
--- a/test/fixtures/backlog_item_votes.yml
+++ b/test/fixtures/backlog_item_votes.yml
@@ -3,7 +3,22 @@ planned_sprint_backlog_item_jdonuts_vote:
backlog_item_id: <%= Fixtures.identify(:planned_sprint_backlog_item) %>
vote: <%= BacklogItemVote::ACK %>
-planned_sprint_backlog_item_mcpierce_vote:
- user_id: <%= Fixtures.identify(:mcpierce) %>
+planned_sprint_backlog_item_celliot_vote:
+ user_id: <%= Fixtures.identify(:celliot) %>
+ backlog_item_id: <%= Fixtures.identify(:planned_sprint_backlog_item) %>
+ vote: <%= BacklogItemVote::ACK %>
+
+planned_sprint_owned_backlog_item_celliot_vote:
+ user_id: <%= Fixtures.identify(:celliot) %>
+ backlog_item_id: <%= Fixtures.identify(:owned_planned_sprint_backlog_item) %>
+ vote: <%= BacklogItemVote::ACK %>
+
+active_sprint_unowned_backlog_item_jdonuts_vote:
+ user_id: <%= Fixtures.identify(:jdonuts) %>
backlog_item_id: <%= Fixtures.identify(:unowned_backlog_item) %>
- vote: <%= BacklogItemVote::NAK %>
+ vote: <%= BacklogItemVote::ACK %>
+
+startable_sprint_backlog_item_mcpierce_vote:
+ user_id: <%= Fixtures.identify(:mcpierce) %>
+ backlog_item_id: <%= Fixtures.identify(:startable_sprint_backlog_item) %>
+ vote: <%= BacklogItemVote::ACK %>
diff --git a/test/fixtures/backlog_items.yml b/test/fixtures/backlog_items.yml
index fd0ee9b..5912257 100644
--- a/test/fixtures/backlog_items.yml
+++ b/test/fixtures/backlog_items.yml
@@ -15,11 +15,6 @@ closed_backlog_item:
owner_id: <%= Fixtures.identify(:mcpierce) %>
state: <%= BacklogItem::STATE_COMPLETED %>
-proposed_sprint_backlog_item:
- sprint_id: <%= Fixtures.identify(:proposed_sprint) %>
- user_story_id: <%= Fixtures.identify(:create_login) %>
- state: <%= BacklogItem::STATE_PENDING %>
-
planned_sprint_backlog_item:
sprint_id: <%= Fixtures.identify(:planned_sprint) %>
user_story_id: <%= Fixtures.identify(:create_update_profile) %>
@@ -43,3 +38,8 @@ blocked_backlog_item:
owner_id: <%= Fixtures.identify(:mcpierce) %>
state: <%= BacklogItem::STATE_ASSIGNED %>
blocked: true
+
+startable_sprint_backlog_item:
+ sprint_id: <%= Fixtures.identify(:startable_sprint) %>
+ user_story_id: <%= Fixtures.identify(:create_update_profile) %>
+ state: <%= BacklogItem::STATE_PENDING %>
diff --git a/test/fixtures/notifications.yml b/test/fixtures/notifications.yml
index ac0dd71..ec3a9a8 100644
--- a/test/fixtures/notifications.yml
+++ b/test/fixtures/notifications.yml
@@ -3,4 +3,5 @@
user_id: <%= user.id %>
task_reminders: false
daily_updates: false
+ email_on_replies: true
<% end %>
diff --git a/test/fixtures/sprint_members.yml b/test/fixtures/sprint_members.yml
index e475590..43eb90e 100644
--- a/test/fixtures/sprint_members.yml
+++ b/test/fixtures/sprint_members.yml
@@ -1,7 +1,3 @@
-mcpierce_proposed_sprint:
- sprint_id: <%= Fixtures.identify(:proposed_sprint) %>
- member_id: <%= Fixtures.identify(:mcpierce) %>
-
mcpierce_planned_sprint:
sprint_id: <%= Fixtures.identify(:planned_sprint) %>
member_id: <%= Fixtures.identify(:mcpierce) %>
diff --git a/test/fixtures/sprints.yml b/test/fixtures/sprints.yml
index 06c700c..d32b34f 100644
--- a/test/fixtures/sprints.yml
+++ b/test/fixtures/sprints.yml
@@ -1,12 +1,3 @@
-proposed_sprint:
- product_id: <%= Fixtures.identify(:projxp_web) %>
- title: Stuff we're thinking about doing soon.
- starts: <%= DateTime.now.to_s(:db) %>
- duration: 28
- goals: Don't know yet.
- state: <%= Sprint::STATE_PROPOSED %>
- team_lead_id: <%= Fixtures.identify(:team_lead) %>
-
planned_sprint:
product_id: <%= Fixtures.identify(:projxp_web) %>
title: Stuff we're going to do next.
diff --git a/test/functional/messages_controller_test.rb b/test/functional/messages_controller_test.rb
index d5287ac..0c0ca0f 100644
--- a/test/functional/messages_controller_test.rb
+++ b/test/functional/messages_controller_test.rb
@@ -141,6 +141,7 @@ class MessagesControllerTest < ActionController::TestCase
post :post_reply, {:id => @message.id, :reply => @new_reply}, {:user_id => @user.id}
+
assert_redirected_to message_path(@message)
result = Message.find_by_id((a)message.id).replies.last
assert result, "Did not save the reply."
diff --git a/test/functional/sprints_controller_test.rb b/test/functional/sprints_controller_test.rb
index 80894bf..b7d3301 100644
--- a/test/functional/sprints_controller_test.rb
+++ b/test/functional/sprints_controller_test.rb
@@ -40,12 +40,8 @@ class SprintsControllerTest < ActionController::TestCase
raise "Member must be a team member!" unless @planned_sprint.is_team_member?(@member)
raise "Nonmember cannot be a team member!" if @planned_sprint.is_team_member?(@nonmember)
- @proposed_sprint_backlog_item = backlog_items(:proposed_sprint_backlog_item)
- @proposed_sprint = @proposed_sprint_backlog_item.sprint
- raise "Sprint must be in proposed state!" unless @proposed_sprint.proposed?
-
- @votes = {"#{(a)proposed_sprint.backlog_items.first.id}" => "#{BacklogItemVote::ACK}"}
- @estimates = {"#{(a)proposed_sprint.backlog_items.first.id}" => "5.0"}
+ @votes = {"#{(a)planned_sprint.backlog_items.first.id}" => "#{BacklogItemVote::ACK}"}
+ @estimates = {"#{(a)planned_sprint.backlog_items.first.id}" => "5.0"}
@startable_sprint = sprints(:startable_sprint)
raise "Sprint must be in the planned start!" unless @startable_sprint.planning?
@@ -403,7 +399,20 @@ class SprintsControllerTest < ActionController::TestCase
def test_start_with_nonstarter_sprint
put :start, {:id => @plannable_sprint.id },{:user_id => @plannable_sprint.team_lead_id}
- assert_redirected_to error_path
+ assert_redirected_to sprint_path(@plannable_sprint)
+ end
+
+ # Ensures that only sprints with accepted items can be started.
+ def test_start_with_rejected_items
+ vote = @startable_sprint.backlog_items.first.votes.first
+ vote.vote = BacklogItemVote::NAK
+ vote.save
+
+ put :start, {:id => @startable_sprint.id}, {:user_id => @startable_sprint.team_lead_id}
+
+ assert_redirected_to sprint_path(@startable_sprint)
+ result = Sprint.find_by_id((a)startable_sprint.id)
+ assert !result.active?, "The sprint should not have been started."
end
# Ensures that starting a sprint work.s
@@ -509,23 +518,16 @@ class SprintsControllerTest < ActionController::TestCase
assert_redirected_to error_path
end
- # Ensures that you can only populate a pending sprint.
- def test_plan_requires_proposed_sprint
- get :plan, {:id => @active_sprint.id}, {:user_id => @product.owner_id}
-
- assert_redirected_to sprint_path(@active_sprint)
- end
-
# Ensures that only the owner can plan a sprint.
def test_plan_as_nonowner
- get :plan, {:id => @proposed_sprint.id}, {:user_id => @nonowner.id}
+ get :plan, {:id => @planned_sprint.id}, {:user_id => @nonowner.id}
- assert_redirected_to sprint_path(@proposed_sprint)
+ assert_redirected_to sprint_path(@planned_sprint)
end
# Ensures that planning works.
def test_plan
- get :plan, { :id => @proposed_sprint.id}, {:user_id => @proposed_sprint.product.owner_id}
+ get :plan, { :id => @planned_sprint.id}, {:user_id => @planned_sprint.product.owner_id}
assert_response :success
assert assigns['user_stories'], "Failed to load any user stories."
@@ -555,30 +557,30 @@ class SprintsControllerTest < ActionController::TestCase
# Ensures that only the owner can populate a sprint.
def test_populate_as_nonowner
- post :populate, { :id => @proposed_sprint.id, :estimates => @estimates, :selected => @selected},
+ post :populate, { :id => @planned_sprint.id, :estimates => @estimates, :selected => @selected},
{:user_id => @nonowner.id}
- assert_redirected_to sprint_path(@proposed_sprint)
- assert !BacklogItem.find_by_sprint_id_and_user_story_id(@proposed_sprint.id,(a)user_story.id),
+ assert_redirected_to sprint_path(@planned_sprint)
+ assert !BacklogItem.find_by_sprint_id_and_user_story_id(@planned_sprint.id,(a)user_story.id),
"Backlog item should not have been created"
end
# Ensures that populating with no user stories is not an error. BUG#92
def test_populate_with_no_user_stories
- post :populate, { :id => @proposed_sprint.id},{:user_id => @proposed_sprint.product.owner_id}
+ post :populate, { :id => @planned_sprint.id},{:user_id => @planned_sprint.product.owner_id}
- assert_redirected_to sprint_path(@proposed_sprint)
+ assert_redirected_to sprint_path(@planned_sprint)
end
# Ensures that populating works.
def test_populate
- count = @proposed_sprint.product.rss_entries.size
- post :populate, {:id => @proposed_sprint.id, :selected => @selected},{:user_id => @proposed_sprint.product.owner_id}
+ count = @planned_sprint.product.rss_entries.size
+ post :populate, {:id => @planned_sprint.id, :selected => @selected},{:user_id => @planned_sprint.product.owner_id}
- assert_redirected_to sprint_path(@proposed_sprint)
- assert BacklogItem.find_by_sprint_id_and_user_story_id(@proposed_sprint.id,(a)user_story.id),
+ assert_redirected_to sprint_path(@planned_sprint)
+ assert BacklogItem.find_by_sprint_id_and_user_story_id(@planned_sprint.id,(a)user_story.id),
"Backlog item should have been created"
- product = Product.find_by_id((a)proposed_sprint.product_id)
+ product = Product.find_by_id((a)planned_sprint.product_id)
assert_equal count + 1, product.rss_entries.size, "Failed to add an RSS entry."
end
@@ -621,9 +623,9 @@ class SprintsControllerTest < ActionController::TestCase
# Ensures that only a team member can vote on sprint items.
def test_vote_with_nonmember
- get :vote, {:id => @proposed_sprint.id}, {:user_id => @nonmember.id}
+ get :vote, {:id => @planned_sprint.id}, {:user_id => @nonmember.id}
- assert_redirected_to sprint_path(@proposed_sprint)
+ assert_redirected_to sprint_path(@planned_sprint)
end
# Ensures that only a sprint in planning can get votes.
@@ -635,7 +637,7 @@ class SprintsControllerTest < ActionController::TestCase
# Ensures that the vote action works as expected.
def test_vote
- get :vote, {:id => @proposed_sprint.id}, {:user_id => @member.id}
+ get :vote, {:id => @planned_sprint.id}, {:user_id => @member.id}
assert_response :success
assert assigns['votes'], "Failed to setup the votes."
@@ -643,30 +645,30 @@ class SprintsControllerTest < ActionController::TestCase
end
# Ensures that anonymous users cannot cast votes.
- def test_cast_vote_as_anonymous
+ def test_cast_votes_as_anonymous
post :cast_votes
assert_redirected_to login_path
end
# Ensures that a valid sprint is required.
- def test_cast_vote_with_invalid_sprint
+ def test_cast_votes_with_invalid_sprint
post :cast_votes, {}, {:user_id => @member.id}
assert_redirected_to error_path
end
# Ensures that only members can cast votes.
- def test_cast_vote_as_nonmember
- count = BacklogItemVote.for_sprint((a)proposed_sprint).size
- post :cast_votes, {:id => @proposed_sprint.id}, {:user_id => @nonmember.id}
+ def test_cast_votes_as_nonmember
+ count = BacklogItemVote.for_sprint((a)planned_sprint).size
+ post :cast_votes, {:id => @planned_sprint.id}, {:user_id => @nonmember.id}
- assert_redirected_to sprint_path(@proposed_sprint)
- assert_equal count, BacklogItemVote.for_sprint((a)proposed_sprint).size, "No new votes should have been cast."
+ assert_redirected_to sprint_path(@planned_sprint)
+ assert_equal count, BacklogItemVote.for_sprint((a)planned_sprint).size, "No new votes should have been cast."
end
# Ensures that only sprints in planning can have votes.
- def test_cast_vote_for_nonplanning_sprint
+ def test_cast_votes_for_nonplanning_sprint
count = BacklogItemVote.for_sprint((a)active_sprint).size
post :cast_votes, {:id => @active_sprint.id}, {:user_id => @member.id}
@@ -675,39 +677,39 @@ class SprintsControllerTest < ActionController::TestCase
end
# Ensures that a vote cast for a non-existent backlog item fails.
- def test_cast_vote_for_nonexistent_backlog_item
- count = BacklogItemVote.for_sprint((a)proposed_sprint).size
- post :cast_votes, {:id => @proposed_sprint.id, :votes => {9999 => BacklogItemVote::ACK}}, {:user_id => @member.id}
+ def test_cast_votes_for_nonexistent_backlog_item
+ count = BacklogItemVote.for_sprint((a)planned_sprint).size
+ post :cast_votes, {:id => @planned_sprint.id, :votes => {9999 => BacklogItemVote::ACK}}, {:user_id => @member.id}
assert_redirected_to error_path
- assert_equal count, BacklogItemVote.for_sprint((a)proposed_sprint).size, "Vote should not have been cast."
+ assert_equal count, BacklogItemVote.for_sprint((a)planned_sprint).size, "Vote should not have been cast."
end
# Ensures that a vote cast for an item on a different sprint fails.
- def test_cast_vote_for_item_on_different_sprint
- count = BacklogItemVote.for_sprint((a)proposed_sprint).size
- post :cast_votes, {:id => @proposed_sprint.id, :votes => {(a)active_sprint.backlog_items.first.id => BacklogItemVote::ACK}}, {:user_id => @member.id}
+ def test_cast_votes_for_item_on_different_sprint
+ count = BacklogItemVote.for_sprint((a)planned_sprint).size
+ post :cast_votes, {:id => @planned_sprint.id, :votes => {(a)active_sprint.backlog_items.first.id => BacklogItemVote::ACK}}, {:user_id => @member.id}
assert_redirected_to error_path
- assert_equal count, BacklogItemVote.for_sprint((a)proposed_sprint).size, "Vote should not have been cast."
+ assert_equal count, BacklogItemVote.for_sprint((a)planned_sprint).size, "Vote should not have been cast."
end
# Ensures that, when casting a positive vote, an estimate must be included.
- def test_cast_vote_accept_with_no_estimate
- post :cast_votes, {:id => @proposed_sprint.id, :votes => @votes}, {:user_id => @member.id}
+ def test_cast_votes_accept_with_no_estimate
+ post :cast_votes, {:id => @planned_sprint.id, :votes => @votes}, {:user_id => @member.id}
assert_redirected_to error_path
end
# Ensures that casting votes works as expected.
- def test_cast_vote
- count = BacklogItemVote.for_sprint((a)proposed_sprint).size
- post :cast_votes, {:id => @proposed_sprint.id, :votes => @votes, :estimates => @estimates}, {:user_id => @member.id}
+ def test_cast_votes
+ count = BacklogItemVote.for_sprint((a)planned_sprint).size
+ post :cast_votes, {:id => @planned_sprint.id, :votes => @votes, :estimates => @estimates}, {:user_id => @member.id}
- assert_redirected_to sprint_path(@proposed_sprint)
- assert_equal count + 1, BacklogItemVote.for_sprint((a)proposed_sprint).size, "A new vote should have been cast."
+ assert_redirected_to sprint_path(@planned_sprint)
+ assert_equal count + 1, BacklogItemVote.for_sprint((a)planned_sprint).size, "A new vote should have been cast."
email = @emails.first
assert email, "An email should have been sent to the team lead."
- assert_equal @proposed_sprint.team_lead.email, email.to[0], "Email was not sent to the correct person."
+ assert_equal @planned_sprint.team_lead.email, email.to[0], "Email was not sent to the correct person."
end
end
diff --git a/test/unit/backlog_item_test.rb b/test/unit/backlog_item_test.rb
index 8ea0d22..e29273b 100644
--- a/test/unit/backlog_item_test.rb
+++ b/test/unit/backlog_item_test.rb
@@ -221,4 +221,34 @@ class BacklogItemTest < ActiveSupport::TestCase
@unowned_item.defer
assert @unowned_item.deferred?, "Item should have been deferred."
end
+
+ # Ensures that an item on a proposed sprint with a NAK is not considered accepted.
+ def test_accepted_fails_with_nak
+ vote = @item_on_planned_sprint.votes.first
+ vote.vote = BacklogItemVote::NAK
+ vote.save
+
+ assert !(a)item_on_planned_sprint.accepted?, "An item with a NAK cannot be accepted."
+ end
+
+ # Ensures that an item that has no rejections but only abstains is not considered accepted.
+ def test_accepted_fails_when_abstained_only
+ @item_on_planned_sprint.votes.each {|vote| vote.vote = BacklogItemVote::ABSTAIN; vote.save}
+
+ assert !(a)item_on_planned_sprint.accepted?, "An item with only abstains cannot be accepted."
+ end
+
+ # Ensures that a single abstain does not cause an item to be rejected.
+ def test_accepted_passes_with_abstain_and_ack
+ vote = @item_on_planned_sprint.votes.first
+ vote.vote = BacklogItemVote::ABSTAIN
+ vote.save
+
+ assert @item_on_planned_sprint.accepted?, "An item should be accepted when it has ACKs and abstains."
+ end
+
+ # Ensures that an accepted backlog item is reported as such.
+ def test_accepted
+ assert @item_on_planned_sprint.accepted?, "An accepted item should report itself as such."
+ end
end
diff --git a/test/unit/sprint_test.rb b/test/unit/sprint_test.rb
index 9bd309e..c5d4946 100644
--- a/test/unit/sprint_test.rb
+++ b/test/unit/sprint_test.rb
@@ -26,9 +26,6 @@ class SprintTest < ActiveSupport::TestCase
:goals => "To get stuff done!",
:team_lead => users(:mcpierce)
)
- @proposed_sprint = sprints(:proposed_sprint)
- raise "Sprint must be proposed!" unless @proposed_sprint.proposed?
-
@planned_sprint = sprints(:planned_sprint)
raise "Sprint must be in planning!" unless @planned_sprint.planning?
raise "Product must have an active sprint!" if Sprint.for_product((a)planned_sprint.product).active.empty?
@@ -119,16 +116,6 @@ class SprintTest < ActiveSupport::TestCase
end
end
- # Ensures that a proposed sprint can be moved to the planning state.
- def test_can_plan_a_proposed_sprint
- flunk "A proposed sprint must be able to move to the planning state." unless @proposed_sprint.can_plan?
- end
-
- # Ensures that a planned sprint can be moved back to the proposed state.
- def test_can_move_a_planned_sprint_to_proposed_state
- flunk "A planned sprint should be able to go back to the proposed state." unless @planned_sprint.can_unplan?
- end
-
# Ensures that nobody can edit a completed sprint.
def test_can_edit_with_completed_sprint
flunk "Completed sprints must not be edited by the team lead." if @completed_sprint.can_edit?((a)completed_sprint.team_lead)
@@ -182,4 +169,18 @@ class SprintTest < ActiveSupport::TestCase
def test_start_for_cancelled
flunk "A cancelled sprint must be startable." unless @startable_cancelled_sprint.can_start?
end
+
+ # Ensures that a sprint is not accepted if it has a rejected backlog item.
+ def test_accept_fails_with_rejected_item
+ vote = @planned_sprint.backlog_items.first.votes.first
+ vote.vote = BacklogItemVote::NAK
+ vote.save
+
+ fail "A sprint with a NAK cannot be accepted." if @planned_sprint.accepted?
+ end
+
+ # Ensures that a sprint is accepted if all of its backlog items are accepted.
+ def test_accept
+ fail "This sprint should report itself as accepted." unless @planned_sprint.accepted?
+ end
end
--
1.6.2.5
14 years, 8 months
[PATCH] Emails are sent when a developer votes on backlog items. #171
by Darryl L. Pierce
The backlog items on the sprint are grouped by whether they were
accepted, rejected or abstained from in the vote. If they were accepted
then the developer's estimate is included.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
app/controllers/sprints_controller.rb | 2 ++
app/models/backlog_item_vote.rb | 1 +
app/models/user_mailer.rb | 10 ++++++++++
app/views/user_mailer/votes_cast.html.erb | 11 +++++++++++
doc/ChangeLog | 1 +
test/functional/sprints_controller_test.rb | 10 ++++++++++
6 files changed, 35 insertions(+), 0 deletions(-)
create mode 100644 app/views/user_mailer/votes_cast.html.erb
diff --git a/app/controllers/sprints_controller.rb b/app/controllers/sprints_controller.rb
index 4c3d739..3aac6a2 100644
--- a/app/controllers/sprints_controller.rb
+++ b/app/controllers/sprints_controller.rb
@@ -176,6 +176,8 @@ class SprintsController < ApplicationController
@user_votes.each do |vote|
vote.save!
end
+ # send an email to the team lead
+ UserMailer.deliver_votes_cast(@sprint, @user, @user_votes)
respond_to do |format|
flash[:message] = "Your votes have been cast."
format.html {redirect_to sprint_path(@sprint)}
diff --git a/app/models/backlog_item_vote.rb b/app/models/backlog_item_vote.rb
index 7ecd5e5..17a4582 100644
--- a/app/models/backlog_item_vote.rb
+++ b/app/models/backlog_item_vote.rb
@@ -37,6 +37,7 @@ class BacklogItemVote < ActiveRecord::Base
NAK = -1
VOTES = {"Abstain" => ABSTAIN, "Accept" => ACK, "Reject" => NAK}
+ VOTE_TEXT = {ABSTAIN => "Abstrained", ACK => "Accepted", NAK => "Rejected"}
def validate
errors.add("estimated_hours", "An accepted item must have an hours estimate.") if (vote == ACK) && (estimated_hours <= 0)
diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb
index 18afd18..4e1acef 100644
--- a/app/models/user_mailer.rb
+++ b/app/models/user_mailer.rb
@@ -73,6 +73,16 @@ class UserMailer < ActionMailer::Base
body :role => role
end
+ # Sends an email to the sprint team lead when a developer has cast votes
+ # on backlog items for a proposed sprint.
+ def votes_cast(sprint, developer, votes)
+ recipients sprint.team_lead.email
+ cc developer.email
+ from MAIL_CONFIG[:from]
+ subject "Votes cast: #{sprint.title}"
+ body :sprint => sprint, :developer => developer, :votes => votes
+ end
+
# Sends an email to a user letting him know his daily activities.
def daily_updates(user, cc_list, open_items, completed_items, task_performed)
recipients user.email
diff --git a/app/views/user_mailer/votes_cast.html.erb b/app/views/user_mailer/votes_cast.html.erb
new file mode 100644
index 0000000..39b3b16
--- /dev/null
+++ b/app/views/user_mailer/votes_cast.html.erb
@@ -0,0 +1,11 @@
+The following votes were cast by <%= @developer.display_name %> <<%= @developer.email %>>:
+
+<% @votes.sort! { |x, y| x.vote <=> y.vote } %>
+<% last_vote_type = nil %>
+<% @votes.each do |vote| %>
+ <% if last_vote_type != vote.vote %>
+<%= BacklogItemVote::VOTE_TEXT[vote.vote] %>
+ <% last_vote_type = vote.vote %>
+ <% end %>
+<%= vote.backlog_item.user_story.title %> <% if vote.vote == BacklogItemVote::ACK %><%= "(#{vote.estimated_hours} hours)" %><% end %>
+<% end %>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 2802c71..adac356 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -12,6 +12,7 @@ Change Log (0.3.0):
* #168 - Users can post comments for epic stories.
* #169 - Comments on epics are shown on the details page.
* #170 - Users are sent an email notifying them of replies to their posts.
+ * #171 - When a developer votes on a backlog, the team lead is sent an email.
* #173 - Backlog items can be dropped from an active sprint.
* #174 - Deferred backlog items can be re-added to the active sprint.
* #175 - When viewing an unapproved project's product list, the sidebar is misplaced. (BUG)
diff --git a/test/functional/sprints_controller_test.rb b/test/functional/sprints_controller_test.rb
index 505e7b7..80894bf 100644
--- a/test/functional/sprints_controller_test.rb
+++ b/test/functional/sprints_controller_test.rb
@@ -89,6 +89,13 @@ class SprintsControllerTest < ActionController::TestCase
@selected = [
@user_story.id
]
+
+ ActionMailer::Base.delivery_method = :test
+ ActionMailer::Base.perform_deliveries = true
+ ActionMailer::Base.deliveries = []
+
+ @emails = ActionMailer::Base.deliveries
+ @emails.clear
end
# Ensures that a list of all sprints is viewable.
@@ -699,5 +706,8 @@ class SprintsControllerTest < ActionController::TestCase
assert_redirected_to sprint_path(@proposed_sprint)
assert_equal count + 1, BacklogItemVote.for_sprint((a)proposed_sprint).size, "A new vote should have been cast."
+ email = @emails.first
+ assert email, "An email should have been sent to the team lead."
+ assert_equal @proposed_sprint.team_lead.email, email.to[0], "Email was not sent to the correct person."
end
end
--
1.6.2.5
14 years, 8 months