# Copyright (c) 2008-2013 Michael Dvorkin and contributors. # # Fat Free CRM is freely distributable under the terms of MIT license. # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php #------------------------------------------------------------------------------ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe TasksController do def update_sidebar @task_total = { :key => :value, :pairs => :etc } Task.stub!(:totals).and_return(@task_total) end def produce_tasks(user, view) settings = (view != "completed" ? Setting.task_bucket : Setting.task_completed) settings.inject({}) do | hash, due | hash[due] ||= [] if Date.tomorrow == Date.today.end_of_week && due == :due_tomorrow due = :due_this_week hash[due] ||= [] end hash[due] << case view when "pending" FactoryGirl.create(:task, :user => user, :bucket => due.to_s) when "assigned" FactoryGirl.create(:task, :user => user, :bucket => due.to_s, :assigned_to => 1) when "completed" completed_at = case due when :completed_today Date.yesterday + 1.day when :completed_yesterday Date.yesterday when :completed_last_week Date.today.beginning_of_week - 7.days when :completed_this_month Date.today.beginning_of_month when :completed_last_month Date.today.beginning_of_month - 1.day end FactoryGirl.create(:task, :user => user, :bucket => due.to_s, :completed_at => completed_at) end hash end end before(:each) do require_user set_current_tab(:tasks) end # GET /tasks # GET /tasks.xml #---------------------------------------------------------------------------- describe "responding to GET index" do before do update_sidebar @timezone, Time.zone = Time.zone, 'UTC' end after do Time.zone = @timezone end TASK_STATUSES.each do |view| it "should expose all tasks as @tasks and render [index] template for #{view} view" do @tasks = produce_tasks(current_user, view) get :index, :view => view (assigns[:tasks].keys.map(&:to_sym) - @tasks.keys).should == [] (assigns[:tasks].values.flatten - @tasks.values.flatten).should == [] assigns[:task_total].symbolize_keys.should == @task_total response.should render_template("tasks/index") end it "should render all tasks as JSON for #{view} view" do @tasks = produce_tasks(current_user, view) get :index, :view => view, :format => :json (assigns[:tasks].keys.map(&:to_sym) - @tasks.keys).should == [] (assigns[:tasks].values.flatten - @tasks.values.flatten).should == [] hash = ActiveSupport::JSON.decode(response.body) hash.keys.each do |key| hash[key].each do |attr| task = Task.new(attr["task"]) task.should be_instance_of(Task) task.valid?.should == true end end end it "should render all tasks as xml for #{view} view" do @tasks = produce_tasks(current_user, view) get :index, :view => view, :format => :xml (assigns[:tasks].keys.map(&:to_sym) - @tasks.keys).should == [] (assigns[:tasks].values.flatten - @tasks.values.flatten).should == [] hash = Hash.from_xml(response.body) hash["hash"].keys.each do |key| hash["hash"][key].each do |attr| task = Task.new(attr) task.should be_instance_of(Task) task.valid?.should == true end end end end end # GET /tasks/1 # GET /tasks/1.xml #---------------------------------------------------------------------------- describe "responding to GET show" do TASK_STATUSES.each do |view| it "should render the requested task as JSON for #{view} view" do Task.stub_chain(:tracked_by, :find).and_return(task = mock("Task")) task.should_receive(:to_json).and_return("generated JSON") request.env["HTTP_ACCEPT"] = "application/json" get :show, :id => 42, :view => "pending" response.body.should == "generated JSON" end it "should render the requested task as xml for #{view} view" do Task.stub_chain(:tracked_by, :find).and_return(task = mock("Task")) task.should_receive(:to_xml).and_return("generated XML") request.env["HTTP_ACCEPT"] = "application/xml" get :show, :id => 42, :view => "pending" response.body.should == "generated XML" end end end # GET /tasks/new # GET /tasks/new.xml AJAX #---------------------------------------------------------------------------- describe "responding to GET new" do it "should expose a new task as @task and render [new] template" do account = FactoryGirl.create(:account, :user => current_user) @task = FactoryGirl.build(:task, :user => current_user, :asset => account) Task.stub!(:new).and_return(@task) @bucket = Setting.unroll(:task_bucket)[1..-1] << [ "On Specific Date...", :specific_time ] @category = Setting.unroll(:task_category) xhr :get, :new assigns[:task].should == @task assigns[:bucket].should == @bucket assigns[:category].should == @category response.should render_template("tasks/new") end it "should find related asset when necessary" do @asset = FactoryGirl.create(:account, :id => 42) xhr :get, :new, :related => "account_42" assigns[:asset].should == @asset response.should render_template("tasks/new") end describe "(when creating related task)" do it "should redirect to parent asset's index page with the message if parent asset got deleted" do @account = FactoryGirl.create(:account) @account.destroy xhr :get, :new, :related => "account_#{@account.id}" flash[:warning].should_not == nil response.body.should == 'window.location.href = "/accounts";' end it "should redirect to parent asset's index page with the message if parent asset got protected" do @account = FactoryGirl.create(:account, :access => "Private") xhr :get, :new, :related => "account_#{@account.id}" flash[:warning].should_not == nil response.body.should == 'window.location.href = "/accounts";' end end end # GET /tasks/1/edit AJAX #---------------------------------------------------------------------------- describe "responding to GET edit" do it "should expose the requested task as @task and render [edit] template" do @asset = FactoryGirl.create(:account, :user => current_user) @task = FactoryGirl.create(:task, :user => current_user, :asset => @asset) @bucket = Setting.unroll(:task_bucket)[1..-1] << [ "On Specific Date...", :specific_time ] @category = Setting.unroll(:task_category) xhr :get, :edit, :id => @task.id assigns[:task].should == @task assigns[:bucket].should == @bucket assigns[:category].should == @category assigns[:asset].should == @asset response.should render_template("tasks/edit") end it "should find previously open task when necessary" do @task = FactoryGirl.create(:task, :user => current_user) @previous = FactoryGirl.create(:task, :id => 999, :user => current_user) xhr :get, :edit, :id => @task.id, :previous => 999 assigns[:task].should == @task assigns[:previous].should == @previous response.should render_template("tasks/edit") end describe "(task got deleted or reassigned)" do it "should reload current page with the flash message if the task got deleted" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => current_user) @task.destroy xhr :get, :edit, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end it "should reload current page with the flash message if the task got reassigned" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user)) xhr :get, :edit, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end end describe "(previous task got deleted or reassigned)" do before(:each) do @task = FactoryGirl.create(:task, :user => current_user) @previous = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => current_user) end it "should notify the view if previous task got deleted" do @previous.destroy xhr :get, :edit, :id => @task.id, :previous => @previous.id flash[:warning].should == nil # no warning, just silently remove the div assigns[:previous].should == @previous.id response.should render_template("tasks/edit") end it "should notify the view if previous task got reassigned" do @previous.update_attribute(:assignee, FactoryGirl.create(:user)) xhr :get, :edit, :id => @task.id, :previous => @previous.id flash[:warning].should == nil assigns[:previous].should == @previous.id response.should render_template("tasks/edit") end end end # POST /tasks # POST /tasks.xml AJAX #---------------------------------------------------------------------------- describe "responding to POST create" do describe "with valid params" do it "should expose a newly created task as @task and render [create] template" do @task = FactoryGirl.build(:task, :user => current_user) Task.stub!(:new).and_return(@task) xhr :post, :create, :task => { :name => "Hello world" } assigns(:task).should == @task assigns(:view).should == "pending" assigns[:task_total].should == nil response.should render_template("tasks/create") end [ "", "?view=pending", "?view=assigned", "?view=completed" ].each do |view| it "should update tasks sidebar when [create] is being called from [/tasks#{view}] page" do @task = FactoryGirl.build(:task, :user => current_user) Task.stub!(:new).and_return(@task) request.env["HTTP_REFERER"] = "http://localhost/tasks#{view}" xhr :post, :create, :task => { :name => "Hello world" } assigns[:task_total].should be_an_instance_of(HashWithIndifferentAccess) end end end describe "with invalid params" do it "should expose a newly created but unsaved task as @lead and still render [create] template" do @task = FactoryGirl.build(:task, :name => nil, :user => current_user) Task.stub!(:new).and_return(@task) xhr :post, :create, :task => {} assigns(:task).should == @task assigns(:view).should == "pending" assigns[:task_total].should == nil response.should render_template("tasks/create") end end end # PUT /tasks/1 # PUT /tasks/1.xml AJAX #---------------------------------------------------------------------------- describe "responding to PUT update" do describe "with valid params" do it "should update the requested task, expose it as @task, and render [update] template" do @task = FactoryGirl.create(:task, :name => "Hi", :user => current_user) xhr :put, :update, :id => @task.id, :task => { :name => "Hello" } @task.reload.name.should == "Hello" assigns(:task).should == @task assigns(:view).should == "pending" assigns[:task_total].should == nil response.should render_template("tasks/update") end [ "", "?view=pending", "?view=assigned", "?view=completed" ].each do |view| it "should update tasks sidebar when [update] is being called from [/tasks#{view}] page" do @task = FactoryGirl.create(:task, :name => "Hi", :user => current_user) request.env["HTTP_REFERER"] = "http://localhost/tasks#{view}" xhr :put, :update, :id => @task.id, :task => { :name => "Hello" } assigns[:task_total].should be_an_instance_of(HashWithIndifferentAccess) end end end describe "with invalid params" do it "should not update the task, but still expose it as @task and render [update] template" do @task = FactoryGirl.create(:task, :name => "Hi", :user => current_user) xhr :put, :update, :id => @task.id, :task => { :name => nil } @task.reload.name.should == "Hi" assigns(:task).should == @task assigns(:view).should == "pending" assigns[:task_total].should == nil response.should render_template("tasks/update") end end describe "task got deleted or reassigned" do it "should reload current page with the flash message if the task got deleted" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => current_user) @task.destroy xhr :put, :update, :id => @task.id, :task => { :name => "Hello" } flash[:warning].should_not == nil response.body.should == "window.location.reload();" end it "should reload current page with the flash message if the task got reassigned" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user)) xhr :put, :update, :id => @task.id, :task => { :name => "Hello" } flash[:warning].should_not == nil response.body.should == "window.location.reload();" end end end # DELETE /tasks/1 # DELETE /tasks/1.xml AJAX #---------------------------------------------------------------------------- describe "responding to DELETE destroy" do it "should destroy the requested task and render [destroy] template" do @task = FactoryGirl.create(:task, :user => current_user) xhr :delete, :destroy, :id => @task.id, :bucket => "due_asap" assigns(:task).should == @task assigns(:view).should == "pending" assigns[:task_total].should == nil response.should render_template("tasks/destroy") end [ "", "?view=pending", "?view=assigned", "?view=completed" ].each do |view| it "should update sidebar when [destroy] is being called from [/tasks#{view}]" do @task = FactoryGirl.create(:task, :user => current_user) request.env["HTTP_REFERER"] = "http://localhost/tasks#{view}" xhr :delete, :destroy, :id => @task.id, :bucket => "due_asap" assigns[:task_total].should be_an_instance_of(HashWithIndifferentAccess) end end it "should not update sidebar when [destroy] is being called from asset page" do @task = FactoryGirl.create(:task, :user => current_user) xhr :delete, :destroy, :id => @task.id assigns[:task_total].should == nil end describe "task got deleted or reassigned" do it "should reload current page with the flash message if the task got deleted" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => current_user) @task.destroy xhr :delete, :destroy, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end it "should reload current page with the flash message if the task got reassigned" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user)) xhr :delete, :destroy, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end end end # PUT /tasks/1/complete # PUT /leads/1/complete.xml AJAX #---------------------------------------------------------------------------- describe "responding to PUT complete" do it "should change task status, expose task as @task, and render [complete] template" do @task = FactoryGirl.create(:task, :completed_at => nil, :user => current_user) xhr :put, :complete, :id => @task.id @task.reload.completed_at.should_not == nil assigns[:task].should == @task assigns[:task_total].should == nil response.should render_template("tasks/complete") end it "should change task status, expose task as @task, and render [complete] template where task.bucket = 'specific_time'" do @task = FactoryGirl.create(:task, :completed_at => nil, :user => current_user, :bucket => "specific_time", :calendar => "01/01/2010 1:00 AM") xhr :put, :complete, :id => @task.id @task.reload.completed_at.should_not == nil assigns[:task].should == @task assigns[:task_total].should == nil response.should render_template("tasks/complete") end it "should change update tasks sidebar if bucket is not empty" do @task = FactoryGirl.create(:task, :completed_at => nil, :user => current_user) xhr :put, :complete, :id => @task.id, :bucket => "due_asap" assigns[:task_total].should be_an_instance_of(HashWithIndifferentAccess) end describe "task got deleted or reassigned" do it "should reload current page with the flash message if the task got deleted" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => current_user) @task.destroy xhr :put, :complete, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end it "should reload current page with the flash message if the task got reassigned" do @task = FactoryGirl.create(:task, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user)) xhr :put, :complete, :id => @task.id flash[:warning].should_not == nil response.body.should == "window.location.reload();" end end end # Ajax request to filter out a list of tasks. AJAX #---------------------------------------------------------------------------- describe "responding to GET filter" do TASK_STATUSES.each do |view| it "should remove a filter from session and render [filter] template for #{view} view" do name = "filter_by_task_#{view}" session[name] = "due_asap,due_today,due_tomorrow" xhr :get, :filter, :filter => "due_asap", :view => view session[name].should_not include("due_asap") session[name].should include("due_today") session[name].should include("due_tomorrow") response.should render_template("tasks/filter") end it "should add a filter from session and render [filter] template for #{view} view" do name = "filter_by_task_#{view}" session[name] = "due_today,due_tomorrow" xhr :get, :filter, :checked => "true", :filter => "due_asap", :view => view session[name].should include("due_asap") session[name].should include("due_today") session[name].should include("due_tomorrow") response.should render_template("tasks/filter") end end end end