Renamed TaskController and TaskControllerTest to TasksController and TasksControllerTest. Added a new resource to routes.rb to provide RESTful methods for working with Task objects. Updated all of the unit tests and renamed and refactored the actions in the controller.
Signed-off-by: Darryl L. Pierce dpierce@redhat.com --- app/controllers/backlog_controller.rb | 1 + app/controllers/task_controller.rb | 127 ------- app/controllers/tasks_controller.rb | 167 +++++++++ app/helpers/application_helper.rb | 9 - app/models/task.rb | 50 ++- app/views/backlog/view.html.erb | 66 +---- app/views/{task => tasks}/_edit.html.erb | 31 +- app/views/tasks/_list.html.erb | 34 ++ .../{task/modify.html.erb => tasks/edit.html.erb} | 0 app/views/tasks/index.html.erb | 1 + .../{task/create.html.erb => tasks/new.html.erb} | 0 app/views/tasks/show.html.erb | 35 ++ config/routes.rb | 5 + ...controller_test.rb => tasks_controller_test.rb} | 364 ++++++++++++-------- test/unit/product_test.rb | 3 +- 15 files changed, 520 insertions(+), 373 deletions(-) delete mode 100644 app/controllers/task_controller.rb create mode 100644 app/controllers/tasks_controller.rb rename app/views/{task => tasks}/_edit.html.erb (73%) create mode 100644 app/views/tasks/_list.html.erb rename app/views/{task/modify.html.erb => tasks/edit.html.erb} (100%) create mode 100644 app/views/tasks/index.html.erb rename app/views/{task/create.html.erb => tasks/new.html.erb} (100%) create mode 100644 app/views/tasks/show.html.erb rename test/functional/{task_controller_test.rb => tasks_controller_test.rb} (56%)
diff --git a/app/controllers/backlog_controller.rb b/app/controllers/backlog_controller.rb index 3e6ca56..6011264 100644 --- a/app/controllers/backlog_controller.rb +++ b/app/controllers/backlog_controller.rb @@ -28,6 +28,7 @@ class BacklogController < ApplicationController @task = Task.new @task.primary = @user if @user @users = User.find(:all, :conditions => "id in (select user_id from product_roles where product_id = #{@product.id})") + @tasks = @backlog_item.tasks end
def modify diff --git a/app/controllers/task_controller.rb b/app/controllers/task_controller.rb deleted file mode 100644 index 6c6e42b..0000000 --- a/app/controllers/task_controller.rb +++ /dev/null @@ -1,127 +0,0 @@ -# task_controller.rb -# Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com -# -# 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 3 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, see http://www.gnu.org/licenses/. -# - -# +TaskController+ allows the user to create and modify tasks for a backlog -# item. -# -class TaskController < ApplicationController - before_filter :authenticated, :except => [:view] - before_filter :load_backlog_item, :only => [:create] - before_filter :load_task, :only => [:modify, :delete] - before_filter :authorized_to_create, :only => [:create] - before_filter :authorized_to_manage, :only => [:modify, :delete] - before_filter :before_edit, :only => [:create, :modify] - before_filter :ensure_sprint_is_active, :only => [:create] - - def create - if request.post? - begin - Task.transaction do - @task = Task.create(params[:task]) - @task.backlog_item = @backlog_item - @backlog_item.tasks << @task - @backlog_item.save! - @task.save! - - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item - end - - rescue ActiveRecord::RecordInvalid => error - flash[:message] = error.message - @task.valid? - render :action => :create - end - else - @task = Task.new(:backlog_item => @backlog_item, :primary => @user) - end - end - - def modify - if request.post? - @task.update_attributes(params[:task]) - - @task.save - - redirect_to :controller => :backlog, :action => :view, :id => @task.backlog_item.id - end - end - - def delete - if request.post? - begin - Task.transaction do - @task.destroy - end - - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - - rescue Exception => error - flash[:message] = error.message - - redirect_to error_path - end - else - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end - - private - - def load_backlog_item - @backlog_item = BacklogItem.find_by_id(params[:backlog_item]) - - unless @backlog_item - flash[:message] = 'Invalid user id.' - redirect_to error_path - end - end - - def load_task - @task = Task.find_by_id(params[:id]) - @backlog_item = @task.backlog_item if @task - - unless @task - flash[:message] = 'Invalid task id.' - redirect_to error_path - end - end - - def authorized_to_create - unless TaskHelper.can_create_task(@user,@backlog_item) - flash[:message] = 'You are not authorized to work on tasks for this user story.' - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end - - def authorized_to_manage - unless TaskHelper.can_manage_task(@user,@task) - flash[:message] = 'You are not authorized to modify this task.' - redirect_to :controller => :backlog, :action => :view, :id => @task.backlog_item.id - end - end - - def before_edit - @users = User.find(:all, :order => 'display_name ASC') - end - - def ensure_sprint_is_active - unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE - flash[:message] = 'The sprint must be active for that action.' - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end -end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb new file mode 100644 index 0000000..af3aec0 --- /dev/null +++ b/app/controllers/tasks_controller.rb @@ -0,0 +1,167 @@ +# tasks_controller.rb +# Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com +# +# 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 3 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, see http://www.gnu.org/licenses/. +# + +# +TasksController+ allows the user to create and modify tasks for a backlog +# item. +# +class TasksController < ApplicationController + before_filter :authenticated, :except => [:index, :show] + before_filter :load_backlog_item, :only => [:index, :new, :create] + before_filter :load_task, :only => [:show, :edit, :update, :destroy] + before_filter :before_edit, :only => [:create, :update] + before_filter :ensure_sprint_is_active, :only => [:create] + + # GET /tasks + def index + @tasks = Task.find_all_by_backlog_item_id(@backlog_item.id) + + respond_to do |format| + format.html + end + end + + # GET /tasks/1 + def show + respond_to do |format| + format.html + end + end + + # GET /tasks/new + def new + if @backlog_item.user_can_add_tasks?(@user) + @task = Task.new( + :backlog_item_id => @backlog_item.id, + :primary_id => @user.id + ) + @users = User.find(:all, :order => 'display_name ASC') + else + flash[:error] = 'You are not allowed to add tasks to this backlog item.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + # GET /tasks/1;edit + def edit + unless @task.user_can_edit?(@user) + flash[:error] = "You are not allowd to edit this task." + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + @users = User.find(:all, :order => 'display_name ASC') + end + + # POST /tasks + def create + if @backlog_item.user_can_add_tasks?(@user) + Task.transaction do + @task = Task.new(params[:task]) + @task.backlog_item = @backlog_item + + respond_to do |format| + if @task.save + flash[:message] = "Recorded #{@task.hours} against this backlog item." + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + else + format.html { render :action => 'new' } + end + end + end + else + flash[:error] = "You are not allowed to add tasks to this backlog item." + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + # PUT /tasks/1 + def update + if @task.user_can_edit?(@user) + begin + Task.transaction do + @task.update_attributes(params[:task]) + @task.save! + end + + flash[:message] = "Task updated." + rescue Exception => error + flash[:error] = "ERROR: #{error.message}" + end + + respond_to do |format| + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + end + else + flash[:error] = "You are not allowed to update that task." + + @task.valid? + + respond_to do |format| + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + end + end + end + + # DELETE /tasks/1 + def destroy + if @task.user_can_delete?(@user) + begin + Task.transaction do + @task.destroy + end + + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + rescue Exception => error + flash[:error] = "ERROR: #{error.message}" + redirect_to error_url + end + else + flash[:error] = 'You are not allowed to delete this task.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + private + + def load_backlog_item + @backlog_item = BacklogItem.find_by_id(params[:backlog_item]) + + unless @backlog_item + flash[:message] = 'Invalid user id.' + redirect_to error_path + end + end + + def load_task + @task = Task.find_by_id(params[:id]) + @backlog_item = @task.backlog_item if @task + + unless @task + flash[:message] = 'Invalid task id.' + redirect_to error_path + end + end + + def before_edit + @users = User.find(:all, :order => 'display_name ASC') + end + + def ensure_sprint_is_active + unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE + flash[:message] = 'The sprint must be active for that action.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id + end + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f20df88..96d59a7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -102,15 +102,6 @@ module ApplicationHelper :id => backlog_item.id) end
- # Creates a link to a task. - # - def link_to_task(task, options = {}) - @template.link_to(options[:text] ? options[:text] : task.id, - :controller => :task, - :action => options[:action] ? options[:action] : :view, - :id => task.id) - end - # Shows the hours for a given object as estimated, actual and remaining. # def show_hours_as_ear(estimated) diff --git a/app/models/task.rb b/app/models/task.rb index 89af73d..9173f64 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,22 +1,22 @@ # task.rb # Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com # -# 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 3 of the License, or -# (at your option) any later version. +# 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 3 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. +# 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, see http://www.gnu.org/licenses/. +# You should have received a copy of the GNU General Public License along with +# this program. If not, see http://www.gnu.org/licenses/. #
# A +Task+ represents a single piece of work associated with a +UserStory+. -# Tasks have a primary developer, and an optional backup developer. +# Tasks have a primary developer, and an optional backup developer. class Task < ActiveRecord::Base validates_presence_of :backlog_item_id
@@ -24,16 +24,32 @@ class Task < ActiveRecord::Base belongs_to :backup, :class_name => 'User', :foreign_key => 'backup_id'
validates_presence_of :description, - :message => 'You need to describe this task.' + :message => 'You need to describe this task.' validates_length_of :description, - :maximum => 1000, - :too_long => 'Please shorten the description.' + :maximum => 1000, + :too_long => 'Please shorten the description.'
validates_presence_of :hours, - :message => 'You must report the hours worked on this task.' + :message => 'You must report the hours worked on this task.' validates_numericality_of :hours, - :greater_than => 0, - :message => 'Hours must be numeric and greater than 0.' + :greater_than => 0, + :message => 'Hours must be numeric and greater than 0.'
belongs_to :backlog_item + + # Returns whether the user can edit the task. + # + def user_can_edit?(user) + user && + backlog_item.sprint.active? && + (primary && (primary.id == user.id)) + end + + # Returns whether the user can delete the task or not. + # + def user_can_delete?(user) + user && + (backlog_item.owner && (backlog_item.owner.id == user.id)) && + backlog_item.sprint.active? + end end diff --git a/app/views/backlog/view.html.erb b/app/views/backlog/view.html.erb index c02f27e..8ee0333 100644 --- a/app/views/backlog/view.html.erb +++ b/app/views/backlog/view.html.erb @@ -63,67 +63,5 @@ </tbody> </table>
-<table class="list"> - <colgroup> - <col class="row_id" /> - <col class="description" /> - <col class="user" /> - <col class="user" /> - <col class="hours" /> - <col class="date" /> - <col class="actions" /> - </colgroup> - - <thead> - <tr> - <th colspan="7" class="title">Tasks</th> - </tr> - <tr> - <th>##</th> - <th>Description</th> - <th>Primary</th> - <th>Backup</th> - <th>Hours</th> - <th>Date</th> - <th>Action</th> - </tr> - </thead> - - <tbody> - - <% if @backlog_item.user_can_add_tasks?(@user) %> - <% form_for :task, :url => {:controller => :task, :action => :create} do |form| %> - <%= hidden_field_tag :backlog_item, @backlog_item.id %> - <tr> - <td></td> - <td><%= form.text_field :description %></td> - <td><%= collection_select :task, :primary_id, @users, :id, - :display_name, {:include_blank => true} %></td> - <td><%= collection_select :task, :backup_id, @users, :id, - :display_name, {:include_blank => true} %></td> - <td><%= form.text_field :hours %></td> - <td><%= form.date_select :when %></td> - <td><%= submit_tag "Create" %></td> - </tr> - <% end %> - <% end %> - - <% @backlog_item.tasks.each_with_index do |task,i| %> - <tr class="<% row_class = i%2 == 0 ? 'even' : 'odd' %>"> - - <td><%= link_to_task task, :action => :modify %></td> - <td><%= simple_format task.description %></td> - <td><%= link_to_user task.primary %></td> - <td><%= link_to_user task.backup %></td> - <td><%= task.hours %></td> - <td><%= task.when.to_s(:date) %></td> - <td> - <% form_for :task, :url => {:controller => :task, :action => :delete} do |f| %> - <%= hidden_field_tag :id, task.id %> - <%= submit_tag "Delete" %> - <% end %> - </td> - </tr> - <% end %> - </tbody> -</table> +<%= link_to "Add Task", new_task_path(:backlog_item => @backlog_item) %> +<%= render(:partial => 'tasks/list', :object => @backlog_item.tasks) %> diff --git a/app/views/task/_edit.html.erb b/app/views/tasks/_edit.html.erb similarity index 73% rename from app/views/task/_edit.html.erb rename to app/views/tasks/_edit.html.erb index 4195c45..8fa5ad1 100644 --- a/app/views/task/_edit.html.erb +++ b/app/views/tasks/_edit.html.erb @@ -1,36 +1,41 @@ <div> - <% form_for :task do |form| %> - + <% form_for(:task, @task, :url => tasks_path) do |form| %> + <%= hidden_field_tag :backlog_item, @task.backlog_item.id %> - + <table class="edit"> <tbody> <tr> - <td class="label">Primary:</td> + <td class="label">When</td> + <td class="value"><%= form.date_select :when %></td> + </tr> + + <tr> + <td class="label">Primary</td> <td class="value"> <%= collection_select :task, :primary_id, @users, :id, :display_name, {:include_blank => true} %> </td> </tr> - + <tr> - <td class="label">Backup:</td> + <td class="label">Backup</td> <td class="value"> <%= collection_select :task, :backup_id, @users, :id, :display_name, {:include_blank => true} %> </td> </tr> - + <tr> - <td class="label">Hours:</td> + <td class="label">Hours</td> <td class="value"><%= form.text_field :hours %></td> </tr> - + <tr> - <td class="label">Description:</td> + <td class="label">Description</td> <td class="value"><%= form.text_area :description %></td> </tr> - + <tr> <td class="buttons" colspan="2"> <% if @task.new_record? %> @@ -42,6 +47,6 @@ </tr> </tbody> </table> - + <% end %> -</div> \ No newline at end of file +</div> diff --git a/app/views/tasks/_list.html.erb b/app/views/tasks/_list.html.erb new file mode 100644 index 0000000..273296e --- /dev/null +++ b/app/views/tasks/_list.html.erb @@ -0,0 +1,34 @@ +<table class="list"> + <thead> + <tr> + <th>Date</th> + <th>Description</th> + <th>Primary</th> + <th>Backup</th> + <th>Hours</th> + <th>Actions</th> + </tr> + </thead> + + <tbody> + <% @tasks.each_with_index do |task, index| %> + <% row_class = index%2 == 0 ? 'even' : 'odd' %> + <tr class="<%= row_class %>"> + <td><%= task.when %></td> + <td><%= simple_format task.description %></td> + <td><%= link_to_user task.primary %></td> + <td><%= link_to_user task.backup %></td> + <td><%= format "%0.2f", task.hours %> + <td> + <%= link_to "View", task_path(task) %> + <% if task.user_can_edit?(@user) %> + <%= link_to "Edit", edit_task_path(task) %> + <% end %> + <% if task.user_can_delete?(@user) %> + <%= link_to "Delete", task_path(task), :confirm => "Are you sure?", :method => :delete %> + <% end %> + </td> + </tr> + <% end %> + </tbody> +</table> diff --git a/app/views/task/modify.html.erb b/app/views/tasks/edit.html.erb similarity index 100% rename from app/views/task/modify.html.erb rename to app/views/tasks/edit.html.erb diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb new file mode 100644 index 0000000..0a9c778 --- /dev/null +++ b/app/views/tasks/index.html.erb @@ -0,0 +1 @@ +<%= render(:partial => 'list') %> diff --git a/app/views/task/create.html.erb b/app/views/tasks/new.html.erb similarity index 100% rename from app/views/task/create.html.erb rename to app/views/tasks/new.html.erb diff --git a/app/views/tasks/show.html.erb b/app/views/tasks/show.html.erb new file mode 100644 index 0000000..fb0cfb9 --- /dev/null +++ b/app/views/tasks/show.html.erb @@ -0,0 +1,35 @@ +<table class="details"> + <tr> + <td class="label">Description:</td> + <td class="value"><%= simple_format @task.description %></td> + </tr> + + <tr> + <td class="label">When:</td> + <td class="value"><%= @task.when %></td> + </tr> + + <tr> + <td class="label">Hours:</td> + <td class="value"><%= format "%0.2f", @task.hours %></td> + </tr> + + <tr> + <td class="label">Primary:</td> + <td class="value"><%= link_to_user @task.primary %></td> + </tr> + + <tr> + <td class="label">Backup:</td> + <td class="label"><%= link_to_user @task.backup %></td> + </tr> + +</table> + +<%= link_to "Back", :controller => :backlog, :action => :view, :id => @task.backlog_item %> +<% if @task.user_can_edit?(@user) %> + <% link_to "Edit", edit_task_path(@task) %> +<% end %> +<% if @task.user_can_delete?(@user) %> + <%= link_to "Delete", task_path(@task), :confirm => "Are you sure?", :method => :delete %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 874f8e9..2f66a65 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,10 +16,15 @@ #
ActionController::Routing::Routes.draw do |map| + map.resources :tasks + # You can have the root of your site routed with map.root -- just remember to # delete public/index.html. map.root :controller => 'project'
+ # Login URL + map.login "/login", :controller => 'home', :action => 'login' + # Error handling map.error "/error", :controller => 'home', :action => 'error'
diff --git a/test/functional/task_controller_test.rb b/test/functional/tasks_controller_test.rb similarity index 56% rename from test/functional/task_controller_test.rb rename to test/functional/tasks_controller_test.rb index 92551da..270b308 100644 --- a/test/functional/task_controller_test.rb +++ b/test/functional/tasks_controller_test.rb @@ -1,224 +1,267 @@ -# task_controller_test.rb +# tasks_controller_test.rb # Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com -# +# # 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 3 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, see http://www.gnu.org/licenses/. -# +#
require File.dirname(__FILE__) + '/../test_helper'
-class TaskControllerTest < ActionController::TestCase +class TasksControllerTest < ActionController::TestCase fixtures :backlog_items fixtures :users - + def setup @backlog_item = backlog_items(:owned_backlog_item) @task = @backlog_item.tasks.first @owner = @backlog_item.owner - @non_owner = users(:jdonuts) + @nonowner = users(:jdonuts) + raise "Owner and nonowner cannot be the same person!" if @owner.id == @nonowner.id + @inactive_sprint_item = backlog_items(:owned_inactive_sprint_backlog_item) - - flunk "FIXTURES ARE BROKEN" if @owner.id == @non_owner.id - + @details = { :primary_id => @backlog_item.owner.id, :description => 'This is a new task.', :hours => '2.5', :when => Date.today - } + } end - - # Ensures that an invalid user story causes create to fail. - # - def test_create_with_invalid_user_story - get :create, - nil, - {:user_id => users(:mcpierce).id} - - assert_redirected_to error_path - - get :create, - {:user_story => 9999}, - {:user_id => users(:mcpierce).id} - - assert_redirected_to error_path + + # Ensures that viewing tasks when no backlog item is specified fails. + # + def test_index_without_backlog_item + get :index + + assert_redirected_to error_url end - - # Ensures that an anonymous user cannot create a task. - # - def test_create_for_anonymous - get :create, - {:backlog_item => @backlog_item.id} - - assert_redirected_to :controller => :home, :action => :login + + # Ensures that getting the list of tasks for a backlog item works as expected. + # + def test_index + get :index, {:backlog_item => @backlog_item.id} + + assert_response :success + assert assigns['backlog_item'], "Failed to load the backlog item." + assert_equal @backlog_item.id, assigns['backlog_item'].id, + "Did not load the right backlog item." + assert assigns['tasks'], "Failed to load the tasks." end - - # Ensures that a user who doesn't have authorization aren't able to create - # tasks. - # - def test_create_for_unauthorized_user - get :create, {:backlog_item => @backlog_item.id}, {:user_id => users(:jdonuts).id} - - assert_redirected_to :controller => :backlog, :action => :view + + # Ensures that an error is show if no task id is present. + # + def test_show_without_task_id + get :show + + assert_redirected_to error_url end - - # Ensures that tasks cannot be created for a backlog item on an non-active - # sprint. + + # Ensures that creating a task requires the user to be logged in. # - def test_create_for_nonactive_sprint - count = @inactive_sprint_item.tasks.size - - get :create, - {:backlog_item => @inactive_sprint_item.id}, - {:user_id => @inactive_sprint_item.owner.id} - - assert_redirected_to :controller => :backlog, :action => :view, :id => @inactive_sprint_item.id - result = BacklogItem.find_by_id(@inactive_sprint_item.id) - assert_equal count, result.tasks.size, 'Tasks should not have been updated' + def test_new_for_anonymous + get :new + + assert_redirected_to login_url + end + + # Ensures that creating a task without a backlog item fails. + # + def test_new_without_backlog_item + get :new, nil, {:user_id => @owner.id} + + assert_redirected_to error_url end - + + # Ensures that a non-owner cannot create tasks for a backlog item. + # + def test_new_for_nonowner + get :new, + {:backlog_item => @backlog_item.id}, + {:user_id => @nonowner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item.id + end + # Ensures that creating a task works as expected. - # - def test_create - get :create, - { - :backlog_item => @backlog_item.id, - :task => @details - }, - {:user_id => users(:mcpierce).id} - + # + def test_new + get :new, + {:backlog_item => @backlog_item.id}, + {:user_id => @owner.id} + assert_response :success - assert assigns['task'], 'Did not create a new task.' - assert assigns['users'], 'Did not load users.' + assert assigns['task'], "Didn't create a task." + assert assigns['users'], "Didn't load the list of users." + assert_equal @backlog_item.id, assigns['task'].backlog_item_id, + "Didn't set the backlog item correctly." + assert_equal @owner.id, assigns['task'].primary_id, + "Didn't set the primary developer correctly." end - - # Ensures that tasks are saved corrected. - # - def test_create_as_save - post :create, - { - :backlog_item => @backlog_item.id, - :task => @details - }, - {:user_id => users(:mcpierce).id} - - assert_redirected_to :controller => :backlog, :action => :view - end - + # Ensures that attempting to modify an invalid task fails. - # - def test_modify_with_invalid_task - get :modify, nil, {:user_id => users(:mcpierce).id} - + # + def test_edit_with_invalid_task + get :edit, nil, {:user_id => users(:mcpierce).id} + assert_redirected_to error_path - - get :modify, {:id => 9999} - + + get :edit, {:id => 9999} + assert_redirected_to error_path end - + # Ensures that anonymous users cannot modify tasks. - # - def test_modify_for_anonymous - get :modify, {:id => tasks(:created_login_view).id} - + # + def test_edit_for_anonymous + get :edit, {:id => tasks(:created_login_view).id} + assert_redirected_to :controller => :home, :action => :login end - + # Ensures that a user who's not the task owner or backup cannot modify the # task. - # - def test_modify_for_non_developer - get :modify, + # + def test_edit_for_non_developer + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => users(:project_admin).id} - + assert_redirected_to :controller => :backlog, :action => :view end - + # Ensures that the owner of the user story is allowed to edit a task against # that user story. - # - def test_modify_for_user_story_owner - get :modify, + # + def test_edit_for_user_story_owner + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => users(:mcpierce).id} - + assert_response :success end - + # Ensures that modifying works as expected. - # - def test_modify - get :modify, + # + def test_edit + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => tasks(:created_login_view).primary_id} - - assert_response :success + + assert_response :success assert assigns['task'], 'Did not load the task.' assert_equal tasks(:created_login_view).id, assigns['task'].id, 'Did not load the right task.' assert assigns['users'], 'Did not load the series.' end - - # Ensures that saving a task after modifying it works as expected. - # - def test_modify_as_save - task = tasks(:created_login_view).attributes - - post :modify, - {:id => tasks(:created_login_view), :task => task}, - {:user_id => tasks(:created_login_view).primary_id} - - assert_redirected_to :controller => :backlog, :action => :view + + # Ensures that an invalid user story causes create to fail. + # + def test_create_with_invalid_user_story + get :create, + nil, + {:user_id => users(:mcpierce).id} + + assert_redirected_to error_path + + get :create, + {:user_story => 9999}, + {:user_id => users(:mcpierce).id} + + assert_redirected_to error_path + end + + # Ensures that an anonymous user cannot create a task. + # + def test_create_for_anonymous + get :create, + {:backlog_item => @backlog_item.id} + + assert_redirected_to :controller => :home, :action => :login + end + + # Ensures that a user who doesn't have authorization aren't able to create + # tasks. + # + def test_create_for_unauthorized_user + get :create, {:backlog_item => @backlog_item.id}, {:user_id => users(:jdonuts).id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + end + + # Ensures that tasks cannot be created for a backlog item on an non-active + # sprint. + # + def test_create_for_nonactive_sprint + count = @inactive_sprint_item.tasks.size + + get :create, + {:backlog_item => @inactive_sprint_item.id}, + {:user_id => @inactive_sprint_item.owner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @inactive_sprint_item.id + result = BacklogItem.find_by_id(@inactive_sprint_item.id) + assert_equal count, result.tasks.size, 'Tasks should not have been updated' end - + + # Ensures that tasks are saved corrected. + # + def test_create + post :create, + { + :backlog_item => @backlog_item.id, + :task => @details + }, + {:user_id => users(:mcpierce).id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + end + # Ensures that anonymous users can't delete tasks - # - def test_delete_as_anonymous - get :delete - + # + def test_destroy_as_anonymous + get :destroy + assert_redirected_to :controller => :home, :action => :login end - + # Ensures that deletion requires a valid task id. - # - def test_delete_with_invalid_id - post :delete, + # + def test_destroy_with_invalid_id + post :destroy, {:id => 9999}, {:user_id => @owner.id} - + assert_redirected_to error_path end - + # Ensures that someone who is not the backlog item owner can't add tasks to # it. - # - def test_delete_as_non_owner - post :delete, + # + def test_destroy_as_non_owner + post :destroy, { :id => @task.id }, { - :user_id => @non_owner.id + :user_id => @nonowner.id } - + assert Task.find_by_id(@task.id), 'Task should not have been deleted.' end - + # Ensures that the primary can delete a task. - # - def test_delete - post :delete, + # + def test_destroy + post :destroy, { :id => @task.id }, @@ -226,8 +269,45 @@ class TaskControllerTest < ActionController::TestCase
assert_redirected_to( :controller => :backlog, - :action => :view, + :action => :view, :id => @task.backlog_item.id) assert_equal nil, Task.find_by_id(@task.id), 'Did not delete the task.' end + + # Ensures that an update requires the user be loggeg in. + # + def test_update_as_anonymous + put :update + + assert_redirected_to login_url + end + + # Ensures that an update fails without a task id. + # + def test_update_without_id + put :update, {}, {:user_id => @owner.id} + + assert_redirected_to error_url + end + + # Ensures that an update fails with an invalid id. + # + def test_update_with_invalid_id + put :update, + {:id => 9999}, + {:user_id => @owner.id} + end + + # Ensures that a non-owner cannot update a task. + # + def test_update_as_nonowner + put :update, + {:id => @task.id, :task => @task.attributes}, + {:user_id => @nonowner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + result = Task.find_by_id(@task.id) + assert_equal @task.updated_at, result.updated_at, + "Task should not have been updated." + end end diff --git a/test/unit/product_test.rb b/test/unit/product_test.rb index a0df74d..62647db 100644 --- a/test/unit/product_test.rb +++ b/test/unit/product_test.rb @@ -25,7 +25,8 @@ class ProductTest < ActiveSupport::TestCase def setup @new_product = Product.new( :project_id => projects(:cairo).id, - :owner_id => users(:mcpierce).id) + :owner_id => users(:mcpierce).id, + :name => "Test Product")
@product = products(:cairo_web) @owner = @product.owner
On Wed, Oct 29, 2008 at 8:11 AM, Darryl L. Pierce mcpierce@gmail.com wrote:
Renamed TaskController and TaskControllerTest to TasksController and TasksControllerTest. Added a new resource to routes.rb to provide RESTful methods for working with Task objects. Updated all of the unit tests and renamed and refactored the actions in the controller.
This patch addresses some small bugs I found while doing more testing.
NAK - At the add task view's, I leave blank the description's field. When I click on "create", no flash message raise to say that description can't be empty.
Le 29/10/08 16:11, « Darryl L. Pierce » mcpierce@gmail.com a écrit :
Renamed TaskController and TaskControllerTest to TasksController and TasksControllerTest. Added a new resource to routes.rb to provide RESTful methods for working with Task objects. Updated all of the unit tests and renamed and refactored the actions in the controller.
Signed-off-by: Darryl L. Pierce dpierce@redhat.com --- app/controllers/backlog_controller.rb | 1 + app/controllers/task_controller.rb | 127 ------- app/controllers/tasks_controller.rb | 167 +++++++++ app/helpers/application_helper.rb | 9 - app/models/task.rb | 50 ++- app/views/backlog/view.html.erb | 66 +---- app/views/{task => tasks}/_edit.html.erb | 31 +- app/views/tasks/_list.html.erb | 34 ++ .../{task/modify.html.erb => tasks/edit.html.erb} | 0 app/views/tasks/index.html.erb | 1 + .../{task/create.html.erb => tasks/new.html.erb} | 0 app/views/tasks/show.html.erb | 35 ++ config/routes.rb | 5 + ...controller_test.rb => tasks_controller_test.rb} | 364 ++++++++++++-------- test/unit/product_test.rb | 3 +- 15 files changed, 520 insertions(+), 373 deletions(-) delete mode 100644 app/controllers/task_controller.rb create mode 100644 app/controllers/tasks_controller.rb rename app/views/{task => tasks}/_edit.html.erb (73%) create mode 100644 app/views/tasks/_list.html.erb rename app/views/{task/modify.html.erb => tasks/edit.html.erb} (100%) create mode 100644 app/views/tasks/index.html.erb rename app/views/{task/create.html.erb => tasks/new.html.erb} (100%) create mode 100644 app/views/tasks/show.html.erb rename test/functional/{task_controller_test.rb => tasks_controller_test.rb} (56%)
diff --git a/app/controllers/backlog_controller.rb b/app/controllers/backlog_controller.rb index 3e6ca56..6011264 100644 --- a/app/controllers/backlog_controller.rb +++ b/app/controllers/backlog_controller.rb @@ -28,6 +28,7 @@ class BacklogController < ApplicationController @task = Task.new @task.primary = @user if @user @users = User.find(:all, :conditions => "id in (select user_id from product_roles where product_id = #{@product.id})") + @tasks = @backlog_item.tasks end
def modify diff --git a/app/controllers/task_controller.rb b/app/controllers/task_controller.rb deleted file mode 100644 index 6c6e42b..0000000 --- a/app/controllers/task_controller.rb +++ /dev/null @@ -1,127 +0,0 @@ -# task_controller.rb -# Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com -# -# 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 3 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, see http://www.gnu.org/licenses/. -# - -# +TaskController+ allows the user to create and modify tasks for a backlog -# item. -# -class TaskController < ApplicationController - before_filter :authenticated, :except => [:view] - before_filter :load_backlog_item, :only => [:create] - before_filter :load_task, :only => [:modify, :delete] - before_filter :authorized_to_create, :only => [:create] - before_filter :authorized_to_manage, :only => [:modify, :delete] - before_filter :before_edit, :only => [:create, :modify] - before_filter :ensure_sprint_is_active, :only => [:create] - - def create - if request.post? - begin - Task.transaction do - @task = Task.create(params[:task]) - @task.backlog_item = @backlog_item - @backlog_item.tasks << @task - @backlog_item.save! - @task.save! - - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item - end - - rescue ActiveRecord::RecordInvalid => error - flash[:message] = error.message - @task.valid? - render :action => :create - end - else - @task = Task.new(:backlog_item => @backlog_item, :primary => @user) - end - end - - def modify - if request.post? - @task.update_attributes(params[:task]) - - @task.save - - redirect_to :controller => :backlog, :action => :view, :id => @task.backlog_item.id - end - end - - def delete - if request.post? - begin - Task.transaction do - @task.destroy - end - - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - - rescue Exception => error - flash[:message] = error.message - - redirect_to error_path - end - else - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end - - private - - def load_backlog_item - @backlog_item = BacklogItem.find_by_id(params[:backlog_item]) - - unless @backlog_item - flash[:message] = 'Invalid user id.' - redirect_to error_path - end - end - - def load_task - @task = Task.find_by_id(params[:id]) - @backlog_item = @task.backlog_item if @task - - unless @task - flash[:message] = 'Invalid task id.' - redirect_to error_path - end - end - - def authorized_to_create - unless TaskHelper.can_create_task(@user,@backlog_item) - flash[:message] = 'You are not authorized to work on tasks for this user story.' - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end - - def authorized_to_manage - unless TaskHelper.can_manage_task(@user,@task) - flash[:message] = 'You are not authorized to modify this task.' - redirect_to :controller => :backlog, :action => :view, :id => @task.backlog_item.id - end - end - - def before_edit - @users = User.find(:all, :order => 'display_name ASC') - end - - def ensure_sprint_is_active - unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE - flash[:message] = 'The sprint must be active for that action.' - redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id - end - end -end diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb new file mode 100644 index 0000000..af3aec0 --- /dev/null +++ b/app/controllers/tasks_controller.rb @@ -0,0 +1,167 @@ +# tasks_controller.rb +# Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com +# +# 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 3 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, see http://www.gnu.org/licenses/. +# + +# +TasksController+ allows the user to create and modify tasks for a backlog +# item. +# +class TasksController < ApplicationController + before_filter :authenticated, :except => [:index, :show] + before_filter :load_backlog_item, :only => [:index, :new, :create] + before_filter :load_task, :only => [:show, :edit, :update, :destroy] + before_filter :before_edit, :only => [:create, :update] + before_filter :ensure_sprint_is_active, :only => [:create] + + # GET /tasks + def index + @tasks = Task.find_all_by_backlog_item_id(@backlog_item.id) + + respond_to do |format| + format.html + end + end + + # GET /tasks/1 + def show + respond_to do |format| + format.html + end + end + + # GET /tasks/new + def new + if @backlog_item.user_can_add_tasks?(@user) + @task = Task.new( + :backlog_item_id => @backlog_item.id, + :primary_id => @user.id + ) + @users = User.find(:all, :order => 'display_name ASC') + else + flash[:error] = 'You are not allowed to add tasks to this backlog item.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + # GET /tasks/1;edit + def edit + unless @task.user_can_edit?(@user) + flash[:error] = "You are not allowd to edit this task." + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + @users = User.find(:all, :order => 'display_name ASC') + end + + # POST /tasks + def create + if @backlog_item.user_can_add_tasks?(@user) + Task.transaction do + @task = Task.new(params[:task]) + @task.backlog_item = @backlog_item + + respond_to do |format| + if @task.save + flash[:message] = "Recorded #{@task.hours} against this backlog item." + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + else + format.html { render :action => 'new' } + end + end + end + else + flash[:error] = "You are not allowed to add tasks to this backlog item." + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + # PUT /tasks/1 + def update + if @task.user_can_edit?(@user) + begin + Task.transaction do + @task.update_attributes(params[:task]) + @task.save! + end + + flash[:message] = "Task updated." + rescue Exception => error + flash[:error] = "ERROR: #{error.message}" + end + + respond_to do |format| + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + end + else + flash[:error] = "You are not allowed to update that task." + + @task.valid? + + respond_to do |format| + format.html { redirect_to :controller => :backlog, :action => :view, :id => @backlog_item } + end + end + end + + # DELETE /tasks/1 + def destroy + if @task.user_can_delete?(@user) + begin + Task.transaction do + @task.destroy + end + + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + rescue Exception => error + flash[:error] = "ERROR: #{error.message}" + redirect_to error_url + end + else + flash[:error] = 'You are not allowed to delete this task.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item + end + end + + private + + def load_backlog_item + @backlog_item = BacklogItem.find_by_id(params[:backlog_item]) + + unless @backlog_item + flash[:message] = 'Invalid user id.' + redirect_to error_path + end + end + + def load_task + @task = Task.find_by_id(params[:id]) + @backlog_item = @task.backlog_item if @task + + unless @task + flash[:message] = 'Invalid task id.' + redirect_to error_path + end + end + + def before_edit + @users = User.find(:all, :order => 'display_name ASC') + end + + def ensure_sprint_is_active + unless @backlog_item.sprint.status == Sprint::STATUS_ACTIVE + flash[:message] = 'The sprint must be active for that action.' + redirect_to :controller => :backlog, :action => :view, :id => @backlog_item.id + end + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f20df88..96d59a7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -102,15 +102,6 @@ module ApplicationHelper :id => backlog_item.id) end
- # Creates a link to a task. - # - def link_to_task(task, options = {}) - @template.link_to(options[:text] ? options[:text] : task.id, - :controller => :task, - :action => options[:action] ? options[:action] : :view, - :id => task.id) - end - # Shows the hours for a given object as estimated, actual and remaining. # def show_hours_as_ear(estimated) diff --git a/app/models/task.rb b/app/models/task.rb index 89af73d..9173f64 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,22 +1,22 @@ # task.rb # Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com # -# 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 3 of the License, or -# (at your option) any later version. +# 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 3 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. +# 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, see http://www.gnu.org/licenses/. +# You should have received a copy of the GNU General Public License along with +# this program. If not, see http://www.gnu.org/licenses/. #
# A +Task+ represents a single piece of work associated with a +UserStory+. -# Tasks have a primary developer, and an optional backup developer. +# Tasks have a primary developer, and an optional backup developer. class Task < ActiveRecord::Base validates_presence_of :backlog_item_id
@@ -24,16 +24,32 @@ class Task < ActiveRecord::Base belongs_to :backup, :class_name => 'User', :foreign_key => 'backup_id'
validates_presence_of :description, - :message => 'You need to describe this task.' + :message => 'You need to describe this task.' validates_length_of :description, - :maximum => 1000, - :too_long => 'Please shorten the description.' + :maximum => 1000, + :too_long => 'Please shorten the description.'
validates_presence_of :hours, - :message => 'You must report the hours worked on this task.' + :message => 'You must report the hours worked on this task.' validates_numericality_of :hours, - :greater_than => 0, - :message => 'Hours must be numeric and greater than 0.' + :greater_than => 0, + :message => 'Hours must be numeric and greater than 0.'
belongs_to :backlog_item + + # Returns whether the user can edit the task. + # + def user_can_edit?(user) + user && + backlog_item.sprint.active? && + (primary && (primary.id == user.id)) + end + + # Returns whether the user can delete the task or not. + # + def user_can_delete?(user) + user && + (backlog_item.owner && (backlog_item.owner.id == user.id)) && + backlog_item.sprint.active? + end end diff --git a/app/views/backlog/view.html.erb b/app/views/backlog/view.html.erb index c02f27e..8ee0333 100644 --- a/app/views/backlog/view.html.erb +++ b/app/views/backlog/view.html.erb @@ -63,67 +63,5 @@ </tbody> </table>
-<table class="list"> - <colgroup> - <col class="row_id" /> - <col class="description" /> - <col class="user" /> - <col class="user" /> - <col class="hours" /> - <col class="date" /> - <col class="actions" /> - </colgroup> - - <thead> - <tr> - <th colspan="7" class="title">Tasks</th> - </tr> - <tr> - <th>##</th> - <th>Description</th> - <th>Primary</th> - <th>Backup</th> - <th>Hours</th> - <th>Date</th> - <th>Action</th> - </tr> - </thead> - - <tbody> - - <% if @backlog_item.user_can_add_tasks?(@user) %> - <% form_for :task, :url => {:controller => :task, :action => :create} do |form| %> - <%= hidden_field_tag :backlog_item, @backlog_item.id %> - <tr> - <td></td> - <td><%= form.text_field :description %></td> - <td><%= collection_select :task, :primary_id, @users, :id, - :display_name, {:include_blank => true} %></td> - <td><%= collection_select :task, :backup_id, @users, :id, - :display_name, {:include_blank => true} %></td> - <td><%= form.text_field :hours %></td> - <td><%= form.date_select :when %></td> - <td><%= submit_tag "Create" %></td> - </tr> - <% end %> - <% end %> - - <% @backlog_item.tasks.each_with_index do |task,i| %> - <tr class="<% row_class = i%2 == 0 ? 'even' : 'odd' %>"> - - <td><%= link_to_task task, :action => :modify %></td> - <td><%= simple_format task.description %></td> - <td><%= link_to_user task.primary %></td> - <td><%= link_to_user task.backup %></td> - <td><%= task.hours %></td> - <td><%= task.when.to_s(:date) %></td> - <td> - <% form_for :task, :url => {:controller => :task, :action => :delete} do |f| %> - <%= hidden_field_tag :id, task.id %> - <%= submit_tag "Delete" %> - <% end %> - </td> - </tr> - <% end %> - </tbody> -</table> +<%= link_to "Add Task", new_task_path(:backlog_item => @backlog_item) %> +<%= render(:partial => 'tasks/list', :object => @backlog_item.tasks) %> diff --git a/app/views/task/_edit.html.erb b/app/views/tasks/_edit.html.erb similarity index 73% rename from app/views/task/_edit.html.erb rename to app/views/tasks/_edit.html.erb index 4195c45..8fa5ad1 100644 --- a/app/views/task/_edit.html.erb +++ b/app/views/tasks/_edit.html.erb @@ -1,36 +1,41 @@ <div> - <% form_for :task do |form| %> - + <% form_for(:task, @task, :url => tasks_path) do |form| %> + <%= hidden_field_tag :backlog_item, @task.backlog_item.id %> - + <table class="edit"> <tbody> <tr> - <td class="label">Primary:</td> + <td class="label">When</td> + <td class="value"><%= form.date_select :when %></td> + </tr> + + <tr> + <td class="label">Primary</td> <td class="value"> <%= collection_select :task, :primary_id, @users, :id, :display_name, {:include_blank => true} %> </td> </tr> - + <tr> - <td class="label">Backup:</td> + <td class="label">Backup</td> <td class="value"> <%= collection_select :task, :backup_id, @users, :id, :display_name, {:include_blank => true} %> </td> </tr> - + <tr> - <td class="label">Hours:</td> + <td class="label">Hours</td> <td class="value"><%= form.text_field :hours %></td> </tr> - + <tr> - <td class="label">Description:</td> + <td class="label">Description</td> <td class="value"><%= form.text_area :description %></td> </tr> - + <tr> <td class="buttons" colspan="2"> <% if @task.new_record? %> @@ -42,6 +47,6 @@ </tr> </tbody> </table> - + <% end %> -</div> \ No newline at end of file +</div> diff --git a/app/views/tasks/_list.html.erb b/app/views/tasks/_list.html.erb new file mode 100644 index 0000000..273296e --- /dev/null +++ b/app/views/tasks/_list.html.erb @@ -0,0 +1,34 @@ +<table class="list"> + <thead> + <tr> + <th>Date</th> + <th>Description</th> + <th>Primary</th> + <th>Backup</th> + <th>Hours</th> + <th>Actions</th> + </tr> + </thead> + + <tbody> + <% @tasks.each_with_index do |task, index| %> + <% row_class = index%2 == 0 ? 'even' : 'odd' %> + <tr class="<%= row_class %>"> + <td><%= task.when %></td> + <td><%= simple_format task.description %></td> + <td><%= link_to_user task.primary %></td> + <td><%= link_to_user task.backup %></td> + <td><%= format "%0.2f", task.hours %> + <td> + <%= link_to "View", task_path(task) %> + <% if task.user_can_edit?(@user) %> + <%= link_to "Edit", edit_task_path(task) %> + <% end %> + <% if task.user_can_delete?(@user) %> + <%= link_to "Delete", task_path(task), :confirm => "Are you sure?", :method => :delete %> + <% end %> + </td> + </tr> + <% end %> + </tbody> +</table> diff --git a/app/views/task/modify.html.erb b/app/views/tasks/edit.html.erb similarity index 100% rename from app/views/task/modify.html.erb rename to app/views/tasks/edit.html.erb diff --git a/app/views/tasks/index.html.erb b/app/views/tasks/index.html.erb new file mode 100644 index 0000000..0a9c778 --- /dev/null +++ b/app/views/tasks/index.html.erb @@ -0,0 +1 @@ +<%= render(:partial => 'list') %> diff --git a/app/views/task/create.html.erb b/app/views/tasks/new.html.erb similarity index 100% rename from app/views/task/create.html.erb rename to app/views/tasks/new.html.erb diff --git a/app/views/tasks/show.html.erb b/app/views/tasks/show.html.erb new file mode 100644 index 0000000..fb0cfb9 --- /dev/null +++ b/app/views/tasks/show.html.erb @@ -0,0 +1,35 @@ +<table class="details"> + <tr> + <td class="label">Description:</td> + <td class="value"><%= simple_format @task.description %></td> + </tr> + + <tr> + <td class="label">When:</td> + <td class="value"><%= @task.when %></td> + </tr> + + <tr> + <td class="label">Hours:</td> + <td class="value"><%= format "%0.2f", @task.hours %></td> + </tr> + + <tr> + <td class="label">Primary:</td> + <td class="value"><%= link_to_user @task.primary %></td> + </tr> + + <tr> + <td class="label">Backup:</td> + <td class="label"><%= link_to_user @task.backup %></td> + </tr> + +</table> + +<%= link_to "Back", :controller => :backlog, :action => :view, :id => @task.backlog_item %> +<% if @task.user_can_edit?(@user) %> + <% link_to "Edit", edit_task_path(@task) %> +<% end %> +<% if @task.user_can_delete?(@user) %> + <%= link_to "Delete", task_path(@task), :confirm => "Are you sure?", :method => :delete %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 874f8e9..2f66a65 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,10 +16,15 @@ #
ActionController::Routing::Routes.draw do |map| + map.resources :tasks + # You can have the root of your site routed with map.root -- just remember to # delete public/index.html. map.root :controller => 'project'
+ # Login URL + map.login "/login", :controller => 'home', :action => 'login' + # Error handling map.error "/error", :controller => 'home', :action => 'error'
diff --git a/test/functional/task_controller_test.rb b/test/functional/tasks_controller_test.rb similarity index 56% rename from test/functional/task_controller_test.rb rename to test/functional/tasks_controller_test.rb index 92551da..270b308 100644 --- a/test/functional/task_controller_test.rb +++ b/test/functional/tasks_controller_test.rb @@ -1,224 +1,267 @@ -# task_controller_test.rb +# tasks_controller_test.rb # Copyright (C) 2008, Darryl L. Pierce mcpierce@gmail.com -# +# # 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 3 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, see http://www.gnu.org/licenses/. -# +#
require File.dirname(__FILE__) + '/../test_helper'
-class TaskControllerTest < ActionController::TestCase +class TasksControllerTest < ActionController::TestCase fixtures :backlog_items fixtures :users - + def setup @backlog_item = backlog_items(:owned_backlog_item) @task = @backlog_item.tasks.first @owner = @backlog_item.owner - @non_owner = users(:jdonuts) + @nonowner = users(:jdonuts) + raise "Owner and nonowner cannot be the same person!" if @owner.id == @nonowner.id + @inactive_sprint_item = backlog_items(:owned_inactive_sprint_backlog_item) - - flunk "FIXTURES ARE BROKEN" if @owner.id == @non_owner.id - + @details = { :primary_id => @backlog_item.owner.id, :description => 'This is a new task.', :hours => '2.5', :when => Date.today - } + } end - - # Ensures that an invalid user story causes create to fail. - # - def test_create_with_invalid_user_story - get :create, - nil, - {:user_id => users(:mcpierce).id} - - assert_redirected_to error_path - - get :create, - {:user_story => 9999}, - {:user_id => users(:mcpierce).id} - - assert_redirected_to error_path + + # Ensures that viewing tasks when no backlog item is specified fails. + # + def test_index_without_backlog_item + get :index + + assert_redirected_to error_url end - - # Ensures that an anonymous user cannot create a task. - # - def test_create_for_anonymous - get :create, - {:backlog_item => @backlog_item.id} - - assert_redirected_to :controller => :home, :action => :login + + # Ensures that getting the list of tasks for a backlog item works as expected. + # + def test_index + get :index, {:backlog_item => @backlog_item.id} + + assert_response :success + assert assigns['backlog_item'], "Failed to load the backlog item." + assert_equal @backlog_item.id, assigns['backlog_item'].id, + "Did not load the right backlog item." + assert assigns['tasks'], "Failed to load the tasks." end - - # Ensures that a user who doesn't have authorization aren't able to create - # tasks. - # - def test_create_for_unauthorized_user - get :create, {:backlog_item => @backlog_item.id}, {:user_id => users(:jdonuts).id} - - assert_redirected_to :controller => :backlog, :action => :view + + # Ensures that an error is show if no task id is present. + # + def test_show_without_task_id + get :show + + assert_redirected_to error_url end - - # Ensures that tasks cannot be created for a backlog item on an non-active - # sprint. + + # Ensures that creating a task requires the user to be logged in. # - def test_create_for_nonactive_sprint - count = @inactive_sprint_item.tasks.size - - get :create, - {:backlog_item => @inactive_sprint_item.id}, - {:user_id => @inactive_sprint_item.owner.id} - - assert_redirected_to :controller => :backlog, :action => :view, :id => @inactive_sprint_item.id - result = BacklogItem.find_by_id(@inactive_sprint_item.id) - assert_equal count, result.tasks.size, 'Tasks should not have been updated' + def test_new_for_anonymous + get :new + + assert_redirected_to login_url + end + + # Ensures that creating a task without a backlog item fails. + # + def test_new_without_backlog_item + get :new, nil, {:user_id => @owner.id} + + assert_redirected_to error_url end - + + # Ensures that a non-owner cannot create tasks for a backlog item. + # + def test_new_for_nonowner + get :new, + {:backlog_item => @backlog_item.id}, + {:user_id => @nonowner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item.id + end + # Ensures that creating a task works as expected. - # - def test_create - get :create, - { - :backlog_item => @backlog_item.id, - :task => @details - }, - {:user_id => users(:mcpierce).id} - + # + def test_new + get :new, + {:backlog_item => @backlog_item.id}, + {:user_id => @owner.id} + assert_response :success - assert assigns['task'], 'Did not create a new task.' - assert assigns['users'], 'Did not load users.' + assert assigns['task'], "Didn't create a task." + assert assigns['users'], "Didn't load the list of users." + assert_equal @backlog_item.id, assigns['task'].backlog_item_id, + "Didn't set the backlog item correctly." + assert_equal @owner.id, assigns['task'].primary_id, + "Didn't set the primary developer correctly." end - - # Ensures that tasks are saved corrected. - # - def test_create_as_save - post :create, - { - :backlog_item => @backlog_item.id, - :task => @details - }, - {:user_id => users(:mcpierce).id} - - assert_redirected_to :controller => :backlog, :action => :view - end - + # Ensures that attempting to modify an invalid task fails. - # - def test_modify_with_invalid_task - get :modify, nil, {:user_id => users(:mcpierce).id} - + # + def test_edit_with_invalid_task + get :edit, nil, {:user_id => users(:mcpierce).id} + assert_redirected_to error_path - - get :modify, {:id => 9999} - + + get :edit, {:id => 9999} + assert_redirected_to error_path end - + # Ensures that anonymous users cannot modify tasks. - # - def test_modify_for_anonymous - get :modify, {:id => tasks(:created_login_view).id} - + # + def test_edit_for_anonymous + get :edit, {:id => tasks(:created_login_view).id} + assert_redirected_to :controller => :home, :action => :login end - + # Ensures that a user who's not the task owner or backup cannot modify the # task. - # - def test_modify_for_non_developer - get :modify, + # + def test_edit_for_non_developer + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => users(:project_admin).id} - + assert_redirected_to :controller => :backlog, :action => :view end - + # Ensures that the owner of the user story is allowed to edit a task against # that user story. - # - def test_modify_for_user_story_owner - get :modify, + # + def test_edit_for_user_story_owner + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => users(:mcpierce).id} - + assert_response :success end - + # Ensures that modifying works as expected. - # - def test_modify - get :modify, + # + def test_edit + get :edit, {:id => tasks(:created_login_view).id}, {:user_id => tasks(:created_login_view).primary_id} - - assert_response :success + + assert_response :success assert assigns['task'], 'Did not load the task.' assert_equal tasks(:created_login_view).id, assigns['task'].id, 'Did not load the right task.' assert assigns['users'], 'Did not load the series.' end - - # Ensures that saving a task after modifying it works as expected. - # - def test_modify_as_save - task = tasks(:created_login_view).attributes - - post :modify, - {:id => tasks(:created_login_view), :task => task}, - {:user_id => tasks(:created_login_view).primary_id} - - assert_redirected_to :controller => :backlog, :action => :view + + # Ensures that an invalid user story causes create to fail. + # + def test_create_with_invalid_user_story + get :create, + nil, + {:user_id => users(:mcpierce).id} + + assert_redirected_to error_path + + get :create, + {:user_story => 9999}, + {:user_id => users(:mcpierce).id} + + assert_redirected_to error_path + end + + # Ensures that an anonymous user cannot create a task. + # + def test_create_for_anonymous + get :create, + {:backlog_item => @backlog_item.id} + + assert_redirected_to :controller => :home, :action => :login + end + + # Ensures that a user who doesn't have authorization aren't able to create + # tasks. + # + def test_create_for_unauthorized_user + get :create, {:backlog_item => @backlog_item.id}, {:user_id => users(:jdonuts).id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + end + + # Ensures that tasks cannot be created for a backlog item on an non-active + # sprint. + # + def test_create_for_nonactive_sprint + count = @inactive_sprint_item.tasks.size + + get :create, + {:backlog_item => @inactive_sprint_item.id}, + {:user_id => @inactive_sprint_item.owner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @inactive_sprint_item.id + result = BacklogItem.find_by_id(@inactive_sprint_item.id) + assert_equal count, result.tasks.size, 'Tasks should not have been updated' end - + + # Ensures that tasks are saved corrected. + # + def test_create + post :create, + { + :backlog_item => @backlog_item.id, + :task => @details + }, + {:user_id => users(:mcpierce).id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + end + # Ensures that anonymous users can't delete tasks - # - def test_delete_as_anonymous - get :delete - + # + def test_destroy_as_anonymous + get :destroy + assert_redirected_to :controller => :home, :action => :login end - + # Ensures that deletion requires a valid task id. - # - def test_delete_with_invalid_id - post :delete, + # + def test_destroy_with_invalid_id + post :destroy, {:id => 9999}, {:user_id => @owner.id} - + assert_redirected_to error_path end - + # Ensures that someone who is not the backlog item owner can't add tasks to # it. - # - def test_delete_as_non_owner - post :delete, + # + def test_destroy_as_non_owner + post :destroy, { :id => @task.id }, { - :user_id => @non_owner.id + :user_id => @nonowner.id } - + assert Task.find_by_id(@task.id), 'Task should not have been deleted.' end - + # Ensures that the primary can delete a task. - # - def test_delete - post :delete, + # + def test_destroy + post :destroy, { :id => @task.id }, @@ -226,8 +269,45 @@ class TaskControllerTest < ActionController::TestCase
assert_redirected_to( :controller => :backlog, - :action => :view, + :action => :view, :id => @task.backlog_item.id) assert_equal nil, Task.find_by_id(@task.id), 'Did not delete the task.' end + + # Ensures that an update requires the user be loggeg in. + # + def test_update_as_anonymous + put :update + + assert_redirected_to login_url + end + + # Ensures that an update fails without a task id. + # + def test_update_without_id + put :update, {}, {:user_id => @owner.id} + + assert_redirected_to error_url + end + + # Ensures that an update fails with an invalid id. + # + def test_update_with_invalid_id + put :update, + {:id => 9999}, + {:user_id => @owner.id} + end + + # Ensures that a non-owner cannot update a task. + # + def test_update_as_nonowner + put :update, + {:id => @task.id, :task => @task.attributes}, + {:user_id => @nonowner.id} + + assert_redirected_to :controller => :backlog, :action => :view, :id => @backlog_item + result = Task.find_by_id(@task.id) + assert_equal @task.updated_at, result.updated_at, + "Task should not have been updated." + end end diff --git a/test/unit/product_test.rb b/test/unit/product_test.rb index a0df74d..62647db 100644 --- a/test/unit/product_test.rb +++ b/test/unit/product_test.rb @@ -25,7 +25,8 @@ class ProductTest < ActiveSupport::TestCase def setup @new_product = Product.new( :project_id => projects(:cairo).id, - :owner_id => users(:mcpierce).id) + :owner_id => users(:mcpierce).id, + :name => "Test Product")
@product = products(:cairo_web) @owner = @product.owner -- 1.5.6.5
_______________________________________________ projxp-devel mailing list projxp-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/projxp-devel
projxp-devel@lists.fedorahosted.org