require 'spec_helper' describe OpenConferenceWare::Proposal do fixtures :all describe "when checking authorization for altering" do before(:each) do @proposal = proposals(:quentin_widgets) end it "should not allow anonymous" do @proposal.can_alter?(nil).should be_false end it "should not allow wrong mortal" do @proposal.can_alter?(users(:clio)).should be_false end it "should allow mortal owner" do @proposal.can_alter?(users(:quentin)).should be_true end it "should allow admin" do @proposal.can_alter?(users(:aaron)).should be_true end end describe "when setting submitted_at date" do it "should set value on save" do proposal = proposals(:quentin_widgets) proposal.submitted_at = nil proposal.save! proposal.reload proposal.submitted_at.should_not be_nil end it "should set value to created_at date" do proposal = proposals(:quentin_widgets) proposal.submitted_at = nil proposal.save! proposal.reload proposal.submitted_at.should == proposal.created_at end end describe "when setting status" do before(:each) do @proposal = new_proposal @proposal.save! end it "should default to a state of 'proposed'" do @proposal.should be_proposed end it "should be possible to accept a proposed proposal" do @proposal.accept! @proposal.should be_accepted end it "should be possible to confirm a proposed proposal" do @proposal.accept_and_confirm! @proposal.should be_confirmed end it "should be possible to decline a proposed proposal" do @proposal.accept_and_decline! @proposal.should be_declined end it "should be possible to reject a proposed proposal" do @proposal.reject! @proposal.should be_rejected end it "should be possible to confirm an accepted proposal" do @proposal.status = 'accepted' @proposal.confirm! @proposal.should be_confirmed end it "should be possible to decline an accepted proposal" do @proposal.status = 'accepted' @proposal.decline! @proposal.should be_declined end it "should be possible to accept a rejected proposal" do @proposal.status = 'rejected' @proposal.accept! @proposal.should be_accepted end it "should be possible to reject an accepted proposal" do @proposal.status = 'accepted' @proposal.reject! @proposal.should be_rejected end it "should be possible to mark a proposed proposal as junk" do @proposal.mark_as_junk! @proposal.should be_junk end it "should be possible to cancel a confirmed proposal" do @proposal.status = 'confirmed' @proposal.cancel! @proposal.should be_cancelled end it "should be possible to waitlist a proposed proposal" do @proposal.waitlist! @proposal.should be_waitlisted end it "should be possible to waitlist an accepted proposal" do @proposal.status = 'accepted' @proposal.waitlist! @proposal.should be_waitlisted end it "should be possible to waitlist a rejected proposal" do @proposal.status = 'rejected' @proposal.waitlist! @proposal.should be_waitlisted end it "should be possible to accept a waitlisted proposal" do @proposal.status = 'waitlisted' @proposal.accept! @proposal.should be_accepted end it "should be possible to confirm a waitlisted proposal" do @proposal.status = 'waitlisted' @proposal.accept_and_confirm! @proposal.should be_confirmed end it "should be possible to decline a waitlisted proposal" do @proposal.status = 'waitlisted' @proposal.accept_and_decline! @proposal.should be_declined end it "should be possible to reject a waitlisted proposal" do @proposal.status = 'waitlisted' @proposal.reject! @proposal.should be_rejected end %w(accepted confirmed waitlisted rejected declined junk cancelled).each do |initial_status| it "should be posible to reset a #{initial_status} proposal back to proposed" do @proposal.status = initial_status @proposal.reset_status! @proposal.should be_proposed end end describe "through the transition accessor" do it "should be possible to call a valid event" do @proposal.should be_proposed @proposal.transition = 'accept' @proposal.should be_accepted end it "should not call invalid event methods" do @proposal.should_not_receive(:destroy!) @proposal.transition = 'destroy' end end end describe "when getting comments" do before(:each) do @proposal = proposals(:quentin_widgets) end it "should return nothing if no comments" do @proposal.comments_text.blank?.should be_true end it "should return one comment" do comments = [ double(Comment, email: "bubba@smith.com", message: "Hi"), ] @proposal.should_receive(:comments).and_return(comments) @proposal.comments_text.should == "bubba@smith.com: Hi" end it "should return multiple comments" do comments = [ double(Comment, email: "bubba@smith.com", message: "Hi"), double(Comment, email: "billy.sue@smith.com", message: "Yo"), ] @proposal.should_receive(:comments).and_return(comments) @proposal.comments_text.should == "bubba@smith.com: Hi\nbilly.sue@smith.com: Yo" end end describe "when getting profile" do before(:each) do @proposal = proposals(:quentin_widgets) end it "should return false if multiple_presenters is enabled" do OpenConferenceWare.stub(:have_multiple_presenters).and_return(true) @proposal.profile.should be_false end it "should return the user if user_profiles is enabled" do OpenConferenceWare.stub(:have_multiple_presenters).and_return(false) OpenConferenceWare.stub(:have_user_profiles).and_return(true) @proposal.profile.should == @proposal.user end it "should return itself if multiple_presenters and user_profiles are disabled" do OpenConferenceWare.stub(:have_multiple_presenters).and_return(false) OpenConferenceWare.stub(:have_user_profiles).and_return(false) @proposal.profile.should == @proposal end end describe "when adding or removing user" do before(:each) do @user = stub_model(User) @users = [] @proposal = stub_model(Proposal) @proposal.should_receive(:users).at_least(:once).and_return(@users) end describe "when adding user" do it "should add a user" do @users.should_receive(:include?).with(@user).and_return(false) @users.should_receive(:<<).with(@user).and_return(@user) @proposal.add_user(@user) end it "should not re-add an existing user" do @users.should_receive(:include?).with(@user).and_return(true) @users.should_not_receive(:<<) @proposal.add_user(@user) end end describe "when removing user" do it "should remove a user" do @users.should_receive(:include?).with(@user).and_return(true) @users.should_receive(:delete).with(@user).and_return(@user) @proposal.remove_user(@user) end it "should not remove a non-existent user" do @users.should_receive(:include?).with(@user).and_return(false) @users.should_not_receive(:delete) @proposal.remove_user(@user) end end end describe "with rooms" do before :each do @proposal = Proposal.new end it "should be able to assign a room to an proposal" do room = stub_model(Room) @proposal.room = room @proposal.room.should==room end end describe "to_icalendar" do def assert_calendar_match(item, component, url_helper=nil) component.should_not be_nil # This is hacky, but necessary because vPim doesn't actually parse # time zone information out of iCalendar files. # # The files are output with the correct time zone, but seem to be # parsed in the computer's local time, regardless of the Rails # time zone setting. dtstart = Time.parse(component.dtstart.strftime('%Y-%m-%d %H:%M:%S UTC')) dtstart.to_i.should == item.start_time.to_i if item.duration dtend = Time.parse(component.dtend.strftime('%Y-%m-%d %H:%M:%S UTC')) dtend.to_i.should == item.end_time.to_i else component.dtend.should be_nil end component.summary.should == item.title component.description.should == (item.respond_to?(:users) ? "#{item.users.map(&:fullname).join(', ')}: #{item.excerpt}" : item.excerpt) component.url.should == url_helper.call(item) if url_helper end it "should export proposals to iCalendar" do mysql_record = proposals(:mysql_session) postgresql_record = proposals(:postgresql_session) items = [ mysql_record, postgresql_record ] title = "MyTitle" url_helper = lambda {|item| "http://foo.bar/#{item.id}"} data = Proposal.to_icalendar(items, title: title, url_helper: url_helper) calendar = Vpim::Icalendar.decode(data).first components = Array(calendar) components.size.should == 2 assert_calendar_match(mysql_record, components.find{|t| t.summary == mysql_record.title}, url_helper) assert_calendar_match(postgresql_record, components.find{|t| t.summary == postgresql_record.title}, url_helper) end it "should skip items without a start time" do event = create :populated_event item = create :schedule_item, start_time: nil, duration: nil data = Proposal.to_icalendar([item]) calendar = Vpim::Icalendar.decode(data).first components = Array(calendar) components.size.should == 0 end it "should not raise exceptions if nil duration" do event = create :populated_event item = create :schedule_item, duration: nil data = Proposal.to_icalendar([item]) calendar = Vpim::Icalendar.decode(data).first components = Array(calendar) components.size.should == 1 component = components.first assert_calendar_match(item, component) end end describe "session_notes_url" do before do @proposal = proposals(:clio_chupacabras) end it "should be nil if no session_notes_url_format is defined" do OpenConferenceWare.stub(session_notes_url_format: nil) @proposal.session_notes_url.should be_nil end describe "with wiki" do before :each do OpenConferenceWare.stub( session_notes_url_format: '%1$s%2$s/wiki/', public_url: 'http://mysite.com/' ) end it "should interpolate content if session_notes_url_format is defined" do @proposal.session_notes_url.should == "http://mysite.com/closed/wiki/Chupacabras_and_you" end it "should replace slashes with dashes" do @proposal.title = "CouchApp Evently Guided Hack w/ CouchDB" @proposal.session_notes_url.should == "http://mysite.com/closed/wiki/CouchApp_Evently_Guided_Hack_w-_CouchDB" end end end describe "when sorting" do before(:each) do @shonen = stub_model(Track, title: "Shonen") @bishoujo = stub_model(Track, title: "Bishoujo") @tracks = [@shonen, @bishoujo] @naruto = stub_model(Proposal, title: "Naruto", track: @shonen, status: "proposed", submitted_at: Time.parse('2009/12/09 01:00'), start_time: Time.parse('2009/12/10 05:00')) @bleach = stub_model(Proposal, title: "Bleach", track: @shonen, status: "confirmed", submitted_at: Time.parse('2009/12/09 02:00'), start_time: Time.parse('2009/12/10 04:00')) @sera_mun = stub_model(Proposal, title: "Bishoujo Senshi Sera Mun", track: @bishoujo, status: "confirmed", submitted_at: Time.parse('2009/12/09 04:00'), start_time: Time.parse('2009/12/10 01:00')) @kadocapta_sakura = stub_model(Proposal, title: "Kadocapta Sakura", track: @bishoujo, status: "accepted", submitted_at: Time.parse('2009/12/09 03:00'), start_time: Time.parse('2009/12/10 02:00')) @kino = stub_model(Proposal, title: "Kino no Tabi", track: nil, status: "accepted", submitted_at: Time.parse('2009/12/09 06:00'), start_time: Time.parse('2009/12/10 06:00')) @proposals = [@kadocapta_sakura, @bleach, @kino, @naruto, @sera_mun] end it "should sort by title" do Proposal.sort(@proposals, :title).should == [@sera_mun, @bleach, @kadocapta_sakura, @kino, @naruto] end it "should sort by title descending" do Proposal.sort(@proposals, :title, false).should == [@naruto, @kino, @kadocapta_sakura, @bleach, @sera_mun] end it "should sort by track" do Proposal.sort(@proposals, :track).should == [@sera_mun, @kadocapta_sakura, @bleach, @naruto, @kino] end it "should sort by status" do Proposal.sort(@proposals, :status).should == [@kadocapta_sakura, @kino, @sera_mun, @bleach, @naruto] end it "should sort by submitted date" do Proposal.sort(@proposals, :submitted_at).should == [@naruto, @bleach, @kadocapta_sakura, @sera_mun, @kino] end it "should default to sorting by submitted date if given unknown sorting" do Proposal.sort(@proposals, :submitted_at).should == [@naruto, @bleach, @kadocapta_sakura, @sera_mun, @kino] end end describe "when retreiving slug" do it "should return a slug composed of the organization and event slugs plus the proposal identifier" do proposal = proposals(:clio_chupacabras) proposal.slug.should == "#{OpenConferenceWare.organization_slug}#{proposal.event.slug}-%04d" % proposal.id end end describe "is a proposal related to an event?" do it "should be related to its own event" do event = create :populated_event proposal = proposal_for_event(event) proposal.related_to_event?(proposal.event).should be_true end it "should be related to its own event's parent" do parent = create :populated_event event = create :populated_event, parent: parent proposal = proposal_for_event(event) proposal.related_to_event?(event).should be_true end it "should be related to its own event's parent's children" do event = create :populated_event child = create :populated_event, parent: event proposal = proposal_for_event(event) proposal.related_to_event?(event).should be_true end it "should not be related to an unrelated event" do event = create :populated_event proposal = proposal_for_event(event) unrelated = create :populated_event proposal.related_to_event?(unrelated).should be_false end end describe "traversal" do before(:each) do @event = create :populated_event @event.proposals.destroy_all @proposal1 = proposal_for_event(@event) @proposal2 = proposal_for_event(@event) end describe "when finding next proposal" do it "should return the next proposal when it exists" do @proposal1.next_proposal.should == @proposal2 end it "should return nil when it's the last" do @proposal2.next_proposal.should be_nil end end describe "when finding previous proposal" do it "should return the previous proposal when it exists" do @proposal2.previous_proposal.should == @proposal1 end it "should return nil when it's the first" do @proposal1.previous_proposal.should be_nil end end end describe "when notifying accepted speakers" do before(:each) do @proposal = proposals(:quentin_widgets) end it "should not notify unaccepted proposal speakers" do emailed, already = @proposal.notify_accepted_speakers emailed.should be_nil already.should be_nil @proposal.notified_at.should be_nil end it "should not notify already-notified proposal speakers" do @proposal.accept! @proposal.notified_at = Time.now emailed, already = @proposal.notify_accepted_speakers emailed.should be_nil already.should == @proposal.mailto_emails end it "should notify accepted proposal speakers" do @proposal.accept! emailed, already = @proposal.notify_accepted_speakers emailed.should == @proposal.mailto_emails already.should be_nil @proposal.notified_at.should_not be_nil end end describe "when notifying rejected speakers" do before(:each) do @proposal = proposals(:quentin_widgets) end it "should not notify unrejected proposal speakers" do emailed, already = @proposal.notify_rejected_speakers emailed.should be_nil already.should be_nil @proposal.notified_at.should be_nil end it "should not notify already-notified proposal speakers" do @proposal.reject! @proposal.notified_at = Time.now emailed, already = @proposal.notify_rejected_speakers emailed.should be_nil already.should == @proposal.mailto_emails end it "should notify rejected proposal speakers" do @proposal.reject! emailed, already = @proposal.notify_rejected_speakers emailed.should == @proposal.mailto_emails already.should be_nil @proposal.notified_at.should_not be_nil end end describe "validating acceptance of the policy agreement," do before(:each) do @proposal = proposals(:quentin_widgets) end describe "when one exists," do it "should require presenters to accept the agreement" do @proposal.agreement = false @proposal.should_not be_valid @proposal.agreement = true @proposal.should be_valid end end describe "when no agreement is set" do before do OpenConferenceWare.stub(agreement: nil) end it "should not require presenters to accept the agreement" do @proposal.agreement = false @proposal.should be_valid end end end private def new_proposal(attr = {}) # TODO: The proposal factory should handle these associations valid_attr = { event: mock_model(Event), track: mock_model(Track), session_type: mock_model(SessionType) } build(:proposal, valid_attr.merge(attr)) end end